mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-14 15:16:04 +10:00
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/project-import_url
This commit is contained in:
+1
-1
@@ -691,7 +691,7 @@ Style/ZeroLengthPredicate:
|
||||
# branches, and conditions.
|
||||
Metrics/AbcSize:
|
||||
Enabled: true
|
||||
Max: 70
|
||||
Max: 60
|
||||
|
||||
# Avoid excessive block nesting.
|
||||
Metrics/BlockNesting:
|
||||
|
||||
@@ -1,31 +1,58 @@
|
||||
Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
v 8.7.0 (unreleased)
|
||||
- The Projects::HousekeepingService class has extra instrumentation (Yorick Peterse)
|
||||
- Fix revoking of authorized OAuth applications (Connor Shea)
|
||||
- All service classes (those residing in app/services) are now instrumented (Yorick Peterse)
|
||||
- Developers can now add custom tags to transactions (Yorick Peterse)
|
||||
- Loading of an issue's referenced merge requests and related branches is now done asynchronously (Yorick Peterse)
|
||||
- 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)
|
||||
- Add setting for customizing the list of trusted proxies !3524
|
||||
- Allow projects to be transfered to a lower visibility level group
|
||||
- Fix `signed_in_ip` being set to 127.0.0.1 when using a reverse proxy !3524
|
||||
- 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)
|
||||
- API: Ability to subscribe and unsubscribe from issues and merge requests (Robert Schilling)
|
||||
- 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
|
||||
- API: Ability to update a group (Robert Schilling)
|
||||
- API: Ability to move issues (Robert Schilling)
|
||||
- Fix Error 500 after renaming a project path (Stan Hu)
|
||||
- Fix a bug whith trailing slash in teamcity_url (Charles May)
|
||||
- Allow back dating on issues when created or updated through the API
|
||||
- Allow back dating on issue notes when created through the API
|
||||
- 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
|
||||
- Allow Omniauth providers to be marked as `external` !3657
|
||||
- Add endpoints to archive or unarchive a project !3372
|
||||
- Fix a bug whith trailing slash in bamboo_url
|
||||
- Add links to CI setup documentation from project settings and builds pages
|
||||
- Handle nil descriptions in Slack issue messages (Stan Hu)
|
||||
- API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling)
|
||||
- API: Ability to star and unstar a project (Robert Schilling)
|
||||
- Add default scope to projects to exclude projects pending deletion
|
||||
- Allow to close merge requests which source projects(forks) are deleted.
|
||||
- Ensure empty recipients are rejected in BuildsEmailService
|
||||
- API: Ability to filter milestones by state `active` and `closed` (Robert Schilling)
|
||||
- API: Fix milestone filtering by `iid` (Robert Schilling)
|
||||
- API: Delete notes of issues, snippets, and merge requests (Robert Schilling)
|
||||
- Implement 'Groups View' as an option for dashboard preferences !3379 (Elias W.)
|
||||
- Better errors handling when creating milestones inside groups
|
||||
- Fix high CPU usage when PostReceive receives refs/merge-requests/<id>
|
||||
- Hide `Create a group` help block when creating a new project in a group
|
||||
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
|
||||
- Gracefully handle notes on deleted commits in merge requests (Stan Hu)
|
||||
- Decouple membership and notifications
|
||||
- Fix creation of merge requests for orphaned branches (Stan Hu)
|
||||
- API: Ability to retrieve a single tag (Robert Schilling)
|
||||
- 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)
|
||||
@@ -34,9 +61,33 @@ v 8.7.0 (unreleased)
|
||||
v 8.6.5 (unreleased)
|
||||
- Only update repository language if it is not set to improve performance
|
||||
- Check permissions when user attempts to import members from another project
|
||||
- API: Do not leak group existence via return code (Robert Schilling)
|
||||
- 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
|
||||
- API: Expose 'updated_at' for issue, snippet, and merge request notes (Robert Schilling)
|
||||
- API: User can leave a project through the API when not master or owner. !3613
|
||||
- Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
|
||||
- Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
|
||||
- Improved markdown forms
|
||||
- Diffs load at the correct point when linking from from number
|
||||
- Selected diff rows highlight
|
||||
|
||||
v 8.6.6
|
||||
- Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk)
|
||||
- Project switcher uses new dropdown styling
|
||||
|
||||
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
|
||||
@@ -153,6 +204,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).
|
||||
|
||||
@@ -297,6 +351,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).
|
||||
|
||||
@@ -416,6 +473,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).
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.7.0
|
||||
2.7.2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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'
|
||||
@@ -282,9 +285,9 @@ group :development, :test do
|
||||
gem 'teaspoon', '~> 1.1.0'
|
||||
gem 'teaspoon-jasmine', '~> 2.2.0'
|
||||
|
||||
gem 'spring', '~> 1.6.4'
|
||||
gem 'spring', '~> 1.7.0'
|
||||
gem 'spring-commands-rspec', '~> 1.0.4'
|
||||
gem 'spring-commands-spinach', '~> 1.0.0'
|
||||
gem 'spring-commands-spinach', '~> 1.1.0'
|
||||
gem 'spring-commands-teaspoon', '~> 0.0.2'
|
||||
|
||||
gem 'rubocop', '~> 0.38.0', require: false
|
||||
|
||||
+50
-48
@@ -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)
|
||||
@@ -145,7 +145,7 @@ GEM
|
||||
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)
|
||||
@@ -459,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)
|
||||
@@ -559,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)
|
||||
@@ -589,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)
|
||||
@@ -608,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)
|
||||
@@ -769,19 +769,20 @@ GEM
|
||||
spinach (>= 0.4)
|
||||
spinach-rerun-reporter (0.0.2)
|
||||
spinach (~> 0.8)
|
||||
spring (1.6.4)
|
||||
spring (1.7.1)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
spring-commands-spinach (1.0.0)
|
||||
spring-commands-spinach (1.1.0)
|
||||
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)
|
||||
@@ -887,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)
|
||||
@@ -992,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)
|
||||
@@ -1028,11 +1030,11 @@ DEPENDENCIES
|
||||
slack-notifier (~> 1.2.0)
|
||||
spinach-rails (~> 0.2.1)
|
||||
spinach-rerun-reporter (~> 0.0.2)
|
||||
spring (~> 1.6.4)
|
||||
spring (~> 1.7.0)
|
||||
spring-commands-rspec (~> 1.0.4)
|
||||
spring-commands-spinach (~> 1.0.0)
|
||||
spring-commands-spinach (~> 1.1.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,7 +22,17 @@
|
||||
#= require cal-heatmap
|
||||
#= require turbolinks
|
||||
#= require autosave
|
||||
#= require bootstrap
|
||||
#= require bootstrap/affix
|
||||
#= require bootstrap/alert
|
||||
#= require bootstrap/button
|
||||
#= require bootstrap/collapse
|
||||
#= require bootstrap/dropdown
|
||||
#= require bootstrap/modal
|
||||
#= require bootstrap/scrollspy
|
||||
#= require bootstrap/tab
|
||||
#= require bootstrap/transition
|
||||
#= require bootstrap/tooltip
|
||||
#= require bootstrap/popover
|
||||
#= require select2
|
||||
#= require raphael
|
||||
#= require g.raphael
|
||||
@@ -41,6 +51,7 @@
|
||||
#= require shortcuts_issuable
|
||||
#= require shortcuts_network
|
||||
#= require jquery.nicescroll
|
||||
#= require date.format
|
||||
#= require_tree .
|
||||
#= require fuzzaldrin-plus
|
||||
#= require cropper
|
||||
@@ -163,7 +174,7 @@ $ ->
|
||||
$('.trigger-submit').on 'change', ->
|
||||
$(@).parents('form').submit()
|
||||
|
||||
$('abbr.timeago, .js-timeago').timeago()
|
||||
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), false)
|
||||
|
||||
# Flash
|
||||
if (flash = $(".flash-container")).length > 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>"
|
||||
|
||||
@@ -29,7 +29,11 @@ $(document).on 'keydown.quick_submit', '.js-quick-submit', (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
$form = $(e.target).closest('form')
|
||||
$form.find('input[type=submit], button[type=submit]').disable()
|
||||
$submit_button = $form.find('input[type=submit], button[type=submit]')
|
||||
|
||||
return if $submit_button.attr('disabled')
|
||||
|
||||
$submit_button.disable()
|
||||
$form.submit()
|
||||
|
||||
# If the user tabs to a submit button on a `js-quick-submit` form, display a
|
||||
|
||||
@@ -35,4 +35,18 @@ $.fn.requiresInput = ->
|
||||
$form.on 'change input', fieldSelector, requireInput
|
||||
|
||||
$ ->
|
||||
$('form.js-requires-input').requiresInput()
|
||||
$form = $('form.js-requires-input')
|
||||
$form.requiresInput()
|
||||
|
||||
# Hide or Show the help block when creating a new project
|
||||
# based on the option selected
|
||||
hideOrShowHelpBlock = (form) ->
|
||||
selected = $('.js-select-namespace option:selected')
|
||||
if selected.length and selected.data('options-parent') is 'groups'
|
||||
return form.find('.help-block').hide()
|
||||
else if selected.length
|
||||
form.find('.help-block').show()
|
||||
|
||||
hideOrShowHelpBlock($form)
|
||||
|
||||
$('.select2.js-select-namespace').change -> hideOrShowHelpBlock($form)
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
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()
|
||||
)
|
||||
@@ -28,26 +28,26 @@ class Dispatcher
|
||||
new Todos()
|
||||
when 'projects:milestones:new', 'projects:milestones:edit'
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.milestone-form'))
|
||||
new GLForm($('.milestone-form'))
|
||||
when 'groups:milestones:new'
|
||||
new ZenMode()
|
||||
when 'projects:compare:show'
|
||||
new Diff()
|
||||
when 'projects:issues:new','projects:issues:edit'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new DropzoneInput($('.issue-form'))
|
||||
new GLForm($('.issue-form'))
|
||||
new IssuableForm($('.issue-form'))
|
||||
when 'projects:merge_requests:new', 'projects:merge_requests:edit'
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new DropzoneInput($('.merge-request-form'))
|
||||
new GLForm($('.merge-request-form'))
|
||||
new IssuableForm($('.merge-request-form'))
|
||||
when 'projects:tags:new'
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.tag-form'))
|
||||
new GLForm($('.tag-form'))
|
||||
when 'projects:releases:edit'
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.release-form'))
|
||||
new GLForm($('.release-form'))
|
||||
when 'projects:merge_requests:show'
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsIssuable(true)
|
||||
@@ -137,7 +137,7 @@ class Dispatcher
|
||||
new Wikis()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new ZenMode()
|
||||
new DropzoneInput($('.wiki-form'))
|
||||
new GLForm($('.wiki-form'))
|
||||
when 'snippets'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new ZenMode() if path[2] == 'show'
|
||||
|
||||
@@ -15,11 +15,13 @@ class @DropzoneInput
|
||||
project_uploads_path = window.project_uploads_path or null
|
||||
max_file_size = gon.max_file_size or 10
|
||||
|
||||
form_textarea = $(form).find("textarea.markdown-area")
|
||||
form_textarea = $(form).find(".js-gfm-input")
|
||||
form_textarea.wrap "<div class=\"div-dropzone\"></div>"
|
||||
form_textarea.on 'paste', (event) =>
|
||||
handlePaste(event)
|
||||
|
||||
$mdArea = $(form_textarea).closest('.md-area')
|
||||
|
||||
$(form).setupMarkdownPreview()
|
||||
|
||||
form_dropzone = $(form).find('.div-dropzone')
|
||||
@@ -49,17 +51,16 @@ class @DropzoneInput
|
||||
$(".div-dropzone-alert").alert "close"
|
||||
|
||||
dragover: ->
|
||||
form_textarea.addClass "div-dropzone-focus"
|
||||
$mdArea.addClass 'is-dropzone-hover'
|
||||
form.find(".div-dropzone-hover").css "opacity", 0.7
|
||||
return
|
||||
|
||||
dragleave: ->
|
||||
form_textarea.removeClass "div-dropzone-focus"
|
||||
$mdArea.removeClass 'is-dropzone-hover'
|
||||
form.find(".div-dropzone-hover").css "opacity", 0
|
||||
return
|
||||
|
||||
drop: ->
|
||||
form_textarea.removeClass "div-dropzone-focus"
|
||||
form.find(".div-dropzone-hover").css "opacity", 0
|
||||
form_textarea.focus()
|
||||
return
|
||||
|
||||
@@ -57,14 +57,30 @@ class GitLabDropdownFilter
|
||||
|
||||
filter: (search_text) ->
|
||||
data = @options.data()
|
||||
results = data
|
||||
|
||||
if search_text isnt ""
|
||||
results = fuzzaldrinPlus.filter(data, search_text,
|
||||
key: @options.keys
|
||||
)
|
||||
if data?
|
||||
results = data
|
||||
|
||||
@options.callback results
|
||||
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) ->
|
||||
@@ -106,7 +122,9 @@ class GitLabDropdown
|
||||
FILTER_INPUT = '.dropdown-input .dropdown-input-field'
|
||||
|
||||
constructor: (@el, @options) ->
|
||||
@dropdown = $(@el).parent()
|
||||
self = @
|
||||
selector = $(@el).data "target"
|
||||
@dropdown = if selector? then $(selector) else $(@el).parent()
|
||||
|
||||
# Set Defaults
|
||||
{
|
||||
@@ -123,7 +141,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 +165,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 +401,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
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
class @GLForm
|
||||
constructor: (@form) ->
|
||||
@textarea = @form.find('textarea.js-gfm-input')
|
||||
|
||||
# Before we start, we should clean up any previous data for this form
|
||||
@destroy()
|
||||
|
||||
# Setup the form
|
||||
@setupForm()
|
||||
|
||||
@form.data 'gl-form', @
|
||||
|
||||
destroy: ->
|
||||
# Clean form listeners
|
||||
@clearEventListeners()
|
||||
@form.data 'gl-form', null
|
||||
|
||||
setupForm: ->
|
||||
isNewForm = @form.is(':not(.gfm-form)')
|
||||
|
||||
@form.removeClass 'js-new-note-form'
|
||||
|
||||
if isNewForm
|
||||
@form.find('.div-dropzone').remove()
|
||||
@form.addClass('gfm-form')
|
||||
disableButtonIfEmptyField @form.find('.js-note-text'), @form.find('.js-comment-button')
|
||||
|
||||
# remove notify commit author checkbox for non-commit notes
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
new DropzoneInput(@form)
|
||||
|
||||
autosize(@textarea)
|
||||
|
||||
# form and textarea event listeners
|
||||
@addEventListeners()
|
||||
|
||||
# hide discard button
|
||||
@form.find('.js-note-discard').hide()
|
||||
|
||||
@form.show()
|
||||
|
||||
clearEventListeners: ->
|
||||
@textarea.off 'focus'
|
||||
@textarea.off 'blur'
|
||||
|
||||
addEventListeners: ->
|
||||
@textarea.on 'focus', ->
|
||||
$(@).closest('.md-area').addClass 'is-focused'
|
||||
|
||||
@textarea.on 'blur', ->
|
||||
$(@).closest('.md-area').removeClass 'is-focused'
|
||||
@@ -10,6 +10,9 @@ class @Issue
|
||||
@initTaskList()
|
||||
@initIssueBtnEventListeners()
|
||||
|
||||
@initMergeRequests()
|
||||
@initRelatedBranches()
|
||||
|
||||
initTaskList: ->
|
||||
$('.detail-page-description .js-task-list-container').taskList('enable')
|
||||
$(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
|
||||
@@ -69,3 +72,23 @@ class @Issue
|
||||
type: 'PATCH'
|
||||
url: $('form.js-issuable-update').attr('action')
|
||||
data: patchData
|
||||
|
||||
initMergeRequests: ->
|
||||
$container = $('#merge-requests')
|
||||
|
||||
$.getJSON($container.data('url'))
|
||||
.error ->
|
||||
new Flash('Failed to load referenced merge requests', 'alert')
|
||||
.success (data) ->
|
||||
if 'html' of data
|
||||
$container.html(data.html)
|
||||
|
||||
initRelatedBranches: ->
|
||||
$container = $('#related-branches')
|
||||
|
||||
$.getJSON($container.data('url'))
|
||||
.error ->
|
||||
new Flash('Failed to load related branches', 'alert')
|
||||
.success (data) ->
|
||||
if 'html' of data
|
||||
$container.html(data.html)
|
||||
|
||||
@@ -34,7 +34,7 @@ class @LabelsSelect
|
||||
labelHTMLTemplate = _.template(
|
||||
'<% _.each(labels, function(label){ %>
|
||||
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= label.title %>">
|
||||
<span class="label color-label" style="background-color: <%= label.color %>;">
|
||||
<span class="label has-tooltip color-label" title="<%= label.description %>" style="background-color: <%= label.color %>;">
|
||||
<%= label.title %>
|
||||
</span>
|
||||
</a>
|
||||
@@ -165,6 +165,8 @@ class @LabelsSelect
|
||||
.html(template)
|
||||
$sidebarCollapsedValue.text(labelCount)
|
||||
|
||||
$('.has-tooltip', $value).tooltip(container: 'body')
|
||||
|
||||
$value
|
||||
.find('a')
|
||||
.each((i) ->
|
||||
@@ -218,7 +220,7 @@ class @LabelsSelect
|
||||
selectable: true
|
||||
|
||||
toggleLabel: (selected) ->
|
||||
if selected and selected.title isnt 'Any Label'
|
||||
if selected and selected.title?
|
||||
selected.title
|
||||
else
|
||||
defaultLabel
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
((w) ->
|
||||
|
||||
w.gl ?= {}
|
||||
w.gl.utils ?= {}
|
||||
|
||||
w.gl.utils.formatDate = (datetime) ->
|
||||
dateFormat(datetime, 'mmm d, yyyy h:MMtt Z')
|
||||
|
||||
w.gl.utils.localTimeAgo = ($timeagoEls, setTimeago = true) ->
|
||||
$timeagoEls.each( ->
|
||||
$el = $(@)
|
||||
$el.attr('title', gl.utils.formatDate($el.attr('datetime')))
|
||||
)
|
||||
|
||||
$timeagoEls.timeago() if setTimeago
|
||||
|
||||
) window
|
||||
@@ -2,6 +2,11 @@
|
||||
notificationGranted = (message, opts, onclick) ->
|
||||
notification = new Notification(message, opts)
|
||||
|
||||
# Hide the notification after X amount of seconds
|
||||
setTimeout ->
|
||||
notification.close()
|
||||
, 8000
|
||||
|
||||
if onclick
|
||||
notification.onclick = onclick
|
||||
|
||||
|
||||
@@ -73,7 +73,8 @@ class @MergeRequestTabs
|
||||
@expandView()
|
||||
else if action == 'diffs'
|
||||
@loadDiff($target.attr('href'))
|
||||
@shrinkView()
|
||||
if bp? and bp.getBreakpointSize() isnt 'lg'
|
||||
@shrinkView()
|
||||
else if action == 'builds'
|
||||
@loadBuilds($target.attr('href'))
|
||||
@expandView()
|
||||
@@ -84,8 +85,10 @@ class @MergeRequestTabs
|
||||
|
||||
scrollToElement: (container) ->
|
||||
if window.location.hash
|
||||
$el = $("div#{container} #{window.location.hash}")
|
||||
$('body').scrollTo($el.offset().top) if $el.length
|
||||
navBarHeight = $('.navbar-gitlab').outerHeight()
|
||||
|
||||
$el = $("#{container} #{window.location.hash}")
|
||||
$.scrollTo("#{container} #{window.location.hash}", offset: -navBarHeight) if $el.length
|
||||
|
||||
# Activate a tab based on the current action
|
||||
activateTab: (action) ->
|
||||
@@ -141,7 +144,7 @@ class @MergeRequestTabs
|
||||
url: "#{source}.json"
|
||||
success: (data) =>
|
||||
document.querySelector("div#commits").innerHTML = data.html
|
||||
$('.js-timeago').timeago()
|
||||
gl.utils.localTimeAgo($('.js-timeago', 'div#commits'))
|
||||
@commitsLoaded = true
|
||||
@scrollToElement("#commits")
|
||||
|
||||
@@ -151,12 +154,38 @@ class @MergeRequestTabs
|
||||
@_get
|
||||
url: "#{source}.json" + @_location.search
|
||||
success: (data) =>
|
||||
document.querySelector("div#diffs").innerHTML = data.html
|
||||
$('.js-timeago').timeago()
|
||||
$('div#diffs .js-syntax-highlight').syntaxHighlight()
|
||||
$('#diffs').html data.html
|
||||
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'))
|
||||
$('#diffs .js-syntax-highlight').syntaxHighlight()
|
||||
@expandViewContainer() if @diffViewType() is 'parallel'
|
||||
@diffsLoaded = true
|
||||
@scrollToElement("#diffs")
|
||||
@highlighSelectedLine()
|
||||
|
||||
$(document)
|
||||
.off 'click', '.diff-line-num a'
|
||||
.on 'click', '.diff-line-num a', (e) =>
|
||||
e.preventDefault()
|
||||
window.location.hash = $(e.currentTarget).attr 'href'
|
||||
@highlighSelectedLine()
|
||||
@scrollToElement("#diffs")
|
||||
|
||||
highlighSelectedLine: ->
|
||||
$('.hll').removeClass 'hll'
|
||||
locationHash = window.location.hash
|
||||
|
||||
if locationHash isnt ''
|
||||
hashClassString = ".#{locationHash.replace('#', '')}"
|
||||
$diffLine = $(locationHash)
|
||||
|
||||
if $diffLine.is ':not(tr)'
|
||||
$diffLine = $("td#{locationHash}, td#{hashClassString}")
|
||||
else
|
||||
$diffLine = $('td', $diffLine)
|
||||
|
||||
$diffLine.addClass 'hll'
|
||||
diffLineTop = $diffLine.offset().top
|
||||
navBarHeight = $('.navbar-gitlab').outerHeight()
|
||||
|
||||
loadBuilds: (source) ->
|
||||
return if @buildsLoaded
|
||||
@@ -165,7 +194,7 @@ class @MergeRequestTabs
|
||||
url: "#{source}.json"
|
||||
success: (data) =>
|
||||
document.querySelector("div#builds").innerHTML = data.html
|
||||
$('.js-timeago').timeago()
|
||||
gl.utils.localTimeAgo($('.js-timeago', 'div#builds'))
|
||||
@buildsLoaded = true
|
||||
@scrollToElement("#builds")
|
||||
|
||||
|
||||
@@ -12,10 +12,19 @@ class @MergeRequestWidget
|
||||
@readyForCICheck = true
|
||||
clearInterval @fetchBuildStatusInterval
|
||||
|
||||
@clearEventListeners()
|
||||
@addEventListeners()
|
||||
@pollCIStatus()
|
||||
notifyPermissions()
|
||||
|
||||
setOpts: (@opts) ->
|
||||
clearEventListeners: ->
|
||||
$(document).off 'page:change.merge_request'
|
||||
|
||||
addEventListeners: ->
|
||||
$(document).on 'page:change.merge_request', =>
|
||||
if $('body').data('page') isnt 'projects:merge_requests:show'
|
||||
clearInterval @fetchBuildStatusInterval
|
||||
@clearEventListeners()
|
||||
|
||||
mergeInProgress: (deleteSourceBranch = false)->
|
||||
$.ajax
|
||||
@@ -38,7 +47,7 @@ class @MergeRequestWidget
|
||||
$('.mr-state-widget').replaceWith(data)
|
||||
|
||||
ciLabelForStatus: (status) ->
|
||||
if status == 'success'
|
||||
if status is 'success'
|
||||
'passed'
|
||||
else
|
||||
status
|
||||
@@ -67,18 +76,28 @@ class @MergeRequestWidget
|
||||
@opts.ci_status = data.status
|
||||
return
|
||||
|
||||
if data.status isnt @opts.ci_status
|
||||
if data.status isnt @opts.ci_status and data.status?
|
||||
@showCIStatus data.status
|
||||
if data.coverage
|
||||
@showCICoverage data.coverage
|
||||
|
||||
if showNotification
|
||||
message = @opts.ci_message.replace('{{status}}', @ciLabelForStatus(data.status))
|
||||
status = @ciLabelForStatus(data.status)
|
||||
|
||||
if status is "preparing"
|
||||
title = @opts.ci_title.preparing
|
||||
status = status.charAt(0).toUpperCase() + status.slice(1);
|
||||
message = @opts.ci_message.preparing.replace('{{status}}', status)
|
||||
else
|
||||
title = @opts.ci_title.normal
|
||||
message = @opts.ci_message.normal.replace('{{status}}', status)
|
||||
|
||||
title = title.replace('{{status}}', status)
|
||||
message = message.replace('{{sha}}', data.sha)
|
||||
message = message.replace('{{title}}', data.title)
|
||||
|
||||
notify(
|
||||
"Build #{@ciLabelForStatus(data.status)}",
|
||||
title,
|
||||
message,
|
||||
@opts.gitlab_icon,
|
||||
->
|
||||
@@ -98,6 +117,8 @@ class @MergeRequestWidget
|
||||
@setMergeButtonClass('btn-danger')
|
||||
when "running", "pending"
|
||||
@setMergeButtonClass('btn-warning')
|
||||
when "success"
|
||||
@setMergeButtonClass('btn-create')
|
||||
else
|
||||
$('.ci_widget.ci-error').show()
|
||||
@setMergeButtonClass('btn-danger')
|
||||
@@ -107,4 +128,6 @@ class @MergeRequestWidget
|
||||
$('.ci_widget:visible .ci-coverage').text(text)
|
||||
|
||||
setMergeButtonClass: (css_class) ->
|
||||
$('.accept_merge_request').removeClass("btn-create").addClass(css_class)
|
||||
$('.accept_merge_request')
|
||||
.removeClass('btn-danger btn-warning btn-create')
|
||||
.addClass(css_class)
|
||||
|
||||
@@ -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"]')
|
||||
|
||||
@@ -163,9 +163,15 @@ class @Notes
|
||||
else if @isNewNote(note)
|
||||
@note_ids.push(note.id)
|
||||
|
||||
$('ul.main-notes-list')
|
||||
$notesList = $('ul.main-notes-list')
|
||||
|
||||
$notesList
|
||||
.append(note.html)
|
||||
.syntaxHighlight()
|
||||
|
||||
# Update datetime format on the recent note
|
||||
gl.utils.localTimeAgo($notesList.find("#note_#{note.id} .js-timeago"), false)
|
||||
|
||||
@initTaskList()
|
||||
@updateNotesCount(1)
|
||||
|
||||
@@ -217,6 +223,8 @@ class @Notes
|
||||
# append new note to all matching discussions
|
||||
discussionContainer.append note_html
|
||||
|
||||
gl.utils.localTimeAgo($('.js-timeago', note_html), false)
|
||||
|
||||
@updateNotesCount(1)
|
||||
|
||||
###
|
||||
@@ -275,32 +283,10 @@ class @Notes
|
||||
show the form
|
||||
###
|
||||
setupNoteForm: (form) ->
|
||||
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
|
||||
form.removeClass "js-new-note-form"
|
||||
form.find('.div-dropzone').remove()
|
||||
|
||||
# hide discard button
|
||||
form.find('.js-note-discard').hide()
|
||||
|
||||
# setup preview buttons
|
||||
previewButton = form.find(".js-md-preview-button")
|
||||
new GLForm form
|
||||
|
||||
textarea = form.find(".js-note-text")
|
||||
|
||||
textarea.on "input", ->
|
||||
if $(this).val().trim() isnt ""
|
||||
previewButton.removeClass("turn-off").addClass "turn-on"
|
||||
else
|
||||
previewButton.removeClass("turn-on").addClass "turn-off"
|
||||
|
||||
textarea.on 'focus', ->
|
||||
$(this).closest('.md-area').addClass 'is-focused'
|
||||
|
||||
textarea.on 'blur', ->
|
||||
$(this).closest('.md-area').removeClass 'is-focused'
|
||||
|
||||
autosize(textarea)
|
||||
|
||||
new Autosave textarea, [
|
||||
"Note"
|
||||
form.find("#note_commit_id").val()
|
||||
@@ -309,11 +295,6 @@ class @Notes
|
||||
form.find("#note_noteable_id").val()
|
||||
]
|
||||
|
||||
# remove notify commit author checkbox for non-commit notes
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
new DropzoneInput(form)
|
||||
form.show()
|
||||
|
||||
###
|
||||
Called in response to the new note form being submitted
|
||||
|
||||
@@ -345,7 +326,9 @@ class @Notes
|
||||
updateNote: (_xhr, note, _status) =>
|
||||
# Convert returned HTML to a jQuery object so we can modify it further
|
||||
$html = $(note.html)
|
||||
$('.js-timeago', $html).timeago()
|
||||
|
||||
gl.utils.localTimeAgo($('.js-timeago', $html))
|
||||
|
||||
$html.syntaxHighlight()
|
||||
$html.find('.js-task-list-container').taskList('enable')
|
||||
|
||||
@@ -365,34 +348,15 @@ class @Notes
|
||||
note = $(this).closest(".note")
|
||||
note.addClass "is-editting"
|
||||
form = note.find(".note-edit-form")
|
||||
isNewForm = form.is(':not(.gfm-form)')
|
||||
if isNewForm
|
||||
form.addClass('gfm-form')
|
||||
|
||||
form.addClass('current-note-edit-form')
|
||||
|
||||
# Show the attachment delete link
|
||||
note.find(".js-note-attachment-delete").show()
|
||||
|
||||
# Setup markdown form
|
||||
if isNewForm
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
new DropzoneInput(form)
|
||||
new GLForm form
|
||||
|
||||
textarea = form.find("textarea")
|
||||
textarea.focus()
|
||||
|
||||
if isNewForm
|
||||
autosize(textarea)
|
||||
|
||||
# HACK (rspeicher/DouweM): Work around a Chrome 43 bug(?).
|
||||
# The textarea has the correct value, Chrome just won't show it unless we
|
||||
# modify it, so let's clear it and re-set it!
|
||||
value = textarea.val()
|
||||
textarea.val ""
|
||||
textarea.val value
|
||||
|
||||
if isNewForm
|
||||
disableButtonIfEmptyField textarea, form.find(".js-comment-button")
|
||||
form.find(".js-note-text").focus()
|
||||
|
||||
###
|
||||
Called in response to clicking the edit note link
|
||||
@@ -549,6 +513,9 @@ class @Notes
|
||||
removeDiscussionNoteForm: (form)->
|
||||
row = form.closest("tr")
|
||||
|
||||
glForm = form.data 'gl-form'
|
||||
glForm.destroy()
|
||||
|
||||
form.find(".js-note-text").data("autosave").reset()
|
||||
|
||||
# show the reply button (will only work for replies)
|
||||
@@ -560,7 +527,6 @@ class @Notes
|
||||
# only remove the form
|
||||
form.remove()
|
||||
|
||||
|
||||
cancelDiscussionForm: (e) =>
|
||||
e.preventDefault()
|
||||
form = $(e.target).closest(".js-discussion-note-form")
|
||||
|
||||
@@ -18,8 +18,11 @@ class @Profile
|
||||
$(this).find('.btn-save').enable()
|
||||
$(this).find('.loading-gif').hide()
|
||||
|
||||
$('.update-notifications').on 'ajax:complete', ->
|
||||
$(this).find('.btn-save').enable()
|
||||
$('.update-notifications').on 'ajax:success', (e, data) ->
|
||||
if data.saved
|
||||
new Flash("Notification settings saved", "notice")
|
||||
else
|
||||
new Flash("Failed to save new settings", "alert")
|
||||
|
||||
@bindEvents()
|
||||
|
||||
|
||||
@@ -37,19 +37,20 @@ class @Project
|
||||
$('.update-notification').on 'click', (e) ->
|
||||
e.preventDefault()
|
||||
notification_level = $(@).data 'notification-level'
|
||||
$('#notification_level').val(notification_level)
|
||||
label = $(@).data 'notification-title'
|
||||
$('#notification_setting_level').val(notification_level)
|
||||
$('#notification-form').submit()
|
||||
label = null
|
||||
switch notification_level
|
||||
when 0 then label = ' Disabled '
|
||||
when 1 then label = ' Participating '
|
||||
when 2 then label = ' Watching '
|
||||
when 3 then label = ' Global '
|
||||
when 4 then label = ' On Mention '
|
||||
$('#notifications-button').empty().append("<i class='fa fa-bell'></i>" + label + "<i class='fa fa-angle-down'></i>")
|
||||
$(@).parents('ul').find('li.active').removeClass 'active'
|
||||
$(@).parent().addClass 'active'
|
||||
|
||||
$('#notification-form').on 'ajax:success', (e, data) ->
|
||||
if data.saved
|
||||
new Flash("Notification settings saved", "notice")
|
||||
else
|
||||
new Flash("Failed to save new settings", "alert")
|
||||
|
||||
|
||||
@projectSelectDropdown()
|
||||
|
||||
projectSelectDropdown: ->
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
class @ProjectSelect
|
||||
constructor: ->
|
||||
$('.js-projects-dropdown-toggle').each (i, dropdown) ->
|
||||
$dropdown = $(dropdown)
|
||||
|
||||
$dropdown.glDropdown(
|
||||
filterable: true
|
||||
filterRemote: true
|
||||
search:
|
||||
fields: ['name_with_namespace']
|
||||
data: (term, callback) ->
|
||||
finalCallback = (projects) ->
|
||||
callback projects
|
||||
|
||||
if @includeGroups
|
||||
projectsCallback = (projects) ->
|
||||
groupsCallback = (groups) ->
|
||||
data = groups.concat(projects)
|
||||
finalCallback(data)
|
||||
|
||||
Api.groups term, false, groupsCallback
|
||||
else
|
||||
projectsCallback = finalCallback
|
||||
|
||||
if @groupId
|
||||
Api.groupProjects @groupId, term, projectsCallback
|
||||
else
|
||||
Api.projects term, @orderBy, projectsCallback
|
||||
url: (project) ->
|
||||
project.web_url
|
||||
text: (project) ->
|
||||
project.name_with_namespace
|
||||
)
|
||||
|
||||
$('.ajax-project-select').each (i, select) ->
|
||||
@groupId = $(select).data('group-id')
|
||||
@includeGroups = $(select).data('include-groups')
|
||||
|
||||
@@ -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,12 @@ 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')
|
||||
return unless todoLink
|
||||
|
||||
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,12 +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;
|
||||
@@ -49,7 +50,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
a:not(.btn) {
|
||||
color: $gl-dark-link-color;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,6 @@
|
||||
/**
|
||||
* Styles that apply to all GFM related forms.
|
||||
*/
|
||||
.issue-form, .merge-request-form, .wiki-form {
|
||||
.description {
|
||||
height: 16em;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wiki-form {
|
||||
.description {
|
||||
height: 26em;
|
||||
}
|
||||
}
|
||||
|
||||
.milestone-form {
|
||||
.description {
|
||||
height: 14em;
|
||||
}
|
||||
}
|
||||
|
||||
.gfm-commit, .gfm-commit_range {
|
||||
font-family: $monospace_font;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ header {
|
||||
}
|
||||
|
||||
.header-content {
|
||||
position: relative;
|
||||
height: $header-height;
|
||||
padding-right: 20px;
|
||||
|
||||
@@ -76,6 +77,10 @@ header {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
font-size: 19px;
|
||||
@@ -123,11 +128,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;
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
.div-dropzone-wrapper {
|
||||
.div-dropzone {
|
||||
position: relative;
|
||||
margin-bottom: -5px;
|
||||
|
||||
.div-dropzone-focus {
|
||||
border-color: #66afe9 !important;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6) !important;
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
.div-dropzone-hover {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -0.5em;
|
||||
margin-left: -0.6em;
|
||||
margin-top: -11.5px;
|
||||
margin-left: -15px;
|
||||
opacity: 0;
|
||||
font-size: 50px;
|
||||
font-size: 30px;
|
||||
transition: opacity 200ms ease-in-out;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -58,12 +58,12 @@
|
||||
|
||||
.nav-search {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
width: 100%;
|
||||
padding: 11px 0;
|
||||
|
||||
/* Small devices (phones, tablets, 768px and lower) */
|
||||
@media (max-width: $screen-sm-min) {
|
||||
width: 100%;
|
||||
@media (min-width: $screen-sm-min) {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
display: none;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
@import "bootstrap/modals";
|
||||
@import "bootstrap/tooltip";
|
||||
@import "bootstrap/popovers";
|
||||
@import "bootstrap/carousel";
|
||||
|
||||
// Utility classes
|
||||
.clearfix {
|
||||
|
||||
@@ -250,14 +250,6 @@ a > code {
|
||||
* Textareas intended for GFM
|
||||
*
|
||||
*/
|
||||
.js-gfm-input {
|
||||
font-family: $monospace_font;
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
.md-preview {
|
||||
}
|
||||
|
||||
.strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -28,6 +28,7 @@ $gl-link-color: #3084bb;
|
||||
$gl-dark-link-color: #333;
|
||||
$gl-placeholder-color: #8f8f8f;
|
||||
$gl-icon-color: $gl-placeholder-color;
|
||||
$gl-grayish-blue: #7f8fa4;
|
||||
$gl-gray: $gl-text-color;
|
||||
$gl-header-color: $gl-title-color;
|
||||
|
||||
@@ -81,7 +82,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 +109,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;
|
||||
@@ -147,18 +150,26 @@ $light-grey-header: #faf9f9;
|
||||
*/
|
||||
$gl-primary: $blue-normal;
|
||||
$gl-success: $green-normal;
|
||||
$gl-success-focus: rgba($gl-success, .4);
|
||||
$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 +202,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
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
td.diff-line-num.hll:not(.empty-cell),
|
||||
td.line_content.hll:not(.empty-cell) {
|
||||
background-color: #557;
|
||||
border-color: darken(#557, 15%);
|
||||
}
|
||||
|
||||
.diff-line-num.new, .line_content.new {
|
||||
@include diff_background(rgba(51, 255, 51, 0.1), rgba(51, 255, 51, 0.2), #808080);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
td.diff-line-num.hll:not(.empty-cell),
|
||||
td.line_content.hll:not(.empty-cell) {
|
||||
background-color: #49483e;
|
||||
border-color: darken(#49483e, 15%);
|
||||
}
|
||||
|
||||
.diff-line-num.new, .line_content.new {
|
||||
@include diff_background(rgba(166, 226, 46, 0.1), rgba(166, 226, 46, 0.15), #808080);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
td.diff-line-num.hll:not(.empty-cell),
|
||||
td.line_content.hll:not(.empty-cell) {
|
||||
background-color: #174652;
|
||||
border-color: darken(#174652, 15%);
|
||||
}
|
||||
|
||||
.diff-line-num.new, .line_content.new {
|
||||
@include diff_background(rgba(133, 153, 0, 0.15), rgba(133, 153, 0, 0.25), #113b46);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
.diff-line-num, .diff-line-num a {
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
color: $black-transparent;
|
||||
}
|
||||
|
||||
// Code itself
|
||||
@@ -21,6 +21,12 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
td.diff-line-num.hll:not(.empty-cell),
|
||||
td.line_content.hll:not(.empty-cell) {
|
||||
background-color: #ddd8c5;
|
||||
border-color: darken(#ddd8c5, 15%);
|
||||
}
|
||||
|
||||
.diff-line-num.new, .line_content.new {
|
||||
@include diff_background(rgba(133, 153, 0, 0.2), rgba(133, 153, 0, 0.25), #c5d0d4);
|
||||
}
|
||||
@@ -30,7 +36,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 {
|
||||
@@ -21,38 +21,44 @@
|
||||
|
||||
// Diff line
|
||||
.line_holder {
|
||||
td.diff-line-num.hll:not(.empty-cell),
|
||||
td.line_content.hll:not(.empty-cell) {
|
||||
background-color: #f8eec7;
|
||||
border-color: darken(#f8eec7, 15%);
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -63,10 +66,10 @@
|
||||
.line_holder td {
|
||||
line-height: $code_line_height;
|
||||
font-size: $code_font_size;
|
||||
}
|
||||
|
||||
td {
|
||||
white-space: nowrap;
|
||||
span {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +112,10 @@
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
.text-file.diff-wrap-lines table .line_holder td span {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
.image {
|
||||
background: #ddd;
|
||||
@@ -321,6 +328,16 @@
|
||||
float: right;
|
||||
}
|
||||
|
||||
.diffs {
|
||||
.content-block {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.files-changed {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
// Mobile
|
||||
@media (max-width: 480px) {
|
||||
.diff-title {
|
||||
|
||||
@@ -41,8 +41,17 @@
|
||||
word-wrap: break-word;
|
||||
|
||||
.md {
|
||||
color: #7f8fa4;
|
||||
color: $gl-grayish-blue;
|
||||
font-size: $gl-font-size;
|
||||
|
||||
.label {
|
||||
color: $gl-text-color;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
iframe.twitter-share-button {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
.form-actions {
|
||||
margin: -$gl-padding+1;
|
||||
}
|
||||
}
|
||||
|
||||
body.modal-open {
|
||||
|
||||
@@ -263,6 +263,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
a:hover {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-toggle {
|
||||
width: 100%;
|
||||
padding-top: 6px;
|
||||
@@ -316,3 +322,9 @@
|
||||
color: #8c8c8c;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-form-padding-top {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
padding-top: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,63 @@
|
||||
background-color: $gl-danger;
|
||||
color: $white-light;
|
||||
}
|
||||
|
||||
@mixin labels-mobile {
|
||||
@media (max-width: $screen-xs-min) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.manage-labels-list {
|
||||
|
||||
.prepend-left-10, .prepend-description-left {
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
vertical-align: middle;
|
||||
|
||||
@include labels-mobile;
|
||||
}
|
||||
|
||||
.prepend-description-left {
|
||||
width: 57%;
|
||||
|
||||
@include labels-mobile;
|
||||
}
|
||||
|
||||
.pull-info-right {
|
||||
float: right;
|
||||
|
||||
@media (max-width: $screen-xs-min) {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
border-color: transparent;
|
||||
padding: 6px;
|
||||
color: $gl-text-color;
|
||||
|
||||
&.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;
|
||||
@@ -140,6 +142,7 @@
|
||||
overflow: hidden;
|
||||
font-size: 90%;
|
||||
margin: 0 3px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.mr-list {
|
||||
@@ -174,10 +177,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.merge-request-form .select2-container {
|
||||
width: 250px !important;
|
||||
}
|
||||
|
||||
#modal_merge_info .modal-dialog {
|
||||
width: 600px;
|
||||
|
||||
@@ -200,3 +199,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;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* 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;
|
||||
@@ -40,6 +40,7 @@
|
||||
}
|
||||
|
||||
.note-textarea {
|
||||
display: block;
|
||||
padding: 10px 0;
|
||||
font-family: $regular_font;
|
||||
border: 0;
|
||||
@@ -63,7 +64,7 @@
|
||||
|
||||
&.is-focused {
|
||||
border-color: $focus-border-color;
|
||||
box-shadow: 0 0 2px rgba(#000, .2),
|
||||
box-shadow: 0 0 2px $black-transparent,
|
||||
0 0 4px rgba($focus-border-color, .4);
|
||||
|
||||
.comment-toolbar,
|
||||
@@ -71,12 +72,35 @@
|
||||
border-color: $focus-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-dropzone-hover {
|
||||
border-color: $gl-success;
|
||||
box-shadow: 0 0 2px $black-transparent,
|
||||
0 0 4px $gl-success-focus;
|
||||
|
||||
.comment-toolbar,
|
||||
.nav-links {
|
||||
border-color: $gl-success;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
code {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
pre {
|
||||
code {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.discussion-form {
|
||||
padding: $gl-padding-top $gl-padding;
|
||||
background-color: #fff;
|
||||
background-color: $white-light;
|
||||
}
|
||||
|
||||
.note-edit-form {
|
||||
@@ -113,13 +137,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,
|
||||
@@ -80,9 +81,15 @@ ul.notes {
|
||||
@include md-typography;
|
||||
|
||||
// On diffs code should wrap nicely and not overflow
|
||||
pre {
|
||||
p {
|
||||
code {
|
||||
white-space: pre;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
pre {
|
||||
code {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,15 +118,16 @@ ul.notes {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.note-header {
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 1px solid $border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +137,7 @@ ul.notes {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.line_content {
|
||||
white-space: pre-wrap;
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,29 +145,37 @@ 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 {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
border-left: 1px solid #ddd !important;
|
||||
}
|
||||
|
||||
&.notes_content {
|
||||
background-color: #fff;
|
||||
background-color: $background-color;
|
||||
border-width: 1px 0;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
white-space: normal;
|
||||
|
||||
&.parallel {
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.notes {
|
||||
background-color: $white-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,9 +191,6 @@ ul.notes {
|
||||
}
|
||||
}
|
||||
|
||||
.author_link {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.note-headline-light,
|
||||
@@ -203,14 +216,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
color: #7f8fa4;
|
||||
font-size: $gl-font-size;
|
||||
|
||||
.label {
|
||||
color: $gl-text-color;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #5c5d5e;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ require 'fogbugz'
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
include Gitlab::CurrentSettings
|
||||
include Gitlab::GonHelper
|
||||
include GitlabRoutingHelper
|
||||
include PageLayoutHelper
|
||||
|
||||
@@ -47,6 +48,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
|
||||
|
||||
@@ -148,20 +159,6 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
end
|
||||
|
||||
def add_gon_variables
|
||||
gon.api_version = API::API.version
|
||||
gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
|
||||
gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
|
||||
gon.max_file_size = current_application_settings.max_attachment_size
|
||||
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
|
||||
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
|
||||
|
||||
if current_user
|
||||
gon.current_user_id = current_user.id
|
||||
gon.api_token = current_user.private_token
|
||||
end
|
||||
end
|
||||
|
||||
def validate_user_service_ticket!
|
||||
return unless signed_in? && session[:service_tickets]
|
||||
|
||||
|
||||
@@ -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
|
||||
if create_milestones(project_ids)
|
||||
redirect_to milestone_path(title)
|
||||
else
|
||||
render_new_with_error(project_ids.empty?)
|
||||
end
|
||||
|
||||
redirect_to milestone_path(title)
|
||||
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
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
class Groups::NotificationSettingsController < Groups::ApplicationController
|
||||
before_action :authenticate_user!
|
||||
|
||||
def update
|
||||
notification_setting = current_user.notification_settings_for(group)
|
||||
saved = notification_setting.update_attributes(notification_setting_params)
|
||||
|
||||
render json: { saved: saved }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def notification_setting_params
|
||||
params.require(:notification_setting).permit(:level)
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,11 @@
|
||||
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
|
||||
include Gitlab::CurrentSettings
|
||||
include Gitlab::GonHelper
|
||||
include PageLayoutHelper
|
||||
|
||||
before_action :verify_user_oauth_applications_enabled
|
||||
before_action :authenticate_user!
|
||||
before_action :add_gon_variables
|
||||
|
||||
layout 'profile'
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
|
||||
continue_login_process
|
||||
end
|
||||
rescue Gitlab::OAuth::SignupDisabledError
|
||||
handle_signup_error
|
||||
end
|
||||
|
||||
def omniauth_error
|
||||
@@ -92,16 +94,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
continue_login_process
|
||||
end
|
||||
rescue Gitlab::OAuth::SignupDisabledError
|
||||
label = Gitlab::OAuth::Provider.label_for(oauth['provider'])
|
||||
message = "Signing in using your #{label} account without a pre-existing GitLab account is not allowed."
|
||||
|
||||
if current_application_settings.signup_enabled?
|
||||
message << " Create a GitLab account first, and then connect it to your #{label} account."
|
||||
end
|
||||
|
||||
flash[:notice] = message
|
||||
|
||||
redirect_to new_user_session_path
|
||||
handle_signup_error
|
||||
end
|
||||
|
||||
def handle_service_ticket provider, ticket
|
||||
@@ -122,6 +115,19 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
end
|
||||
end
|
||||
|
||||
def handle_signup_error
|
||||
label = Gitlab::OAuth::Provider.label_for(oauth['provider'])
|
||||
message = "Signing in using your #{label} account without a pre-existing GitLab account is not allowed."
|
||||
|
||||
if current_application_settings.signup_enabled?
|
||||
message << " Create a GitLab account first, and then connect it to your #{label} account."
|
||||
end
|
||||
|
||||
flash[:notice] = message
|
||||
|
||||
redirect_to new_user_session_path
|
||||
end
|
||||
|
||||
def oauth
|
||||
@oauth ||= request.env['omniauth.auth']
|
||||
end
|
||||
|
||||
@@ -1,39 +1,18 @@
|
||||
class Profiles::NotificationsController < Profiles::ApplicationController
|
||||
def show
|
||||
@user = current_user
|
||||
@notification = current_user.notification
|
||||
@project_members = current_user.project_members
|
||||
@group_members = current_user.group_members
|
||||
@group_notifications = current_user.notification_settings.for_groups
|
||||
@project_notifications = current_user.notification_settings.for_projects
|
||||
end
|
||||
|
||||
def update
|
||||
type = params[:notification_type]
|
||||
|
||||
@saved = if type == 'global'
|
||||
current_user.update_attributes(user_params)
|
||||
elsif type == 'group'
|
||||
group_member = current_user.group_members.find(params[:notification_id])
|
||||
group_member.notification_level = params[:notification_level]
|
||||
group_member.save
|
||||
else
|
||||
project_member = current_user.project_members.find(params[:notification_id])
|
||||
project_member.notification_level = params[:notification_level]
|
||||
project_member.save
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
if @saved
|
||||
flash[:notice] = "Notification settings saved"
|
||||
else
|
||||
flash[:alert] = "Failed to save new settings"
|
||||
end
|
||||
|
||||
redirect_back_or_default(default: profile_notifications_path)
|
||||
end
|
||||
|
||||
format.js
|
||||
if current_user.update_attributes(user_params)
|
||||
flash[:notice] = "Notification settings saved"
|
||||
else
|
||||
flash[:alert] = "Failed to save new settings"
|
||||
end
|
||||
|
||||
redirect_back_or_default(default: profile_notifications_path)
|
||||
end
|
||||
|
||||
def user_params
|
||||
|
||||
@@ -3,7 +3,8 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
include IssuableActions
|
||||
|
||||
before_action :module_enabled
|
||||
before_action :issue, only: [:edit, :update, :show]
|
||||
before_action :issue,
|
||||
only: [:edit, :update, :show, :referenced_merge_requests, :related_branches]
|
||||
|
||||
# Allow read any issue
|
||||
before_action :authorize_read_issue!, only: [:show]
|
||||
@@ -17,9 +18,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
# Allow issues bulk update
|
||||
before_action :authorize_admin_issues!, only: [:bulk_update]
|
||||
|
||||
# Cross-reference merge requests
|
||||
before_action :closed_by_merge_requests, only: [:show]
|
||||
|
||||
respond_to :html
|
||||
|
||||
def index
|
||||
@@ -65,8 +63,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
@note = @project.notes.new(noteable: @issue)
|
||||
@notes = @issue.notes.nonawards.with_associations.fresh
|
||||
@noteable = @issue
|
||||
@merge_requests = @issue.referenced_merge_requests(current_user)
|
||||
@related_branches = @issue.related_branches - @merge_requests.map(&:source_branch)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
@@ -118,15 +114,39 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def referenced_merge_requests
|
||||
@merge_requests = @issue.referenced_merge_requests(current_user)
|
||||
@closed_by_merge_requests = @issue.closed_by_merge_requests(current_user)
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string('projects/issues/_merge_requests')
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def related_branches
|
||||
merge_requests = @issue.referenced_merge_requests(current_user)
|
||||
|
||||
@related_branches = @issue.related_branches -
|
||||
merge_requests.map(&:source_branch)
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string('projects/issues/_related_branches')
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def bulk_update
|
||||
result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute
|
||||
redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" })
|
||||
end
|
||||
|
||||
def closed_by_merge_requests
|
||||
@closed_by_merge_requests ||= @issue.closed_by_merge_requests(current_user)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def issue
|
||||
|
||||
@@ -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
|
||||
@@ -237,6 +237,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
status = "preparing" if status.nil?
|
||||
|
||||
response = {
|
||||
title: merge_request.title,
|
||||
sha: merge_request.last_commit_short_sha,
|
||||
|
||||
@@ -39,8 +39,7 @@ class Projects::NotesController < Projects::ApplicationController
|
||||
|
||||
def destroy
|
||||
if note.editable?
|
||||
note.destroy
|
||||
note.reset_events_cache
|
||||
Notes::DeleteService.new(project, current_user).execute(note)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
class Projects::NotificationSettingsController < Projects::ApplicationController
|
||||
before_action :authenticate_user!
|
||||
|
||||
def update
|
||||
notification_setting = current_user.notification_settings_for(project)
|
||||
saved = notification_setting.update_attributes(notification_setting_params)
|
||||
|
||||
render json: { saved: saved }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def notification_setting_params
|
||||
params.require(:notification_setting).permit(:level)
|
||||
end
|
||||
end
|
||||
@@ -11,7 +11,6 @@ class Projects::RepositoriesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def archive
|
||||
RepositoryArchiveCacheWorker.perform_async
|
||||
headers.store(*Gitlab::Workhorse.send_git_archive(@project, params[:ref], params[:format]))
|
||||
head :ok
|
||||
rescue => ex
|
||||
|
||||
@@ -101,14 +101,18 @@ class ProjectsController < Projects::ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
if current_user
|
||||
@membership = @project.team.find_member(current_user.id)
|
||||
|
||||
if @membership
|
||||
@notification_setting = current_user.notification_settings_for(@project)
|
||||
end
|
||||
end
|
||||
|
||||
if @project.repository_exists?
|
||||
if @project.empty_repo?
|
||||
render 'projects/empty'
|
||||
else
|
||||
if current_user
|
||||
@membership = @project.team.find_member(current_user.id)
|
||||
end
|
||||
|
||||
render :show
|
||||
end
|
||||
else
|
||||
|
||||
@@ -184,7 +184,7 @@ module ApplicationHelper
|
||||
element = content_tag :time, time.to_s,
|
||||
class: "#{html_class} js-timeago #{"js-timeago-pending" unless skip_js}",
|
||||
datetime: time.to_time.getutc.iso8601,
|
||||
title: time.in_time_zone.to_s(:medium),
|
||||
title: time.to_time.in_time_zone.to_s(:medium),
|
||||
data: { toggle: 'tooltip', placement: placement, container: 'body' }
|
||||
|
||||
unless skip_js
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
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
|
||||
|
||||
@@ -52,6 +52,7 @@ module IssuesHelper
|
||||
|
||||
def milestone_options(object)
|
||||
milestones = object.project.milestones.active.reorder(due_date: :asc, title: :asc).to_a
|
||||
milestones.unshift(object.milestone) if object.milestone.present? && object.milestone.closed?
|
||||
milestones.unshift(Milestone::None)
|
||||
|
||||
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
|
||||
@@ -115,17 +116,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(' '),
|
||||
emoji: name,
|
||||
unicode_name: unicode
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -3,8 +3,16 @@ module NamespacesHelper
|
||||
groups = current_user.owned_groups + current_user.masters_groups
|
||||
users = [current_user.namespace]
|
||||
|
||||
group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [display_path ? g.path : g.human_name, g.id]} ]
|
||||
users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [display_path ? u.path : u.human_name, u.id]} ]
|
||||
data_attr_group = { 'data-options-parent' => 'groups' }
|
||||
data_attr_users = { 'data-options-parent' => 'users' }
|
||||
|
||||
group_opts = [
|
||||
"Groups", groups.sort_by(&:human_name).map { |g| [display_path ? g.path : g.human_name, g.id, data_attr_group] }
|
||||
]
|
||||
|
||||
users_opts = [
|
||||
"Users", users.sort_by(&:human_name).map { |u| [display_path ? u.path : u.human_name, u.id, data_attr_users] }
|
||||
]
|
||||
|
||||
options = []
|
||||
options << group_opts
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
module NotificationsHelper
|
||||
include IconsHelper
|
||||
|
||||
def notification_icon(notification)
|
||||
if notification.disabled?
|
||||
icon('volume-off', class: 'ns-mute')
|
||||
elsif notification.participating?
|
||||
icon('volume-down', class: 'ns-part')
|
||||
elsif notification.watch?
|
||||
icon('volume-up', class: 'ns-watch')
|
||||
else
|
||||
icon('circle-o', class: 'ns-default')
|
||||
def notification_icon_class(level)
|
||||
case level.to_sym
|
||||
when :disabled
|
||||
'microphone-slash'
|
||||
when :participating
|
||||
'volume-up'
|
||||
when :watch
|
||||
'eye'
|
||||
when :mention
|
||||
'at'
|
||||
when :global
|
||||
'globe'
|
||||
end
|
||||
end
|
||||
|
||||
def notification_list_item(notification_level, user_membership)
|
||||
case notification_level
|
||||
when Notification::N_DISABLED
|
||||
update_notification_link(Notification::N_DISABLED, user_membership, 'Disabled', 'microphone-slash')
|
||||
when Notification::N_PARTICIPATING
|
||||
update_notification_link(Notification::N_PARTICIPATING, user_membership, 'Participate', 'volume-up')
|
||||
when Notification::N_WATCH
|
||||
update_notification_link(Notification::N_WATCH, user_membership, 'Watch', 'eye')
|
||||
when Notification::N_MENTION
|
||||
update_notification_link(Notification::N_MENTION, user_membership, 'On mention', 'at')
|
||||
when Notification::N_GLOBAL
|
||||
update_notification_link(Notification::N_GLOBAL, user_membership, 'Global', 'globe')
|
||||
def notification_icon(level, text = nil)
|
||||
icon("#{notification_icon_class(level)} fw", text: text)
|
||||
end
|
||||
|
||||
def notification_title(level)
|
||||
case level.to_sym
|
||||
when :participating
|
||||
'Participate'
|
||||
when :mention
|
||||
'On mention'
|
||||
else
|
||||
# do nothing
|
||||
level.to_s.titlecase
|
||||
end
|
||||
end
|
||||
|
||||
def update_notification_link(notification_level, user_membership, title, icon)
|
||||
content_tag(:li, class: active_level_for(user_membership, notification_level)) do
|
||||
link_to '#', class: 'update-notification', data: { notification_level: notification_level } do
|
||||
icon("#{icon} fw", text: title)
|
||||
def notification_list_item(level, setting)
|
||||
title = notification_title(level)
|
||||
|
||||
data = {
|
||||
notification_level: level,
|
||||
notification_title: title
|
||||
}
|
||||
|
||||
content_tag(:li, class: ('active' if setting.level == level)) do
|
||||
link_to '#', class: 'update-notification', data: data do
|
||||
notification_icon(level, title)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def notification_label(user_membership)
|
||||
Notification.new(user_membership).to_s
|
||||
end
|
||||
|
||||
def active_level_for(user_membership, level)
|
||||
'active' if user_membership.notification_level == level
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,21 +65,14 @@ module ProjectsHelper
|
||||
link_to(simple_sanitize(owner.name), user_path(owner))
|
||||
end
|
||||
|
||||
project_link = link_to project_path(project), { class: "project-item-select-holder" } do
|
||||
link_output = simple_sanitize(project.name)
|
||||
project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" }
|
||||
|
||||
if current_user
|
||||
link_output += project_select_tag :project_path,
|
||||
class: "project-item-select js-projects-dropdown",
|
||||
data: { include_groups: false, order_by: 'last_activity_at' }
|
||||
end
|
||||
|
||||
link_output
|
||||
if current_user
|
||||
project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" })
|
||||
end
|
||||
project_link += icon "chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle" if current_user
|
||||
|
||||
full_title = namespace_link + ' / ' + project_link
|
||||
full_title += ' · '.html_safe + link_to(simple_sanitize(name), url) if name
|
||||
full_title = "#{namespace_link} / #{project_link}".html_safe
|
||||
full_title << ' · '.html_safe << link_to(simple_sanitize(name), url) if name
|
||||
|
||||
full_title
|
||||
end
|
||||
|
||||
@@ -20,6 +20,8 @@ module TodosHelper
|
||||
end
|
||||
|
||||
def todo_target_path(todo)
|
||||
return unless todo.target.present?
|
||||
|
||||
anchor = dom_id(todo.note) if todo.note.present?
|
||||
|
||||
if todo.for_commit?
|
||||
|
||||
@@ -150,13 +150,11 @@ class Commit
|
||||
end
|
||||
|
||||
def hook_attrs(with_changed_files: false)
|
||||
path_with_namespace = project.path_with_namespace
|
||||
|
||||
data = {
|
||||
id: id,
|
||||
message: safe_message,
|
||||
timestamp: committed_date.xmlschema,
|
||||
url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{id}",
|
||||
url: Gitlab::UrlBuilder.build(self),
|
||||
author: {
|
||||
name: author_name,
|
||||
email: author_email
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# == Notifiable concern
|
||||
#
|
||||
# Contains notification functionality
|
||||
#
|
||||
module Notifiable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
validates :notification_level, inclusion: { in: Notification.project_notification_levels }, presence: true
|
||||
end
|
||||
|
||||
def notification
|
||||
@notification ||= Notification.new(self)
|
||||
end
|
||||
end
|
||||
@@ -27,6 +27,7 @@ class Group < Namespace
|
||||
has_many :users, through: :group_members
|
||||
has_many :project_group_links, dependent: :destroy
|
||||
has_many :shared_projects, through: :project_group_links, source: :project
|
||||
has_many :notification_settings, dependent: :destroy, as: :source
|
||||
|
||||
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
|
||||
validate :visibility_level_allowed_by_projects
|
||||
|
||||
+2
-2
@@ -106,7 +106,7 @@ class Issue < ActiveRecord::Base
|
||||
|
||||
def related_branches
|
||||
project.repository.branch_names.select do |branch|
|
||||
branch.end_with?("-#{iid}")
|
||||
branch =~ /\A#{iid}-(?!\d+-stable)/i
|
||||
end
|
||||
end
|
||||
|
||||
@@ -151,7 +151,7 @@ class Issue < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def to_branch_name
|
||||
"#{title.parameterize}-#{iid}"
|
||||
"#{iid}-#{title.parameterize}"
|
||||
end
|
||||
|
||||
def can_be_worked_on?(current_user)
|
||||
|
||||
+11
-1
@@ -19,7 +19,6 @@
|
||||
|
||||
class Member < ActiveRecord::Base
|
||||
include Sortable
|
||||
include Notifiable
|
||||
include Gitlab::Access
|
||||
|
||||
attr_accessor :raw_invite_token
|
||||
@@ -56,12 +55,15 @@ class Member < ActiveRecord::Base
|
||||
|
||||
before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? }
|
||||
after_create :send_invite, if: :invite?
|
||||
after_create :create_notification_setting, unless: :invite?
|
||||
after_create :post_create_hook, unless: :invite?
|
||||
after_update :post_update_hook, unless: :invite?
|
||||
after_destroy :post_destroy_hook, unless: :invite?
|
||||
|
||||
delegate :name, :username, :email, to: :user, prefix: true
|
||||
|
||||
default_value_for :notification_level, NotificationSetting.levels[:global]
|
||||
|
||||
class << self
|
||||
def find_by_invite_token(invite_token)
|
||||
invite_token = Devise.token_generator.digest(self, :invite_token, invite_token)
|
||||
@@ -160,6 +162,14 @@ class Member < ActiveRecord::Base
|
||||
send_invite
|
||||
end
|
||||
|
||||
def create_notification_setting
|
||||
user.notification_settings.find_or_create_for(source)
|
||||
end
|
||||
|
||||
def notification_setting
|
||||
@notification_setting ||= user.notification_settings_for(source)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_invite
|
||||
|
||||
@@ -24,7 +24,6 @@ class GroupMember < Member
|
||||
|
||||
# Make sure group member points only to group as it source
|
||||
default_value_for :source_type, SOURCE_TYPE
|
||||
default_value_for :notification_level, Notification::N_GLOBAL
|
||||
validates_format_of :source_type, with: /\ANamespace\z/
|
||||
default_scope { where(source_type: SOURCE_TYPE) }
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ class ProjectMember < Member
|
||||
|
||||
# Make sure project member points only to project as it source
|
||||
default_value_for :source_type, SOURCE_TYPE
|
||||
default_value_for :notification_level, Notification::N_GLOBAL
|
||||
validates_format_of :source_type, with: /\AProject\z/
|
||||
default_scope { where(source_type: SOURCE_TYPE) }
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ class MergeRequest < ActiveRecord::Base
|
||||
validates :target_project, presence: true
|
||||
validates :target_branch, presence: true
|
||||
validates :merge_user, presence: true, if: :merge_when_build_succeeds?
|
||||
validate :validate_branches
|
||||
validate :validate_branches, unless: :allow_broken
|
||||
validate :validate_fork
|
||||
|
||||
scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
|
||||
@@ -218,7 +218,7 @@ class MergeRequest < ActiveRecord::Base
|
||||
end
|
||||
|
||||
if opened? || reopened?
|
||||
similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.id).opened
|
||||
similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.try(:id)).opened
|
||||
similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
|
||||
if similar_mrs.any?
|
||||
errors.add :validate_branches,
|
||||
@@ -345,7 +345,7 @@ class MergeRequest < ActiveRecord::Base
|
||||
|
||||
def hook_attrs
|
||||
attrs = {
|
||||
source: source_project.hook_attrs,
|
||||
source: source_project.try(:hook_attrs),
|
||||
target: target_project.hook_attrs,
|
||||
last_commit: nil,
|
||||
work_in_progress: work_in_progress?
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
class Notification
|
||||
#
|
||||
# Notification levels
|
||||
#
|
||||
N_DISABLED = 0
|
||||
N_PARTICIPATING = 1
|
||||
N_WATCH = 2
|
||||
N_GLOBAL = 3
|
||||
N_MENTION = 4
|
||||
|
||||
attr_accessor :target
|
||||
|
||||
class << self
|
||||
def notification_levels
|
||||
[N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH]
|
||||
end
|
||||
|
||||
def options_with_labels
|
||||
{
|
||||
disabled: N_DISABLED,
|
||||
participating: N_PARTICIPATING,
|
||||
watch: N_WATCH,
|
||||
mention: N_MENTION,
|
||||
global: N_GLOBAL
|
||||
}
|
||||
end
|
||||
|
||||
def project_notification_levels
|
||||
[N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH, N_GLOBAL]
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(target)
|
||||
@target = target
|
||||
end
|
||||
|
||||
def disabled?
|
||||
target.notification_level == N_DISABLED
|
||||
end
|
||||
|
||||
def participating?
|
||||
target.notification_level == N_PARTICIPATING
|
||||
end
|
||||
|
||||
def watch?
|
||||
target.notification_level == N_WATCH
|
||||
end
|
||||
|
||||
def global?
|
||||
target.notification_level == N_GLOBAL
|
||||
end
|
||||
|
||||
def mention?
|
||||
target.notification_level == N_MENTION
|
||||
end
|
||||
|
||||
def level
|
||||
target.notification_level
|
||||
end
|
||||
|
||||
def to_s
|
||||
case level
|
||||
when N_DISABLED
|
||||
'Disabled'
|
||||
when N_PARTICIPATING
|
||||
'Participating'
|
||||
when N_WATCH
|
||||
'Watching'
|
||||
when N_MENTION
|
||||
'On mention'
|
||||
when N_GLOBAL
|
||||
'Global'
|
||||
else
|
||||
# do nothing
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
class NotificationSetting < ActiveRecord::Base
|
||||
enum level: { disabled: 0, participating: 1, watch: 2, global: 3, mention: 4 }
|
||||
|
||||
default_value_for :level, NotificationSetting.levels[:global]
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :source, polymorphic: true
|
||||
|
||||
validates :user, presence: true
|
||||
validates :source, presence: true
|
||||
validates :level, presence: true
|
||||
validates :user_id, uniqueness: { scope: [:source_type, :source_id],
|
||||
message: "already exists in source",
|
||||
allow_nil: true }
|
||||
|
||||
scope :for_groups, -> { where(source_type: 'Namespace') }
|
||||
scope :for_projects, -> { where(source_type: 'Project') }
|
||||
|
||||
def self.find_or_create_for(source)
|
||||
setting = find_or_initialize_by(source: source)
|
||||
|
||||
unless setting.persisted?
|
||||
setting.save
|
||||
end
|
||||
|
||||
setting
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: oauth_access_tokens
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# resource_owner_id :integer
|
||||
# application_id :integer
|
||||
# token :string not null
|
||||
# refresh_token :string
|
||||
# expires_in :integer
|
||||
# revoked_at :datetime
|
||||
# created_at :datetime not null
|
||||
# scopes :string
|
||||
#
|
||||
|
||||
class OauthAccessToken < ActiveRecord::Base
|
||||
belongs_to :resource_owner, class_name: 'User'
|
||||
belongs_to :application, class_name: 'Doorkeeper::Application'
|
||||
end
|
||||
@@ -154,6 +154,7 @@ class Project < ActiveRecord::Base
|
||||
has_many :project_group_links, dependent: :destroy
|
||||
has_many :invited_groups, through: :project_group_links, source: :group
|
||||
has_many :todos, dependent: :destroy
|
||||
has_many :notification_settings, dependent: :destroy, as: :source
|
||||
|
||||
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
|
||||
|
||||
@@ -388,9 +389,15 @@ class Project < ActiveRecord::Base
|
||||
|
||||
def add_import_job
|
||||
if forked?
|
||||
RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path)
|
||||
job_id = RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path)
|
||||
else
|
||||
RepositoryImportWorker.perform_async(self.id)
|
||||
job_id = RepositoryImportWorker.perform_async(self.id)
|
||||
end
|
||||
|
||||
if job_id
|
||||
Rails.logger.info "Import job started for #{path_with_namespace} with job ID #{job_id}"
|
||||
else
|
||||
Rails.logger.error "Import job failed to start for #{path_with_namespace}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -82,17 +82,17 @@ class BambooService < CiService
|
||||
end
|
||||
|
||||
def build_info(sha)
|
||||
url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}")
|
||||
url = URI.join(bamboo_url, "/rest/api/latest/result?label=#{sha}").to_s
|
||||
|
||||
if username.blank? && password.blank?
|
||||
@response = HTTParty.get(parsed_url.to_s, verify: false)
|
||||
@response = HTTParty.get(url, verify: false)
|
||||
else
|
||||
get_url = "#{url}&os_authType=basic"
|
||||
url << '&os_authType=basic'
|
||||
auth = {
|
||||
username: username,
|
||||
password: password,
|
||||
username: username,
|
||||
password: password
|
||||
}
|
||||
@response = HTTParty.get(get_url, verify: false, basic_auth: auth)
|
||||
@response = HTTParty.get(url, verify: false, basic_auth: auth)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -101,11 +101,11 @@ class BambooService < CiService
|
||||
|
||||
if @response.code != 200 || @response['results']['results']['size'] == '0'
|
||||
# If actual build link can't be determined, send user to build summary page.
|
||||
"#{bamboo_url}/browse/#{build_key}"
|
||||
URI.join(bamboo_url, "/browse/#{build_key}").to_s
|
||||
else
|
||||
# If actual build link is available, go to build result page.
|
||||
result_key = @response['results']['results']['result']['planResultKey']['key']
|
||||
"#{bamboo_url}/browse/#{result_key}"
|
||||
URI.join(bamboo_url, "/browse/#{result_key}").to_s
|
||||
end
|
||||
end
|
||||
|
||||
@@ -134,7 +134,7 @@ class BambooService < CiService
|
||||
return unless supported_events.include?(data[:object_kind])
|
||||
|
||||
# Bamboo requires a GET and does not take any data.
|
||||
self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}",
|
||||
verify: false)
|
||||
url = URI.join(bamboo_url, "/updateAndBuild.action?buildKey=#{build_key}").to_s
|
||||
self.class.get(url, verify: false)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,7 +23,7 @@ class BuildsEmailService < Service
|
||||
prop_accessor :recipients
|
||||
boolean_accessor :add_pusher
|
||||
boolean_accessor :notify_only_broken_builds
|
||||
validates :recipients, presence: true, if: :activated?
|
||||
validates :recipients, presence: true, if: ->(s) { s.activated? && !s.add_pusher? }
|
||||
|
||||
def initialize_properties
|
||||
if properties.nil?
|
||||
@@ -87,10 +87,14 @@ class BuildsEmailService < Service
|
||||
end
|
||||
|
||||
def all_recipients(data)
|
||||
all_recipients = recipients.split(',').compact.reject(&:blank?)
|
||||
all_recipients = []
|
||||
|
||||
unless recipients.blank?
|
||||
all_recipients += recipients.split(',').compact.reject(&:blank?)
|
||||
end
|
||||
|
||||
if add_pusher? && data[:user][:email]
|
||||
all_recipients << "#{data[:user][:email]}"
|
||||
all_recipients << data[:user][:email]
|
||||
end
|
||||
|
||||
all_recipients
|
||||
|
||||
@@ -85,13 +85,15 @@ class TeamcityService < CiService
|
||||
end
|
||||
|
||||
def build_info(sha)
|
||||
url = URI.parse("#{teamcity_url}/httpAuth/app/rest/builds/"\
|
||||
"branch:unspecified:any,number:#{sha}")
|
||||
url = URI.join(
|
||||
teamcity_url,
|
||||
"/httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}"
|
||||
).to_s
|
||||
auth = {
|
||||
username: username,
|
||||
password: password,
|
||||
password: password
|
||||
}
|
||||
@response = HTTParty.get("#{url}", verify: false, basic_auth: auth)
|
||||
@response = HTTParty.get(url, verify: false, basic_auth: auth)
|
||||
end
|
||||
|
||||
def build_page(sha, ref)
|
||||
@@ -100,12 +102,14 @@ class TeamcityService < CiService
|
||||
if @response.code != 200
|
||||
# If actual build link can't be determined,
|
||||
# send user to build summary page.
|
||||
"#{teamcity_url}/viewLog.html?buildTypeId=#{build_type}"
|
||||
URI.join(teamcity_url, "/viewLog.html?buildTypeId=#{build_type}").to_s
|
||||
else
|
||||
# If actual build link is available, go to build result page.
|
||||
built_id = @response['build']['id']
|
||||
"#{teamcity_url}/viewLog.html?buildId=#{built_id}"\
|
||||
"&buildTypeId=#{build_type}"
|
||||
URI.join(
|
||||
teamcity_url,
|
||||
"/viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}"
|
||||
).to_s
|
||||
end
|
||||
end
|
||||
|
||||
@@ -140,12 +144,13 @@ class TeamcityService < CiService
|
||||
|
||||
branch = Gitlab::Git.ref_name(data[:ref])
|
||||
|
||||
self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue",
|
||||
body: "<build branchName=\"#{branch}\">"\
|
||||
"<buildType id=\"#{build_type}\"/>"\
|
||||
'</build>',
|
||||
headers: { 'Content-type' => 'application/xml' },
|
||||
basic_auth: auth
|
||||
)
|
||||
self.class.post(
|
||||
URI.join(teamcity_url, '/httpAuth/app/rest/buildQueue').to_s,
|
||||
body: "<build branchName=\"#{branch}\">"\
|
||||
"<buildType id=\"#{build_type}\"/>"\
|
||||
'</build>',
|
||||
headers: { 'Content-type' => 'application/xml' },
|
||||
basic_auth: auth
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -253,6 +253,8 @@ class Repository
|
||||
# This ensures this particular cache is flushed after the first commit to a
|
||||
# new repository.
|
||||
expire_emptiness_caches if empty?
|
||||
expire_branch_count_cache
|
||||
expire_tag_count_cache
|
||||
end
|
||||
|
||||
def expire_branch_cache(branch_name = nil)
|
||||
@@ -896,9 +898,9 @@ class Repository
|
||||
end
|
||||
|
||||
def main_language
|
||||
unless empty?
|
||||
Linguist::Repository.new(rugged, rugged.head.target_id).language
|
||||
end
|
||||
return if empty? || rugged.head_unborn?
|
||||
|
||||
Linguist::Repository.new(rugged, rugged.head.target_id).language
|
||||
end
|
||||
|
||||
def avatar
|
||||
|
||||
+13
-5
@@ -143,6 +143,7 @@ class User < ActiveRecord::Base
|
||||
has_many :spam_logs, dependent: :destroy
|
||||
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
|
||||
has_many :todos, dependent: :destroy
|
||||
has_many :notification_settings, dependent: :destroy
|
||||
|
||||
#
|
||||
# Validations
|
||||
@@ -157,7 +158,7 @@ class User < ActiveRecord::Base
|
||||
presence: true,
|
||||
uniqueness: { case_sensitive: false }
|
||||
|
||||
validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
|
||||
validates :notification_level, presence: true
|
||||
validate :namespace_uniq, if: ->(user) { user.username_changed? }
|
||||
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
|
||||
validate :unique_email, if: ->(user) { user.email_changed? }
|
||||
@@ -190,6 +191,13 @@ class User < ActiveRecord::Base
|
||||
# Note: When adding an option, it MUST go on the end of the array.
|
||||
enum project_view: [:readme, :activity, :files]
|
||||
|
||||
# Notification level
|
||||
# Note: When adding an option, it MUST go on the end of the array.
|
||||
#
|
||||
# TODO: Add '_prefix: :notification' to enum when update to Rails 5. https://github.com/rails/rails/pull/19813
|
||||
# Because user.notification_disabled? is much better than user.disabled?
|
||||
enum notification_level: [:disabled, :participating, :watch, :global, :mention]
|
||||
|
||||
alias_attribute :private_token, :authentication_token
|
||||
|
||||
delegate :path, to: :namespace, allow_nil: true, prefix: true
|
||||
@@ -349,10 +357,6 @@ class User < ActiveRecord::Base
|
||||
"#{self.class.reference_prefix}#{username}"
|
||||
end
|
||||
|
||||
def notification
|
||||
@notification ||= Notification.new(self)
|
||||
end
|
||||
|
||||
def generate_password
|
||||
if self.force_random_password
|
||||
self.password = self.password_confirmation = Devise.friendly_token.first(8)
|
||||
@@ -827,6 +831,10 @@ class User < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def notification_settings_for(source)
|
||||
notification_settings.find_or_initialize_by(source: source)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def projects_union
|
||||
|
||||
@@ -3,7 +3,7 @@ module Issues
|
||||
|
||||
def hook_data(issue, action)
|
||||
issue_data = issue.to_hook_data(current_user)
|
||||
issue_url = Gitlab::UrlBuilder.new(:issue).build(issue.id)
|
||||
issue_url = Gitlab::UrlBuilder.build(issue)
|
||||
issue_data[:object_attributes].merge!(url: issue_url, action: action)
|
||||
issue_data
|
||||
end
|
||||
|
||||
@@ -20,8 +20,7 @@ module MergeRequests
|
||||
|
||||
def hook_data(merge_request, action)
|
||||
hook_data = merge_request.to_hook_data(current_user)
|
||||
merge_request_url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id)
|
||||
hook_data[:object_attributes][:url] = merge_request_url
|
||||
hook_data[:object_attributes][:url] = Gitlab::UrlBuilder.build(merge_request)
|
||||
hook_data[:object_attributes][:action] = action
|
||||
hook_data
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user