Commit 21287f1b authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into ce_upstream

parents 8a7e9852 2efee5f6
...@@ -20,6 +20,7 @@ variables: ...@@ -20,6 +20,7 @@ variables:
SIMPLECOV: "true" SIMPLECOV: "true"
USE_DB: "true" USE_DB: "true"
USE_BUNDLE_INSTALL: "true" USE_BUNDLE_INSTALL: "true"
GIT_DEPTH: "20"
before_script: before_script:
- source ./scripts/prepare_build.sh - source ./scripts/prepare_build.sh
......
require: rubocop-rspec require:
- rubocop-rspec
- ./rubocop/rubocop
AllCops: AllCops:
TargetRubyVersion: 2.1 TargetRubyVersion: 2.1
...@@ -532,11 +534,11 @@ Style/SingleLineMethods: ...@@ -532,11 +534,11 @@ Style/SingleLineMethods:
# Use spaces after colons. # Use spaces after colons.
Style/SpaceAfterColon: Style/SpaceAfterColon:
Enabled: false Enabled: true
# Use spaces after commas. # Use spaces after commas.
Style/SpaceAfterComma: Style/SpaceAfterComma:
Enabled: false Enabled: true
# Do not put a space between a method name and the opening parenthesis in a # Do not put a space between a method name and the opening parenthesis in a
# method definition. # method definition.
......
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.10.0 (unreleased) v 8.10.0 (unreleased)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666 - Replace Haml with Hamlit to make view rendering faster. !3666
- Wrap code blocks on Activies and Todos page. !4783 (winniehell) - Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Align flash messages with left side of page content !4959 (winniehell)
- Display last commit of deleted branch in push events !4699 (winniehell)
- Add Sidekiq queue duration to transaction metrics. - Add Sidekiq queue duration to transaction metrics.
- Let Workhorse serve format-patch diffs
- Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836 - Fix MR-auto-close text added to description. !4836
- Fix pagination when sorting by columns with lots of ties (like priority) - Fix pagination when sorting by columns with lots of ties (like priority)
- Exclude email check from the standard health check - Exclude email check from the standard health check
- Implement Subresource Integrity for CSS and JavaScript assets. This prevents malicious assets from loading in the case of a CDN compromise.
- Fix changing issue state columns in milestone view - Fix changing issue state columns in milestone view
- Add notification settings dropdown for groups
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- PipelinesFinder uses git cache data
- Check for conflicts with existing Project's wiki path when creating a new project.
- Remove unused front-end variable -> default_issues_tracker
- Add API endpoint for a group issues !4520 (mahcsig) - Add API endpoint for a group issues !4520 (mahcsig)
- Add Bugzilla integration !4930 (iamtjg)
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
- Add basic system information like memory and disk usage to the admin panel
v 8.9.4 (unreleased)
- Fixes middle click and double request when navigating through the file browser !4891
- Add sub nav to file page view
v 8.9.3
- Fix encrypted data backwards compatibility after upgrading attr_encrypted gem. !4963
- Fix rendering of commit notes. !4953
- Resolve "Pin should show up at 1280px min". !4947
- Switched mobile button icons to ellipsis and angle. !4944
- Correctly returns todo ID after creating todo. !4941
- Better debugging for memory killer middleware. !4936
- Remove duplicate new page btn from edit wiki. !4904
- Use clock_gettime for all performance timestamps. !4899
- Use memorized tags array when searching tags by name. !4859
- Fixed avatar alignment in new MR view. !4901
- Removed fade when filtering results. !4932
- Fix missing avatar on system notes. !4954
- Reduce overhead and optimize ProjectTeam#max_member_access performance. !4973
- Use update_columns to by_pass all the dirty code on active_record. !4985
- Decreased min width of screen to 1280px for pinned sidebar
- Fix encrypted data backwards compatibility after upgrading attr_encrypted gem
- Update mobile button icons to be more inline with typical UI paradigms
v 8.9.2
- Fix visibility of snippets when searching.
- Fix an information disclosure when requesting access to a group containing private projects.
- Update omniauth-saml to 1.6.0 !4951
v 8.9.1 v 8.9.1
- Refactor labels documentation. !3347 - Refactor labels documentation. !3347
...@@ -51,11 +91,13 @@ v 8.9.1 ...@@ -51,11 +91,13 @@ v 8.9.1
- Fix pagination when sorting by columns with lots of ties (like priority) - Fix pagination when sorting by columns with lots of ties (like priority)
- Implement Subresource Integrity for CSS and JavaScript assets. This prevents malicious assets from loading in the case of a CDN compromise. - Implement Subresource Integrity for CSS and JavaScript assets. This prevents malicious assets from loading in the case of a CDN compromise.
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Add API endpoint for a group issues !4520 (mahcsig)
- Fix a wrong MR status when merge_when_build_succeeds & project.only_allow_merge_if_build_succeeds are true. !4912 - Fix a wrong MR status when merge_when_build_succeeds & project.only_allow_merge_if_build_succeeds are true. !4912
- Add SMTP as default delivery method to match gitlab-org/omnibus-gitlab!826. !4915 - Add SMTP as default delivery method to match gitlab-org/omnibus-gitlab!826. !4915
- Remove duplicate 'New Page' button on edit wiki page
v 8.9.0 v 8.9.0
v 8.9.0 (unreleased)
- Fix group visibility form layout in application settings
- Fix builds API response not including commit data - Fix builds API response not including commit data
- Fix error when CI job variables key specified but not defined - Fix error when CI job variables key specified but not defined
- Fix pipeline status when there are no builds in pipeline - Fix pipeline status when there are no builds in pipeline
...@@ -154,6 +196,7 @@ v 8.9.0 ...@@ -154,6 +196,7 @@ v 8.9.0
- Add Application Setting to configure Container Registry token expire delay (default 5min) - Add Application Setting to configure Container Registry token expire delay (default 5min)
- Cache assigned issue and merge request counts in sidebar nav - Cache assigned issue and merge request counts in sidebar nav
- Use Knapsack only in CI environment - Use Knapsack only in CI environment
- Updated project creation page to match new UI #2542
- Cache project build count in sidebar nav - Cache project build count in sidebar nav
- Add milestone expire date to the right sidebar - Add milestone expire date to the right sidebar
- Manually mark a issue or merge request as a todo - Manually mark a issue or merge request as a todo
...@@ -209,6 +252,10 @@ v 8.9.0 ...@@ -209,6 +252,10 @@ v 8.9.0
- Add tooltip to pin/unpin navbar - Add tooltip to pin/unpin navbar
- Add new sub nav style to Wiki and Graphs sub navigation - Add new sub nav style to Wiki and Graphs sub navigation
v 8.8.6
- Fix visibility of snippets when searching.
- Update omniauth-saml to 1.6.0 !4951
v 8.8.5 v 8.8.5
- Import GitHub repositories respecting the API rate limit !4166 - Import GitHub repositories respecting the API rate limit !4166
- Fix todos page throwing errors when you have a project pending deletion !4300 - Fix todos page throwing errors when you have a project pending deletion !4300
...@@ -339,6 +386,10 @@ v 8.8.0 ...@@ -339,6 +386,10 @@ v 8.8.0
- When creating a .gitignore file a dropdown with templates will be provided - When creating a .gitignore file a dropdown with templates will be provided
- Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562 - Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562
v 8.7.8
- Fix visibility of snippets when searching.
- Update omniauth-saml to 1.6.0 !4951
v 8.7.7 v 8.7.7
- Fix import by `Any Git URL` broken if the URL contains a space - Fix import by `Any Git URL` broken if the URL contains a space
- Prevent unauthorized access to other projects build traces - Prevent unauthorized access to other projects build traces
......
...@@ -30,7 +30,7 @@ gem 'omniauth-github', '~> 1.1.1' ...@@ -30,7 +30,7 @@ gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-google-oauth2', '~> 0.2.0' gem 'omniauth-google-oauth2', '~> 0.2.0'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-saml', '~> 1.5.0' gem 'omniauth-saml', '~> 1.6.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
...@@ -96,6 +96,7 @@ gem 'fog-core', '~> 1.40' ...@@ -96,6 +96,7 @@ gem 'fog-core', '~> 1.40'
gem 'fog-local', '~> 0.3' gem 'fog-local', '~> 0.3'
gem 'fog-google', '~> 0.3' gem 'fog-google', '~> 0.3'
gem 'fog-openstack', '~> 0.1' gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1'
# for aws storage # for aws storage
gem "unf", '~> 0.1.4' gem "unf", '~> 0.1.4'
...@@ -357,3 +358,6 @@ gem "paranoia", "~> 2.0" ...@@ -357,3 +358,6 @@ gem "paranoia", "~> 2.0"
# Health check # Health check
gem 'health_check', '~> 1.5.1' gem 'health_check', '~> 1.5.1'
# System information
gem 'vmstat', '~> 2.1.0'
...@@ -256,6 +256,11 @@ GEM ...@@ -256,6 +256,11 @@ GEM
fog-core (>= 1.39) fog-core (>= 1.39)
fog-json (>= 1.0) fog-json (>= 1.0)
ipaddress (>= 0.8) ipaddress (>= 0.8)
fog-rackspace (0.1.1)
fog-core (>= 1.35)
fog-json (>= 1.0)
fog-xml (>= 0.1)
ipaddress (>= 0.8)
fog-xml (0.1.2) fog-xml (0.1.2)
fog-core fog-core
nokogiri (~> 1.5, >= 1.5.11) nokogiri (~> 1.5, >= 1.5.11)
...@@ -483,9 +488,9 @@ GEM ...@@ -483,9 +488,9 @@ GEM
omniauth-oauth2 (1.3.1) omniauth-oauth2 (1.3.1)
oauth2 (~> 1.0) oauth2 (~> 1.0)
omniauth (~> 1.2) omniauth (~> 1.2)
omniauth-saml (1.5.0) omniauth-saml (1.6.0)
omniauth (~> 1.3) omniauth (~> 1.3)
ruby-saml (~> 1.1, >= 1.1.1) ruby-saml (~> 1.3)
omniauth-shibboleth (1.2.1) omniauth-shibboleth (1.2.1)
omniauth (>= 1.0.0) omniauth (>= 1.0.0)
omniauth-twitter (1.2.1) omniauth-twitter (1.2.1)
...@@ -646,9 +651,8 @@ GEM ...@@ -646,9 +651,8 @@ GEM
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-progressbar (1.8.1) ruby-progressbar (1.8.1)
ruby-saml (1.1.2) ruby-saml (1.3.0)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
uuid (~> 2.3)
ruby_parser (3.8.2) ruby_parser (3.8.2)
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.2) rubyntlm (0.5.2)
...@@ -808,6 +812,7 @@ GEM ...@@ -808,6 +812,7 @@ GEM
coercible (~> 1.0) coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3) descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9) equalizer (~> 0.0, >= 0.0.9)
vmstat (2.1.0)
warden (1.2.6) warden (1.2.6)
rack (>= 1.0) rack (>= 1.0)
web-console (2.3.0) web-console (2.3.0)
...@@ -887,6 +892,7 @@ DEPENDENCIES ...@@ -887,6 +892,7 @@ DEPENDENCIES
fog-google (~> 0.3) fog-google (~> 0.3)
fog-local (~> 0.3) fog-local (~> 0.3)
fog-openstack (~> 0.1) fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.6.1) font-awesome-rails (~> 4.6.1)
foreman foreman
fuubar (~> 2.0.0) fuubar (~> 2.0.0)
...@@ -945,7 +951,7 @@ DEPENDENCIES ...@@ -945,7 +951,7 @@ DEPENDENCIES
omniauth-gitlab (~> 1.0.0) omniauth-gitlab (~> 1.0.0)
omniauth-google-oauth2 (~> 0.2.0) omniauth-google-oauth2 (~> 0.2.0)
omniauth-kerberos (~> 0.3.0) omniauth-kerberos (~> 0.3.0)
omniauth-saml (~> 1.5.0) omniauth-saml (~> 1.6.0)
omniauth-shibboleth (~> 1.2.0) omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0) omniauth-twitter (~> 1.2.0)
omniauth_crowd (~> 2.2.0) omniauth_crowd (~> 2.2.0)
...@@ -1019,6 +1025,7 @@ DEPENDENCIES ...@@ -1019,6 +1025,7 @@ DEPENDENCIES
validates_hostname (~> 1.0.0) validates_hostname (~> 1.0.0)
version_sorter (~> 2.0.0) version_sorter (~> 2.0.0)
virtus (~> 1.0.1) virtus (~> 1.0.1)
vmstat (~> 2.1.0)
web-console (~> 2.0) web-console (~> 2.0)
webmock (~> 1.21.0) webmock (~> 1.21.0)
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
......
app/assets/images/auth_buttons/azure_64.png

986 Bytes | W: | H:

app/assets/images/auth_buttons/azure_64.png

695 Bytes | W: | H:

app/assets/images/auth_buttons/azure_64.png
app/assets/images/auth_buttons/azure_64.png
app/assets/images/auth_buttons/azure_64.png
app/assets/images/auth_buttons/azure_64.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/bg_fallback.png

167 Bytes | W: | H:

app/assets/images/bg_fallback.png

167 Bytes | W: | H:

app/assets/images/bg_fallback.png
app/assets/images/bg_fallback.png
app/assets/images/bg_fallback.png
app/assets/images/bg_fallback.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/emoji.png

257 KB | W: | H:

app/assets/images/emoji.png

257 KB | W: | H:

app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
app/assets/images/emoji.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/emoji@2x.png

674 KB | W: | H:

app/assets/images/emoji@2x.png

673 KB | W: | H:

app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/gitlab_logo.png

5.07 KB | W: | H:

app/assets/images/gitlab_logo.png

3.53 KB | W: | H:

app/assets/images/gitlab_logo.png
app/assets/images/gitlab_logo.png
app/assets/images/gitlab_logo.png
app/assets/images/gitlab_logo.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/gitorious-logo-black.png

809 Bytes | W: | H:

app/assets/images/gitorious-logo-black.png

631 Bytes | W: | H:

app/assets/images/gitorious-logo-black.png
app/assets/images/gitorious-logo-black.png
app/assets/images/gitorious-logo-black.png
app/assets/images/gitorious-logo-black.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/gitorious-logo-blue.png

495 Bytes | W: | H:

app/assets/images/gitorious-logo-blue.png

201 Bytes | W: | H:

app/assets/images/gitorious-logo-blue.png
app/assets/images/gitorious-logo-blue.png
app/assets/images/gitorious-logo-blue.png
app/assets/images/gitorious-logo-blue.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/icon-link.png

1.1 KB | W: | H:

app/assets/images/icon-link.png

729 Bytes | W: | H:

app/assets/images/icon-link.png
app/assets/images/icon-link.png
app/assets/images/icon-link.png
app/assets/images/icon-link.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/images.png

5.71 KB | W: | H:

app/assets/images/images.png

5.67 KB | W: | H:

app/assets/images/images.png
app/assets/images/images.png
app/assets/images/images.png
app/assets/images/images.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/no_avatar.png

621 Bytes | W: | H:

app/assets/images/no_avatar.png

621 Bytes | W: | H:

app/assets/images/no_avatar.png
app/assets/images/no_avatar.png
app/assets/images/no_avatar.png
app/assets/images/no_avatar.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/no_group_avatar.png

942 Bytes | W: | H:

app/assets/images/no_group_avatar.png

939 Bytes | W: | H:

app/assets/images/no_group_avatar.png
app/assets/images/no_group_avatar.png
app/assets/images/no_group_avatar.png
app/assets/images/no_group_avatar.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/slider_handles.png

1.34 KB | W: | H:

app/assets/images/slider_handles.png

1.31 KB | W: | H:

app/assets/images/slider_handles.png
app/assets/images/slider_handles.png
app/assets/images/slider_handles.png
app/assets/images/slider_handles.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/touch-icon-ipad.png

3.41 KB | W: | H:

app/assets/images/touch-icon-ipad.png

2.41 KB | W: | H:

app/assets/images/touch-icon-ipad.png
app/assets/images/touch-icon-ipad.png
app/assets/images/touch-icon-ipad.png
app/assets/images/touch-icon-ipad.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -202,7 +202,6 @@ $ -> ...@@ -202,7 +202,6 @@ $ ->
$('.header-content .header-logo').toggle() $('.header-content .header-logo').toggle()
$('.header-content .navbar-collapse').toggle() $('.header-content .navbar-collapse').toggle()
$('.navbar-toggle').toggleClass('active') $('.navbar-toggle').toggleClass('active')
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
# Show/hide comments on diff # Show/hide comments on diff
$body.on "click", ".js-toggle-diff-comments", (e) -> $body.on "click", ".js-toggle-diff-comments", (e) ->
...@@ -265,7 +264,7 @@ $ -> ...@@ -265,7 +264,7 @@ $ ->
new Aside() new Aside()
# Sidenav pinning # Sidenav pinning
if $window.width() < 1440 and $.cookie('pin_nav') is 'true' if $window.width() < 1280 and $.cookie('pin_nav') is 'true'
$.cookie('pin_nav', 'false', { path: '/' }) $.cookie('pin_nav', 'false', { path: '/' })
$('.page-with-sidebar') $('.page-with-sidebar')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded') .toggleClass('page-sidebar-collapsed page-sidebar-expanded')
......
...@@ -341,7 +341,9 @@ class @AwardsHandler ...@@ -341,7 +341,9 @@ class @AwardsHandler
for emoji in frequentlyUsedEmojis for emoji in frequentlyUsedEmojis
$(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul) $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
$('input.emoji-search').after(ul).after($('<h5>').text('Frequently used')) $('.emoji-menu-content')
.prepend(ul)
.prepend($('<h5>').text('Frequently used'))
@frequentEmojiBlockRendered = true @frequentEmojiBlockRendered = true
...@@ -356,7 +358,7 @@ class @AwardsHandler ...@@ -356,7 +358,7 @@ class @AwardsHandler
if term if term
# Generate a search result block # Generate a search result block
h5 = $('<h5>').text('Search results').addClass('emoji-search') h5 = $('<h5>').text('Search results')
found_emojis = @searchEmojis(term).show() found_emojis = @searchEmojis(term).show()
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis) ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis)
$('.emoji-menu-content ul, .emoji-menu-content h5').hide() $('.emoji-menu-content ul, .emoji-menu-content h5').hide()
......
...@@ -84,6 +84,8 @@ class Dispatcher ...@@ -84,6 +84,8 @@ class Dispatcher
new Activities() new Activities()
when 'groups:show' when 'groups:show'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new NotificationsForm()
new NotificationsDropdown()
when 'groups:group_members:index' when 'groups:group_members:index'
new GroupMembers() new GroupMembers()
new UsersSelect() new UsersSelect()
......
...@@ -4,11 +4,19 @@ class @Flash ...@@ -4,11 +4,19 @@ class @Flash
@flash.html("") @flash.html("")
innerDiv = $('<div/>', innerDiv = $('<div/>',
class: "flash-#{type}", class: "flash-#{type}"
text: message
) )
innerDiv.appendTo(".flash-container") innerDiv.appendTo(".flash-container")
textDiv = $("<div/>",
class: "flash-text",
text: message
)
textDiv.appendTo(innerDiv)
if @flash.parent().hasClass('content-wrapper')
textDiv.addClass('container-fluid container-limited')
@flash.click -> $(@).fadeOut() @flash.click -> $(@).fadeOut()
@flash.show() @flash.show()
......
...@@ -186,6 +186,8 @@ class GitLabDropdown ...@@ -186,6 +186,8 @@ class GitLabDropdown
@fullData = data @fullData = data
@parseData @fullData @parseData @fullData
@filter.input.trigger('keyup') if @options.filterable and @filter and @filter.input
} }
# Init filterable # Init filterable
...@@ -218,6 +220,13 @@ class GitLabDropdown ...@@ -218,6 +220,13 @@ class GitLabDropdown
@dropdown.on 'keyup', (e) => @dropdown.on 'keyup', (e) =>
if e.which is 27 # Escape key if e.which is 27 # Escape key
$('.dropdown-menu-close', @dropdown).trigger 'click' $('.dropdown-menu-close', @dropdown).trigger 'click'
@dropdown.on 'blur', 'a', (e) =>
if e.relatedTarget?
$relatedTarget = $(e.relatedTarget)
$dropdownMenu = $relatedTarget.closest('.dropdown-menu')
if $dropdownMenu.length is 0
@dropdown.removeClass('open')
if @dropdown.find(".dropdown-toggle-page").length if @dropdown.find(".dropdown-toggle-page").length
@dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) => @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) =>
......
...@@ -59,13 +59,12 @@ issuable_created = false ...@@ -59,13 +59,12 @@ issuable_created = false
filterResults: (form) => filterResults: (form) =>
formData = form.serialize() formData = form.serialize()
$('.issues-holder, .merge-requests-holder').css('opacity', '0.5')
formAction = form.attr('action') formAction = form.attr('action')
issuesUrl = formAction issuesUrl = formAction
issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}") issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}")
issuesUrl += formData issuesUrl += formData
Turbolinks.visit(issuesUrl); Turbolinks.visit(issuesUrl)
initChecks: -> initChecks: ->
@issuableBulkActions = $('.bulk-update').data('bulkActions') @issuableBulkActions = $('.bulk-update').data('bulkActions')
......
...@@ -99,7 +99,7 @@ class @Issue ...@@ -99,7 +99,7 @@ class @Issue
# If the user doesn't have the required permissions the container isn't # If the user doesn't have the required permissions the container isn't
# rendered at all. # rendered at all.
return unless $container return if $container.length is 0
$.getJSON($container.data('path')) $.getJSON($container.data('path'))
.error -> .error ->
......
...@@ -10,17 +10,41 @@ ...@@ -10,17 +10,41 @@
gl.text.selectedText = (text, textarea) -> gl.text.selectedText = (text, textarea) ->
text.substring(textarea.selectionStart, textarea.selectionEnd) text.substring(textarea.selectionStart, textarea.selectionEnd)
gl.text.insertText = (textArea, text, tag, selected, wrap) -> gl.text.lineBefore = (text, textarea) ->
split = text.substring(0, textarea.selectionStart).trim().split('\n')
split[split.length - 1]
gl.text.lineAfter = (text, textarea) ->
text.substring(textarea.selectionEnd).trim().split('\n')[0]
gl.text.blockTagText = (text, textArea, blockTag, selected) ->
lineBefore = @lineBefore(text, textArea)
lineAfter = @lineAfter(text, textArea)
if lineBefore is blockTag and lineAfter is blockTag
# To remove the block tag we have to select the line before & after
if blockTag?
textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1)
textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1)
selected
else
"#{blockTag}\n#{selected}\n#{blockTag}"
gl.text.insertText = (textArea, text, tag, blockTag, selected, wrap) ->
selectedSplit = selected.split('\n') selectedSplit = selected.split('\n')
startChar = if not wrap and textArea.selectionStart > 0 then '\n' else '' startChar = if not wrap and textArea.selectionStart > 0 then '\n' else ''
if selectedSplit.length > 1 and not wrap if selectedSplit.length > 1 and (not wrap or blockTag?)
insertText = selectedSplit.map((val) -> if blockTag?
if val.indexOf(tag) is 0 insertText = @blockTagText(text, textArea, blockTag, selected)
"#{val.replace(tag, '')}" else
else insertText = selectedSplit.map((val) ->
"#{tag}#{val}" if val.indexOf(tag) is 0
).join('\n') "#{val.replace(tag, '')}"
else
"#{tag}#{val}"
).join('\n')
else else
insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}" insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}"
...@@ -51,7 +75,7 @@ ...@@ -51,7 +75,7 @@
textArea.setSelectionRange pos, pos textArea.setSelectionRange pos, pos
gl.text.updateText = (textArea, tag, wrap) -> gl.text.updateText = (textArea, tag, blockTag, wrap) ->
$textArea = $(textArea) $textArea = $(textArea)
oldVal = $textArea.val() oldVal = $textArea.val()
textArea = $textArea.get(0) textArea = $textArea.get(0)
...@@ -59,7 +83,7 @@ ...@@ -59,7 +83,7 @@
selected = @selectedText(text, textArea) selected = @selectedText(text, textArea)
$textArea.focus() $textArea.focus()
@insertText(textArea, text, tag, selected, wrap) @insertText(textArea, text, tag, blockTag, selected, wrap)
gl.text.init = (form) -> gl.text.init = (form) ->
self = @ self = @
...@@ -70,6 +94,7 @@ ...@@ -70,6 +94,7 @@
self.updateText( self.updateText(
$this.closest('.md-area').find('textarea'), $this.closest('.md-area').find('textarea'),
$this.data('md-tag'), $this.data('md-tag'),
$this.data('md-block'),
not $this.data('md-prepend') not $this.data('md-prepend')
) )
......
...@@ -171,22 +171,15 @@ class @SearchAutocomplete ...@@ -171,22 +171,15 @@ class @SearchAutocomplete
} }
bindEvents: -> bindEvents: ->
$(document).on 'click', @onDocumentClick
@searchInput.on 'keydown', @onSearchInputKeyDown @searchInput.on 'keydown', @onSearchInputKeyDown
@searchInput.on 'keyup', @onSearchInputKeyUp @searchInput.on 'keyup', @onSearchInputKeyUp
@searchInput.on 'click', @onSearchInputClick @searchInput.on 'click', @onSearchInputClick
@searchInput.on 'focus', @onSearchInputFocus @searchInput.on 'focus', @onSearchInputFocus
@searchInput.on 'blur', @onSearchInputBlur
@clearInput.on 'click', @onClearInputClick @clearInput.on 'click', @onClearInputClick
@locationBadgeEl.on 'click', => @locationBadgeEl.on 'click', =>
@searchInput.focus() @searchInput.focus()
onDocumentClick: (e) =>
# If clicking outside the search box
# And search input is not focused
# And we are not clicking inside a suggestion
if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).closest('.search-form').length
@onSearchInputBlur()
enableAutocomplete: -> enableAutocomplete: ->
# No need to enable anything if user is not logged in # No need to enable anything if user is not logged in
return if !gon.current_user_id return if !gon.current_user_id
...@@ -287,8 +280,6 @@ class @SearchAutocomplete ...@@ -287,8 +280,6 @@ class @SearchAutocomplete
value: @originalState._location value: @originalState._location
) )
@dropdown.removeClass 'open'
badgePresent: -> badgePresent: ->
@locationBadgeEl.length @locationBadgeEl.length
......
...@@ -9,12 +9,12 @@ class @Shortcuts ...@@ -9,12 +9,12 @@ class @Shortcuts
onToggleHelp: (e) => onToggleHelp: (e) =>
e.preventDefault() e.preventDefault()
@toggleHelp(@enabledHelp) Shortcuts.toggleHelp(@enabledHelp)
toggleMarkdownPreview: (e) => toggleMarkdownPreview: (e) ->
$(document).triggerHandler('markdown-preview:toggle', [e]) $(document).triggerHandler('markdown-preview:toggle', [e])
toggleHelp: (location) -> @toggleHelp: (location) ->
$modal = $('#modal-shortcuts') $modal = $('#modal-shortcuts')
if $modal.length if $modal.length
......
...@@ -5,9 +5,15 @@ class @TreeView ...@@ -5,9 +5,15 @@ class @TreeView
# Code browser tree slider # Code browser tree slider
# Make the entire tree-item row clickable, but not if clicking another link (like a commit message) # Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
$(".tree-content-holder .tree-item").on 'click', (e) -> $(".tree-content-holder .tree-item").on 'click', (e) ->
if (e.target.nodeName != "A") $clickedEl = $(e.target)
path = $('.tree-item-file-name a', this).attr('href') path = $('.tree-item-file-name a', this).attr('href')
Turbolinks.visit(path)
if not $clickedEl.is('a') and not $clickedEl.is('.str-truncated')
if e.metaKey or e.which is 2
e.preventDefault()
window.open path, '_blank'
else
Turbolinks.visit path
# Show the "Loading commit data" for only the first element # Show the "Loading commit data" for only the first element
$('span.log_loading:first').removeClass('hide') $('span.log_loading:first').removeClass('hide')
......
...@@ -137,7 +137,7 @@ ...@@ -137,7 +137,7 @@
margin: 0; margin: 0;
font-size: 24px; font-size: 24px;
font-weight: normal; font-weight: normal;
margin-bottom: 5px; margin-bottom: 10px;
color: #4c4e54; color: #4c4e54;
font-size: 23px; font-size: 23px;
line-height: 1.1; line-height: 1.1;
......
...@@ -16,4 +16,11 @@ ...@@ -16,4 +16,11 @@
@extend .alert-danger; @extend .alert-danger;
margin: 0; margin: 0;
} }
.flash-notice, .flash-alert {
.container-fluid.flash-text {
background: transparent;
}
}
} }
...@@ -125,7 +125,8 @@ ...@@ -125,7 +125,8 @@
border: 0; border: 0;
outline: 0; outline: 0;
&:hover { &:hover,
&:focus {
color: $gl-link-color; color: $gl-link-color;
} }
} }
...@@ -21,9 +21,8 @@ ...@@ -21,9 +21,8 @@
.fa { .fa {
position: relative; position: relative;
top: 3px; top: 5px;
font-size: 13px; font-size: 18px;
color: $btn-placeholder-gray;
} }
} }
......
.page-with-sidebar { .page-with-sidebar {
padding-top: $header-height; padding-top: $header-height;
padding-bottom: 25px;
transition: padding $sidebar-transition-duration; transition: padding $sidebar-transition-duration;
.sidebar-wrapper { .sidebar-wrapper {
......
...@@ -7,7 +7,7 @@ $gutter_collapsed_width: 62px; ...@@ -7,7 +7,7 @@ $gutter_collapsed_width: 62px;
$gutter_width: 290px; $gutter_width: 290px;
$gutter_inner_width: 258px; $gutter_inner_width: 258px;
$sidebar-transition-duration: .15s; $sidebar-transition-duration: .15s;
$sidebar-breakpoint: 1440px; $sidebar-breakpoint: 1280px;
/* /*
* UI elements * UI elements
......
...@@ -8,8 +8,9 @@ ...@@ -8,8 +8,9 @@
.emoji-menu { .emoji-menu {
position: absolute; position: absolute;
margin-top: 3px; margin-top: 3px;
z-index: 1000; padding: $gl-padding;
min-width: 160px; z-index: 9;
width: 300px;
font-size: 14px; font-size: 14px;
background-color: $award-emoji-menu-bg; background-color: $award-emoji-menu-bg;
border: 1px solid $award-emoji-menu-border; border: 1px solid $award-emoji-menu-border;
...@@ -33,20 +34,18 @@ ...@@ -33,20 +34,18 @@
} }
.emoji-menu-content { .emoji-menu-content {
padding: $gl-padding;
width: 300px;
height: 300px; height: 300px;
overflow-y: scroll; overflow-y: scroll;
input.emoji-search {
background-image: url("data:image/png;base64,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");
background-repeat: no-repeat;
background-position: right 5px center;
background-size: 16px;
}
} }
} }
.emoji-search {
background-image: url("data:image/png;base64,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");
background-repeat: no-repeat;
background-position: right 5px center;
background-size: 16px;
}
.emoji-menu-list { .emoji-menu-list {
list-style: none; list-style: none;
padding-left: 0; padding-left: 0;
......
...@@ -83,11 +83,7 @@ ...@@ -83,11 +83,7 @@
position: relative; position: relative;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
padding-left: 20px; padding-left: 46px;
.commit-info-block {
padding-left: 44px;
}
} }
&:not(:last-child) { &:not(:last-child) {
...@@ -102,9 +98,7 @@ ...@@ -102,9 +98,7 @@
.avatar { .avatar {
position: absolute; margin-left: -46px;
top: 10px;
left: 16px;
} }
.item-title { .item-title {
......
...@@ -53,12 +53,8 @@ ...@@ -53,12 +53,8 @@
.access-request-button { .access-request-button {
@include btn-gray; @include btn-gray;
position: absolute; margin-right: 10px;
right: 16px;
bottom: 32px;
padding: 3px 10px;
text-transform: none; text-transform: none;
background-color: $background-color;
} }
} }
......
...@@ -63,5 +63,6 @@ ...@@ -63,5 +63,6 @@
border: 1px solid $table-border-gray; border: 1px solid $table-border-gray;
padding: 5px; padding: 5px;
margin: 5px; margin: 5px;
max-height: calc(100vh - 100px);
} }
} }
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
border: 1px solid $table-border-gray; border: 1px solid $table-border-gray;
padding: 5px; padding: 5px;
margin: 5px; margin: 5px;
max-height: calc(100vh - 100px);
} }
} }
......
...@@ -264,8 +264,15 @@ ...@@ -264,8 +264,15 @@
margin-bottom: 4px; margin-bottom: 4px;
} }
.item-title {
@media (min-width: $screen-sm-min) {
width: 49%;
}
}
.avatar { .avatar {
margin-left: 0; left: 0;
top: 2px;
} }
.commit-row-info { .commit-row-info {
......
...@@ -41,6 +41,10 @@ ul.notes { ...@@ -41,6 +41,10 @@ ul.notes {
.timeline-icon { .timeline-icon {
.avatar { .avatar {
visibility: hidden; visibility: hidden;
.discussion-body & {
visibility: visible;
}
} }
} }
} }
...@@ -113,6 +117,7 @@ ul.notes { ...@@ -113,6 +117,7 @@ ul.notes {
border: 1px solid $table-border-gray; border: 1px solid $table-border-gray;
padding: 5px; padding: 5px;
margin: 5px 0; margin: 5px 0;
max-height: calc(100vh - 100px);
} }
} }
} }
......
...@@ -13,10 +13,53 @@ ...@@ -13,10 +13,53 @@
.new_project, .new_project,
.edit-project { .edit-project {
fieldset.features { fieldset {
.control-label { &.features .control-label {
font-weight: normal; font-weight: normal;
} }
.form-group {
margin-bottom: 5px;
}
&> .form-group {
padding-left: 0;
}
}
.help-block {
margin-bottom: 10px;
}
.project-path {
padding-right: 0;
.form-control {
border-radius: $border-radius-base;
}
}
.input-group > div {
&:last-child {
padding-right: 0;
}
}
@media (max-width: $screen-xs-max) {
.input-group > div {
margin-bottom: 14px;
&:last-child {
margin-bottom: 0;
}
}
fieldset > .form-group:first-child {
padding-right: 0;
}
}
.input-group-addon {
&.static-namespace {
height: 35px;
border-radius: 3px;
border: 1px solid #e5e5e5;
}
&+ .select2 a {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
} }
} }
...@@ -365,10 +408,28 @@ a.deploy-project-label { ...@@ -365,10 +408,28 @@ a.deploy-project-label {
} }
} }
.project-import .btn { .project-import {
float: left; .form-group {
margin-bottom: 10px; margin-bottom: 0;
margin-right: 10px; }
.import-buttons {
padding-left: 0;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
.btn {
margin-right: 10px;
padding: 8px 12px;
}
&> div {
margin-bottom: 14px;
padding-left: 0;
&:last-child {
margin-bottom: 0;
}
}
}
} }
.project-stats { .project-stats {
......
...@@ -101,7 +101,8 @@ ...@@ -101,7 +101,8 @@
margin: 0; margin: 0;
.commit { .commit {
padding: 0 0 0 55px; padding-top: 0;
padding-bottom: 0;
.commit-row-title { .commit-row-title {
.commit-row-message { .commit-row-message {
......
class Admin::SystemInfoController < Admin::ApplicationController
def show
system_info = Vmstat.snapshot
@cpus = system_info.cpus.length
@mem_used = system_info.memory.active_bytes
@mem_total = system_info.memory.total_bytes
@disk_used = system_info.disks[0].used_bytes
@disk_total = system_info.disks[0].total_bytes
end
end
...@@ -25,7 +25,7 @@ module Ci ...@@ -25,7 +25,7 @@ module Ci
return render_404 unless @project return render_404 unless @project
image = Ci::ImageForBuildService.new.execute(@project, params) image = Ci::ImageForBuildService.new.execute(@project, params)
send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml" send_file image.path, filename: image.name, disposition: 'inline', type: "image/svg+xml"
end end
protected protected
......
class Dashboard::GroupsController < Dashboard::ApplicationController class Dashboard::GroupsController < Dashboard::ApplicationController
def index def index
@group_members = current_user.group_members.page(params[:page]) @group_members = current_user.group_members.includes(:source).page(params[:page])
end end
end end
...@@ -37,15 +37,12 @@ class GroupsController < Groups::ApplicationController ...@@ -37,15 +37,12 @@ class GroupsController < Groups::ApplicationController
end end
def show def show
@last_push = current_user.recent_push if current_user if current_user
@last_push = current_user.recent_push
@projects = @projects.includes(:namespace) @notification_setting = current_user.notification_settings_for(group)
@projects = @projects.sorted_by_activity end
@projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]) if params[:filter_projects].blank?
@shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user) setup_projects
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -103,6 +100,16 @@ class GroupsController < Groups::ApplicationController ...@@ -103,6 +100,16 @@ class GroupsController < Groups::ApplicationController
protected protected
def setup_projects
@projects = @projects.includes(:namespace)
@projects = @projects.sorted_by_activity
@projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]) if params[:filter_projects].blank?
@shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user)
end
def authorize_create_group! def authorize_create_group!
unless can?(current_user, :create_group, nil) unless can?(current_user, :create_group, nil)
return render_404 return render_404
......
...@@ -2,11 +2,9 @@ class NotificationSettingsController < ApplicationController ...@@ -2,11 +2,9 @@ class NotificationSettingsController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
def create def create
project = Project.find(params[:project][:id]) return render_404 unless can_read?(resource)
return render_404 unless can?(current_user, :read_project, project) @notification_setting = current_user.notification_settings_for(resource)
@notification_setting = current_user.notification_settings_for(project)
@saved = @notification_setting.update_attributes(notification_setting_params) @saved = @notification_setting.update_attributes(notification_setting_params)
render_response render_response
...@@ -21,6 +19,22 @@ class NotificationSettingsController < ApplicationController ...@@ -21,6 +19,22 @@ class NotificationSettingsController < ApplicationController
private private
def resource
@resource ||=
if params[:project_id].present?
Project.find(params[:project_id])
elsif params[:namespace_id].present?
Group.find(params[:namespace_id])
end
end
def can_read?(resource)
ability_name = resource.class.name.downcase
ability_name = "read_#{ability_name}".to_sym
can?(current_user, ability_name, resource)
end
def render_response def render_response
render json: { render json: {
html: view_to_html_string("shared/notifications/_button", notification_setting: @notification_setting), html: view_to_html_string("shared/notifications/_button", notification_setting: @notification_setting),
......
...@@ -18,9 +18,16 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -18,9 +18,16 @@ class Projects::CommitController < Projects::ApplicationController
apply_diff_view_cookie! apply_diff_view_cookie!
@grouped_diff_notes = commit.notes.grouped_diff_notes @grouped_diff_notes = commit.notes.grouped_diff_notes
@notes = commit.notes.non_diff_notes.fresh
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten + @notes,
@project,
current_user,
)
@note = @project.build_commit_note(commit) @note = @project.build_commit_note(commit)
@notes = commit.notes.non_diff_notes.fresh
@noteable = @commit @noteable = @commit
@comments_target = { @comments_target = {
noteable_type: 'Commit', noteable_type: 'Commit',
......
...@@ -60,7 +60,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -60,7 +60,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html format.html
format.json { render json: @merge_request, methods: :rebase_in_progress? } format.json { render json: @merge_request, methods: :rebase_in_progress? }
format.patch { render text: @merge_request.to_patch } format.patch do
headers.store(*Gitlab::Workhorse.send_git_patch(@project.repository,
@merge_request.diff_base_commit.id,
@merge_request.last_commit.id))
headers['Content-Disposition'] = 'inline'
head :ok
end
format.diff do format.diff do
return render_404 unless @merge_request.diff_refs return render_404 unless @merge_request.diff_refs
......
...@@ -29,10 +29,10 @@ class PipelinesFinder ...@@ -29,10 +29,10 @@ class PipelinesFinder
end end
def branches def branches
project.repository.branches.map(&:name) project.repository.branch_names
end end
def tags def tags
project.repository.tags.map(&:name) project.repository.tag_names
end end
end end
module BlobHelper module BlobHelper
def highlighter(blob_name, blob_content, nowrap: false) def highlighter(blob_name, blob_content, repository: nil, nowrap: false)
Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap) Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository)
end end
def highlight(blob_name, blob_content, nowrap: false, plain: false) def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false)
Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain) Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository)
end end
def no_highlight_files def no_highlight_files
......
...@@ -69,7 +69,7 @@ module DropdownsHelper ...@@ -69,7 +69,7 @@ module DropdownsHelper
def dropdown_filter(placeholder, search_id: nil) def dropdown_filter(placeholder, search_id: nil)
content_tag :div, class: "dropdown-input" do content_tag :div, class: "dropdown-input" do
filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder, autocomplete: 'off'
filter_output << icon('search', class: "dropdown-input-search") filter_output << icon('search', class: "dropdown-input-search")
filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button") filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button")
......
module JavascriptHelper module JavascriptHelper
def page_specific_javascript_tag(js) def page_specific_javascript_tag(js)
javascript_include_tag asset_path(js), { integrity: true, "data-turbolinks-track" => true } javascript_include_tag asset_path(js), { "data-turbolinks-track" => true }
end end
end end
...@@ -34,10 +34,7 @@ module LabelsHelper ...@@ -34,10 +34,7 @@ module LabelsHelper
# Returns a String # Returns a String
def link_to_label(label, project: nil, type: :issue, tooltip: true, css_class: nil, &block) def link_to_label(label, project: nil, type: :issue, tooltip: true, css_class: nil, &block)
project ||= @project || label.project project ||= @project || label.project
link = send("namespace_project_#{type.to_s.pluralize}_path", link = label_filter_path(project, label, type: type)
project.namespace,
project,
label_name: [label.name])
if block_given? if block_given?
link_to link, class: css_class, &block link_to link, class: css_class, &block
...@@ -46,6 +43,13 @@ module LabelsHelper ...@@ -46,6 +43,13 @@ module LabelsHelper
end end
end end
def label_filter_path(project, label, type: issue)
send("namespace_project_#{type.to_s.pluralize}_path",
project.namespace,
project,
label_name: [label.name])
end
def project_label_names def project_label_names
@project.labels.pluck(:title) @project.labels.pluck(:title)
end end
......
...@@ -69,4 +69,14 @@ module NotesHelper ...@@ -69,4 +69,14 @@ module NotesHelper
button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button', button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button',
data: data, title: 'Add a reply' data: data, title: 'Add a reply'
end end
def note_max_access_for_user(note)
@max_access_by_user_id ||= Hash.new do |hash, key|
project = key[:project]
hash[key] = project.team.human_max_access(key[:user_id])
end
full_key = { project: note.project, user_id: note.author_id }
@max_access_by_user_id[full_key]
end
end end
...@@ -72,6 +72,6 @@ module NotificationsHelper ...@@ -72,6 +72,6 @@ module NotificationsHelper
# Create hidden field to send notification setting source to controller # Create hidden field to send notification setting source to controller
def hidden_setting_source_input(notification_setting) def hidden_setting_source_input(notification_setting)
return unless notification_setting.source_type return unless notification_setting.source_type
hidden_field_tag "#{notification_setting.source_type.downcase}[id]", notification_setting.source_id hidden_field_tag "#{notification_setting.source_type.downcase}_id", notification_setting.source_id
end end
end end
...@@ -52,7 +52,7 @@ module PageLayoutHelper ...@@ -52,7 +52,7 @@ module PageLayoutHelper
raise ArgumentError, 'cannot provide more than two attributes' if map.length > 2 raise ArgumentError, 'cannot provide more than two attributes' if map.length > 2
@page_card_attributes ||= {} @page_card_attributes ||= {}
@page_card_attributes = map.reject { |_,v| v.blank? } if map.present? @page_card_attributes = map.reject { |_, v| v.blank? } if map.present?
@page_card_attributes @page_card_attributes
end end
......
...@@ -15,7 +15,7 @@ module ProjectsHelper ...@@ -15,7 +15,7 @@ module ProjectsHelper
def link_to_member_avatar(author, opts = {}) def link_to_member_avatar(author, opts = {})
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" } default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
opts = default_opts.merge(opts) opts = default_opts.merge(opts)
image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt: '') if opts[:avatar]
end end
def link_to_member(project, author, opts = {}, &block) def link_to_member(project, author, opts = {}, &block)
...@@ -27,7 +27,7 @@ module ProjectsHelper ...@@ -27,7 +27,7 @@ module ProjectsHelper
author_html = "" author_html = ""
# Build avatar image tag # Build avatar image tag
author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt: '') if opts[:avatar]
# Build name span tag # Build name span tag
if opts[:by_username] if opts[:by_username]
......
...@@ -163,7 +163,7 @@ module Ci ...@@ -163,7 +163,7 @@ module Ci
end end
def skip_ci? def skip_ci?
git_commit_message =~ /(\[ci skip\])/ if git_commit_message git_commit_message =~ /\[(ci skip|skip ci)\]/i if git_commit_message
end end
def environments def environments
......
...@@ -321,7 +321,7 @@ class Event < ActiveRecord::Base ...@@ -321,7 +321,7 @@ class Event < ActiveRecord::Base
def body? def body?
if push? if push?
push_with_commits? push_with_commits? || rm_ref?
elsif note? elsif note?
true true
else else
......
...@@ -11,7 +11,7 @@ class Group < Namespace ...@@ -11,7 +11,7 @@ class Group < Namespace
has_many :users, -> { where(members: { requested_at: nil }) }, through: :group_members has_many :users, -> { where(members: { requested_at: nil }) }, through: :group_members
has_many :owners, has_many :owners,
-> { where(members: { access_level: Gitlab::Access::OWNER }) }, -> { where(members: { requested_at: nil, access_level: Gitlab::Access::OWNER }) },
through: :group_members, through: :group_members,
source: :user source: :user
......
...@@ -33,6 +33,7 @@ class Member < ActiveRecord::Base ...@@ -33,6 +33,7 @@ class Member < ActiveRecord::Base
scope :request, -> { where.not(requested_at: nil) } scope :request, -> { where.not(requested_at: nil) }
scope :non_request, -> { where(requested_at: nil) } scope :non_request, -> { where(requested_at: nil) }
scope :non_pending, -> { non_request.non_invite } scope :non_pending, -> { non_request.non_invite }
scope :has_access, -> { where('access_level > 0') }
scope :guests, -> { where(access_level: GUEST) } scope :guests, -> { where(access_level: GUEST) }
scope :reporters, -> { where(access_level: REPORTER) } scope :reporters, -> { where(access_level: REPORTER) }
......
...@@ -340,13 +340,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -340,13 +340,6 @@ class MergeRequest < ActiveRecord::Base
) )
end end
# Returns the commit as a series of email patches.
#
# see "git format-patch"
def to_patch
target_project.repository.format_patch(diff_base_commit.sha, source_sha)
end
def hook_attrs def hook_attrs
attrs = { attrs = {
source: source_project.try(:hook_attrs), source: source_project.try(:hook_attrs),
......
...@@ -108,44 +108,46 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -108,44 +108,46 @@ class MergeRequestDiff < ActiveRecord::Base
# Reload all commits related to current merge request from repo # Reload all commits related to current merge request from repo
# and save it as array of hashes in st_commits db field # and save it as array of hashes in st_commits db field
def reload_commits def reload_commits
new_attributes = {}
commit_objects = unmerged_commits commit_objects = unmerged_commits
if commit_objects.present? if commit_objects.present?
self.st_commits = dump_commits(commit_objects) new_attributes[:st_commits] = dump_commits(commit_objects)
end end
save update_columns_serialized(new_attributes)
end end
# Reload diffs between branches related to current merge request from repo # Reload diffs between branches related to current merge request from repo
# and save it as array of hashes in st_diffs db field # and save it as array of hashes in st_diffs db field
def reload_diffs def reload_diffs
new_attributes = {}
new_diffs = [] new_diffs = []
if commits.size.zero? if commits.size.zero?
self.state = :empty new_attributes[:state] = :empty
else else
diff_collection = unmerged_diffs diff_collection = unmerged_diffs
if diff_collection.overflow? if diff_collection.overflow?
# Set our state to 'overflow' to make the #empty? and #collected? # Set our state to 'overflow' to make the #empty? and #collected?
# methods (generated by StateMachine) return false. # methods (generated by StateMachine) return false.
self.state = :overflow new_attributes[:state] = :overflow
end end
self.real_size = diff_collection.real_size new_attributes[:real_size] = diff_collection.real_size
if diff_collection.any? if diff_collection.any?
new_diffs = dump_diffs(diff_collection) new_diffs = dump_diffs(diff_collection)
self.state = :collected new_attributes[:state] = :collected
end end
end end
self.st_diffs = new_diffs new_attributes[:st_diffs] = new_diffs
new_attributes[:base_commit_sha] = self.repository.merge_base(self.head, self.base)
self.base_commit_sha = self.repository.merge_base(self.head, self.base)
self.save update_columns_serialized(new_attributes)
end end
# Collect array of Git::Diff objects # Collect array of Git::Diff objects
...@@ -190,4 +192,29 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -190,4 +192,29 @@ class MergeRequestDiff < ActiveRecord::Base
) )
end end
end end
private
#
# #save or #update_attributes providing changes on serialized attributes do a lot of
# serialization and deserialization calls resulting in bad performance.
# Using #update_columns solves the problem with just one YAML.dump per serialized attribute that we provide.
# As a tradeoff we need to reload the current instance to properly manage time objects on those serialized
# attributes. So to keep the same behaviour as the attribute assignment we reload the instance.
# The difference is in the usage of
# #write_attribute= (#update_attributes) and #raw_write_attribute= (#update_columns)
#
# Ex:
#
# new_attributes[:st_commits].first.slice(:committed_date)
# => {:committed_date=>2014-02-27 11:01:38 +0200}
# YAML.load(YAML.dump(new_attributes[:st_commits].first.slice(:committed_date)))
# => {:committed_date=>2014-02-27 10:01:38 +0100}
#
def update_columns_serialized(new_attributes)
return unless new_attributes.any?
update_columns(new_attributes.merge(updated_at: current_time_from_proper_timezone))
reload
end
end end
...@@ -54,7 +54,7 @@ module Network ...@@ -54,7 +54,7 @@ module Network
@map = {} @map = {}
@reserved = {} @reserved = {}
@commits.each_with_index do |c,i| @commits.each_with_index do |c, i|
c.time = i c.time = i
days[i] = c.committed_date days[i] = c.committed_date
@map[c.id] = c @map[c.id] = c
...@@ -116,7 +116,7 @@ module Network ...@@ -116,7 +116,7 @@ module Network
end end
def commits_sort_by_ref def commits_sort_by_ref
@commits.sort do |a,b| @commits.sort do |a, b|
if include_ref?(a) if include_ref?(a)
-1 -1
elsif include_ref?(b) elsif include_ref?(b)
......
...@@ -79,6 +79,7 @@ class Project < ActiveRecord::Base ...@@ -79,6 +79,7 @@ class Project < ActiveRecord::Base
has_one :jira_service, dependent: :destroy has_one :jira_service, dependent: :destroy
has_one :redmine_service, dependent: :destroy has_one :redmine_service, dependent: :destroy
has_one :custom_issue_tracker_service, dependent: :destroy has_one :custom_issue_tracker_service, dependent: :destroy
has_one :bugzilla_service, dependent: :destroy
has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project
has_one :external_wiki_service, dependent: :destroy has_one :external_wiki_service, dependent: :destroy
has_one :index_status, dependent: :destroy has_one :index_status, dependent: :destroy
...@@ -173,6 +174,7 @@ class Project < ActiveRecord::Base ...@@ -173,6 +174,7 @@ class Project < ActiveRecord::Base
validates :approvals_before_merge, numericality: true, allow_blank: true validates :approvals_before_merge, numericality: true, allow_blank: true
validate :visibility_level_allowed_by_group validate :visibility_level_allowed_by_group
validate :visibility_level_allowed_as_fork validate :visibility_level_allowed_as_fork
validate :check_wiki_path_conflict
add_authentication_token_field :runners_token add_authentication_token_field :runners_token
before_save :ensure_runners_token before_save :ensure_runners_token
...@@ -639,6 +641,16 @@ class Project < ActiveRecord::Base ...@@ -639,6 +641,16 @@ class Project < ActiveRecord::Base
self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.") self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.")
end end
def check_wiki_path_conflict
return if path.blank?
path_to_check = path.ends_with?('.wiki') ? path.chomp('.wiki') : "#{path}.wiki"
if Project.where(namespace_id: namespace_id, path: path_to_check).exists?
errors.add(:name, 'has already been taken')
end
end
def to_param def to_param
path path
end end
......
class BugzillaService < IssueTrackerService
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
if self.properties && self.properties['title'].present?
self.properties['title']
else
'Bugzilla'
end
end
def description
if self.properties && self.properties['description'].present?
self.properties['description']
else
'Bugzilla issue tracker'
end
end
def to_param
'bugzilla'
end
end
...@@ -32,7 +32,4 @@ class CustomIssueTrackerService < IssueTrackerService ...@@ -32,7 +32,4 @@ class CustomIssueTrackerService < IssueTrackerService
] ]
end end
def initialize_properties
self.properties = {} if properties.nil?
end
end end
...@@ -106,7 +106,7 @@ class HipchatService < Service ...@@ -106,7 +106,7 @@ class HipchatService < Service
else else
message << "pushed to #{ref_type} <a href=\""\ message << "pushed to #{ref_type} <a href=\""\
"#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> " "#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> "
message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> " message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/, '')}</a> "
message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)" message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)"
push[:commits].take(MAX_COMMITS).each do |commit| push[:commits].take(MAX_COMMITS).each do |commit|
......
...@@ -124,7 +124,7 @@ class JiraService < IssueTrackerService ...@@ -124,7 +124,7 @@ class JiraService < IssueTrackerService
def build_api_url_from_project_url def build_api_url_from_project_url
server = URI(project_url) server = URI(project_url)
default_ports = [["http",80],["https",443]].include?([server.scheme,server.port]) default_ports = [["http", 80], ["https", 443]].include?([server.scheme, server.port])
server_url = "#{server.scheme}://#{server.host}" server_url = "#{server.scheme}://#{server.host}"
server_url.concat(":#{server.port}") unless default_ports server_url.concat(":#{server.port}") unless default_ports
"#{server_url}/rest/api/#{DEFAULT_API_VERSION}" "#{server_url}/rest/api/#{DEFAULT_API_VERSION}"
......
...@@ -139,20 +139,10 @@ class ProjectTeam ...@@ -139,20 +139,10 @@ class ProjectTeam
def max_member_access(user_id) def max_member_access(user_id)
access = [] access = []
project.members.non_request.each do |member| access += project.members.non_request.where(user_id: user_id).has_access.pluck(:access_level)
if member.user_id == user_id
access << member.access_field if member.access_field
break
end
end
if group if group
group.members.non_request.each do |member| access += group.members.non_request.where(user_id: user_id).has_access.pluck(:access_level)
if member.user_id == user_id
access << member.access_field if member.access_field
break
end
end
end end
if project.invited_groups.any? && project.allowed_to_share_with_group? if project.invited_groups.any? && project.allowed_to_share_with_group?
......
...@@ -1157,6 +1157,10 @@ class Repository ...@@ -1157,6 +1157,10 @@ class Repository
raw_repository.ls_files(actual_ref) raw_repository.ls_files(actual_ref)
end end
def gitattribute(path, name)
raw_repository.attributes(path)[name]
end
def copy_gitattributes(ref) def copy_gitattributes(ref)
actual_ref = ref || root_ref actual_ref = ref || root_ref
begin begin
......
...@@ -170,6 +170,7 @@ class Service < ActiveRecord::Base ...@@ -170,6 +170,7 @@ class Service < ActiveRecord::Base
bamboo bamboo
buildkite buildkite
builds_email builds_email
bugzilla
campfire campfire
custom_issue_tracker custom_issue_tracker
drone_ci drone_ci
......
...@@ -21,6 +21,7 @@ class Snippet < ActiveRecord::Base ...@@ -21,6 +21,7 @@ class Snippet < ActiveRecord::Base
length: { within: 0..255 }, length: { within: 0..255 },
format: { with: Gitlab::Regex.file_name_regex, format: { with: Gitlab::Regex.file_name_regex,
message: Gitlab::Regex.file_name_regex_message } message: Gitlab::Regex.file_name_regex_message }
validates :content, presence: true validates :content, presence: true
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
...@@ -82,6 +83,11 @@ class Snippet < ActiveRecord::Base ...@@ -82,6 +83,11 @@ class Snippet < ActiveRecord::Base
0 0
end end
# alias for compatibility with blobs and highlighting
def path
file_name
end
def name def name
file_name file_name
end end
...@@ -136,7 +142,16 @@ class Snippet < ActiveRecord::Base ...@@ -136,7 +142,16 @@ class Snippet < ActiveRecord::Base
end end
def accessible_to(user) def accessible_to(user)
where('visibility_level IN (?) OR author_id = ?', [Snippet::INTERNAL, Snippet::PUBLIC], user) return are_public unless user.present?
return all if user.admin?
where(
'visibility_level IN (:visibility_levels)
OR author_id = :author_id
OR project_id IN (:project_ids)',
visibility_levels: [Snippet::PUBLIC, Snippet::INTERNAL],
author_id: user.id,
project_ids: user.authorized_projects.select(:id))
end end
end end
end end
...@@ -58,7 +58,7 @@ class User < ActiveRecord::Base ...@@ -58,7 +58,7 @@ class User < ActiveRecord::Base
# Groups # Groups
has_many :members, dependent: :destroy has_many :members, dependent: :destroy
has_many :group_members, dependent: :destroy, source: 'GroupMember' has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember'
has_many :groups, through: :group_members has_many :groups, through: :group_members
has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group
has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group
...@@ -66,7 +66,7 @@ class User < ActiveRecord::Base ...@@ -66,7 +66,7 @@ class User < ActiveRecord::Base
# Projects # Projects
has_many :groups_projects, through: :groups, source: :projects has_many :groups_projects, through: :groups, source: :projects
has_many :personal_projects, through: :namespace, source: :projects has_many :personal_projects, through: :namespace, source: :projects
has_many :project_members, dependent: :destroy, class_name: 'ProjectMember' has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember'
has_many :projects, through: :project_members has_many :projects, through: :project_members
has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' has_many :created_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :users_star_projects, dependent: :destroy has_many :users_star_projects, dependent: :destroy
...@@ -801,7 +801,7 @@ class User < ActiveRecord::Base ...@@ -801,7 +801,7 @@ class User < ActiveRecord::Base
unless email_domains.blank? unless email_domains.blank?
match_found = email_domains.any? do |domain| match_found = email_domains.any? do |domain|
escaped = Regexp.escape(domain).gsub('\*','.*?') escaped = Regexp.escape(domain).gsub('\*', '.*?')
regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE
email_domain = Mail::Address.new(self.email).domain email_domain = Mail::Address.new(self.email).domain
email_domain =~ regexp email_domain =~ regexp
......
...@@ -159,8 +159,9 @@ class TodoService ...@@ -159,8 +159,9 @@ class TodoService
def create_todos(users, attributes) def create_todos(users, attributes)
Array(users).map do |user| Array(users).map do |user|
next if pending_todos(user, attributes).exists? next if pending_todos(user, attributes).exists?
Todo.create(attributes.merge(user_id: user.id)) todo = Todo.create(attributes.merge(user_id: user.id))
user.update_todos_count_cache user.update_todos_count_cache
todo
end end
end end
......
...@@ -4,7 +4,7 @@ class LfsObjectUploader < CarrierWave::Uploader::Base ...@@ -4,7 +4,7 @@ class LfsObjectUploader < CarrierWave::Uploader::Base
storage :file storage :file
def store_dir def store_dir
"#{Gitlab.config.lfs.storage_path}/#{model.oid[0,2]}/#{model.oid[2,2]}" "#{Gitlab.config.lfs.storage_path}/#{model.oid[0, 2]}/#{model.oid[2, 2]}"
end end
def cache_dir def cache_dir
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
= f.label :default_snippet_visibility, class: 'control-label col-sm-2' = f.label :default_snippet_visibility, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new) = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
.form-group.group-visibility-level-holder .form-group.project-visibility-level-holder
= f.label :default_group_visibility, class: 'control-label col-sm-2' = f.label :default_group_visibility, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
= render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new) = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
......
.nav-links.sub-nav .nav-links.sub-nav
%ul{ class: (container_class) } %ul{ class: (container_class) }
= nav_link(controller: :system_info) do
= link_to admin_system_info_path, title: 'System Info' do
%span
System Info
= nav_link(controller: :background_jobs) do = nav_link(controller: :background_jobs) do
= link_to admin_background_jobs_path, title: 'Background Jobs' do = link_to admin_background_jobs_path, title: 'Background Jobs' do
%span %span
......
- @no_container = true
- page_title "System Info"
= render 'admin/background_jobs/head'
%div{ class: (container_class) }
.prepend-top-default
.row
.col-sm-4
.light-well
%h4 CPU
.data
%h1= "#{@cpus} cores"
.col-sm-4
.light-well
%h4 Memory
.data
%h1= "#{number_to_human_size(@mem_used)} / #{number_to_human_size(@mem_total)}"
.col-sm-4
.light-well
%h4 Disk
.data
%h1= "#{number_to_human_size(@disk_used)} / #{number_to_human_size(@disk_total)}"
- page_title "Groups", @user.name, "Users" - page_title "Groups", @user.name, "Users"
= render 'admin/users/head' = render 'admin/users/head'
- if @user.group_members.present? - group_members = @user.group_members.includes(:source)
- if group_members.any?
.panel.panel-default .panel.panel-default
.panel-heading Groups: .panel-heading Groups:
%ul.well-list %ul.well-list
- @user.group_members.each do |group_member| - group_members.each do |group_member|
- group = group_member.group - group = group_member.group
%li.group_member %li.group_member
%span{class: ("list-item-name" unless group_member.owner?)} %span{class: ("list-item-name" unless group_member.owner?)}
......
.emoji-menu .emoji-menu
= text_field_tag :emoji_search, "", class: "emoji-search search-input form-control", placeholder: "Seach emojis"
.emoji-menu-content .emoji-menu-content
= text_field_tag :emoji_search, "", class: "emoji-search search-input form-control"
- Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis| - Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis|
%h5.emoji-menu-title %h5.emoji-menu-title
= Gitlab::AwardEmoji::CATEGORIES[category] = Gitlab::AwardEmoji::CATEGORIES[category]
......
- project = event.project
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
%span.event_label.pushed #{event.action_name} #{event.ref_type} %span.event_label.pushed #{event.action_name} #{event.ref_type}
...@@ -5,19 +7,18 @@ ...@@ -5,19 +7,18 @@
%strong= event.ref_name %strong= event.ref_name
- else - else
%strong %strong
= link_to event.ref_name, namespace_project_commits_path(event.project.namespace, event.project, event.ref_name), title: h(event.target_title) = link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title)
at at
= link_to_project event.project = link_to_project project
- if event.push_with_commits? - if event.push_with_commits?
- project = event.project
.event-body .event-body
%ul.well-list.event_commits %ul.well-list.event_commits
- few_commits = event.commits[0...2] - few_commits = event.commits[0...2]
- few_commits.each do |commit| - few_commits.each do |commit|
= render "events/commit", commit: commit, project: project, event: event = render "events/commit", commit: commit, project: project, event: event
- create_mr = event.new_ref? && create_mr_button?(event.project.default_branch, event.ref_name, event.project) - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project)
- if event.commits_count > 1 - if event.commits_count > 1
%li.commits-stat %li.commits-stat
- if event.commits_count > 2 - if event.commits_count > 2
...@@ -27,18 +28,26 @@ ...@@ -27,18 +28,26 @@
- from = event.commit_from - from = event.commit_from
- from_label = truncate_sha(from) - from_label = truncate_sha(from)
- else - else
- from = event.project.default_branch - from = project.default_branch
- from_label = from - from_label = from
= link_to namespace_project_compare_path(event.project.namespace, event.project, from: from, to: event.commit_to) do = link_to namespace_project_compare_path(project.namespace, project, from: from, to: event.commit_to) do
Compare #{from_label}...#{truncate_sha(event.commit_to)} Compare #{from_label}...#{truncate_sha(event.commit_to)}
- if create_mr - if create_mr
%span{"data-user-is" => event.author_id, "data-display" => "inline"} %span{"data-user-is" => event.author_id, "data-display" => "inline"}
or or
= link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do = link_to create_mr_path(project.default_branch, event.ref_name, project) do
create a merge request create a merge request
- elsif create_mr - elsif create_mr
%li.commits-stat{"data-user-is" => event.author_id} %li.commits-stat{"data-user-is" => event.author_id}
= link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do = link_to create_mr_path(project.default_branch, event.ref_name, project) do
Create Merge Request Create Merge Request
- elsif event.rm_ref?
- repository = project.repository
- last_commit = repository.commit(event.commit_from)
- if last_commit
.event-body
%ul.well-list.event_commits
= render "events/commit", commit: last_commit, project: project, event: event
...@@ -15,12 +15,17 @@ ...@@ -15,12 +15,17 @@
%span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) } %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
= visibility_level_icon(@group.visibility_level, fw: false) = visibility_level_icon(@group.visibility_level, fw: false)
%span.hidden-xs
= render 'shared/notifications/button', notification_setting: @notification_setting
- if current_user
.pull-right
= render 'shared/members/access_request_buttons', source: @group
- if @group.description.present? - if @group.description.present?
.cover-desc.description .cover-desc.description
= markdown(@group.description, pipeline: :description) = markdown(@group.description, pipeline: :description)
- if current_user
= render 'shared/members/access_request_buttons', source: @group
%div{ class: container_class } %div{ class: container_class }
.top-area .top-area
......
...@@ -43,6 +43,6 @@ ...@@ -43,6 +43,6 @@
%ul.well-list %ul.well-list
%li= link_to 'See our website for getting help', promo_url + '/getting-help/' %li= link_to 'See our website for getting help', promo_url + '/getting-help/'
%li= link_to 'Use the search bar on the top of this page', '#', onclick: 'Shortcuts.focusSearch(event)' %li= link_to 'Use the search bar on the top of this page', '#', onclick: 'Shortcuts.focusSearch(event)'
%li= link_to 'Use shortcuts', '#', onclick: 'Shortcuts.showHelp(event)' %li= link_to 'Use shortcuts', '#', onclick: 'Shortcuts.toggleHelp()'
%li= link_to 'Get a support subscription', 'https://about.gitlab.com/pricing/' %li= link_to 'Get a support subscription', 'https://about.gitlab.com/pricing/'
%li= link_to 'Compare GitLab editions', 'https://about.gitlab.com/features/#compare' %li= link_to 'Compare GitLab editions', 'https://about.gitlab.com/features/#compare'
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.
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.
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