Commit bbda0586 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'master' into refactor/ci-config-move-job-entries

* master: (522 commits)
  Fix CI yaml example
  Align cancel and retry buttons
  Remove deploy to production button
  Fix a bug where the project's repository path was returned instead of the wiki path
  Don't fail to highlight when Rouge doesn't have a lexer
  Revert "Merge branch 'gl-dropdown-issuable-form' into 'master'"
  Update tests
  Don't fail when Ci::Pipeline doesn't have a project
  Don't fail when a LegacyDiffNote didn't store the right diff
  Update CHANGELOG
  Use cattr_accessor instead duplicating code on NoteOnDiff concern
  Fix mentioned users list on diff notes
  Don't ask Heather to review documentation MR's
  add project name and namespace to filename on project export
  navbar_icon was renamed to custom_icon in:
  use %(...) and %[...] in favor of %<...>
  Fix spec Don't attempt to disable statement timeout on a MySQL DB
  Disable statement timeout outside of transaction and during adding concurrent index
  Disable PostgreSQL statement timeout during migrations
  Add visibility icon
  ...
parents 17084d42 65352b5b
# Prefer single quotes
StringLiterals:
EnforcedStyle: single_quotes
Enabled: true
This diff is collapsed.
This diff is collapsed.
stage:
before:
- cp config/gitlab.teatro.yml config/gitlab.yml
- mkdir /apps/gitlab-satellites
- mkdir /apps/repositories
database:
- RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup
\ No newline at end of file
This diff is collapsed.
...@@ -145,7 +145,8 @@ might be edited to make them small and simple. ...@@ -145,7 +145,8 @@ might be edited to make them small and simple.
You are encouraged to use the template below for feature proposals. You are encouraged to use the template below for feature proposals.
``` ```
## Description including problem, use cases, benefits, and/or goals ## Description
Include problem, use cases, benefits, and/or goals
## Proposal ## Proposal
......
source "https://rubygems.org" source 'https://rubygems.org'
gem 'rails', '4.2.6' gem 'rails', '4.2.7'
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with # Responders respond_to and respond_with
...@@ -11,15 +11,15 @@ gem 'responders', '~> 2.0' ...@@ -11,15 +11,15 @@ gem 'responders', '~> 2.0'
gem 'sprockets', '~> 3.6.0' gem 'sprockets', '~> 3.6.0'
# Default values for AR models # Default values for AR models
gem "default_value_for", "~> 3.0.0" gem 'default_value_for', '~> 3.0.0'
# Supported DBs # Supported DBs
gem "mysql2", '~> 0.3.16', group: :mysql gem 'mysql2', '~> 0.3.16', group: :mysql
gem "pg", '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
# Authentication libraries # Authentication libraries
gem 'devise', '~> 4.0' gem 'devise', '~> 4.0'
gem 'doorkeeper', '~> 3.1' gem 'doorkeeper', '~> 4.0'
gem 'omniauth', '~> 1.3.1' gem 'omniauth', '~> 1.3.1'
gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6' gem 'omniauth-azure-oauth2', '~> 0.0.6'
...@@ -28,7 +28,7 @@ gem 'omniauth-cas3', '~> 1.1.2' ...@@ -28,7 +28,7 @@ gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-facebook', '~> 3.0.0' gem 'omniauth-facebook', '~> 3.0.0'
gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-google-oauth2', '~> 0.2.0' gem 'omniauth-google-oauth2', '~> 0.4.1'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-saml', '~> 1.6.0' gem 'omniauth-saml', '~> 1.6.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
...@@ -48,24 +48,24 @@ gem 'attr_encrypted', '~> 3.0.0' ...@@ -48,24 +48,24 @@ gem 'attr_encrypted', '~> 3.0.0'
gem 'u2f', '~> 0.2.1' gem 'u2f', '~> 0.2.1'
# Browser detection # Browser detection
gem "browser", '~> 2.2' gem 'browser', '~> 2.2'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '~> 10.2' gem 'gitlab_git', '~> 10.2'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes # GitLab fork with several improvements to original library. For full list of changes
# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master # see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap" gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
# Git Wiki # Git Wiki
# Required manually in config/initializers/gollum.rb to control load order # Required manually in config/initializers/gollum.rb to control load order
gem 'gollum-lib', '~> 4.1.0', require: false gem 'gollum-lib', '~> 4.2', require: false
gem 'gollum-rugged_adapter', '~> 0.4.2', require: false gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
# Language detection # Language detection
gem "github-linguist", "~> 4.7.0", require: "linguist" gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API # API
gem 'grape', '~> 0.13.0' gem 'grape', '~> 0.13.0'
...@@ -73,13 +73,13 @@ gem 'grape-entity', '~> 0.4.2' ...@@ -73,13 +73,13 @@ gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Pagination # Pagination
gem "kaminari", "~> 0.17.0" gem 'kaminari', '~> 0.17.0'
# HAML # HAML
gem 'hamlit', '~> 2.5' gem 'hamlit', '~> 2.5'
# Files attachments # Files attachments
gem "carrierwave", '~> 0.10.0' gem 'carrierwave', '~> 0.10.0'
# Drag and Drop UI # Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1' gem 'dropzonejs-rails', '~> 0.7.1'
...@@ -94,18 +94,18 @@ gem 'fog-openstack', '~> 0.1' ...@@ -94,18 +94,18 @@ gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1' gem 'fog-rackspace', '~> 0.1.1'
# for aws storage # for aws storage
gem "unf", '~> 0.1.4' gem 'unf', '~> 0.1.4'
# Authorization # Authorization
gem "six", '~> 0.2.0' gem 'six', '~> 0.2.0'
# Seed data # Seed data
gem "seed-fu", '~> 2.3.5' gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1' gem 'github-markup', '~> 1.4'
gem 'redcarpet', '~> 3.3.3' gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6' gem 'rdoc', '~>3.6'
...@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12' ...@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.11' gem 'rouge', '~> 2.0'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
...@@ -124,29 +124,29 @@ gem 'diffy', '~> 3.0.3' ...@@ -124,29 +124,29 @@ gem 'diffy', '~> 3.0.3'
# Application server # Application server
group :unicorn do group :unicorn do
gem "unicorn", '~> 4.9.0' gem 'unicorn', '~> 4.9.0'
gem 'unicorn-worker-killer', '~> 0.4.2' gem 'unicorn-worker-killer', '~> 0.4.2'
end end
# State machine # State machine
gem "state_machines-activerecord", '~> 0.4.0' gem 'state_machines-activerecord', '~> 0.4.0'
# Run events after state machine commits # Run events after state machine commits
gem 'after_commit_queue' gem 'after_commit_queue', '~> 1.3.0'
# Issue tags # Issue tags
gem 'acts-as-taggable-on', '~> 3.4' gem 'acts-as-taggable-on', '~> 3.4'
# Background jobs # Background jobs
gem 'sinatra', '~> 1.4.4', require: nil gem 'sinatra', '~> 1.4.4', require: false
gem 'sidekiq', '~> 4.0' gem 'sidekiq', '~> 4.0'
gem 'sidekiq-cron', '~> 0.4.0' gem 'sidekiq-cron', '~> 0.4.0'
gem 'redis-namespace' gem 'redis-namespace', '~> 1.5.2'
# HTTP requests # HTTP requests
gem "httparty", '~> 0.13.3' gem 'httparty', '~> 0.13.3'
# Colored output to console # Colored output to console
gem "rainbow", '~> 2.1.0' gem 'rainbow', '~> 2.1.0'
# GitLab settings # GitLab settings
gem 'settingslogic', '~> 2.0.9' gem 'settingslogic', '~> 2.0.9'
...@@ -156,7 +156,7 @@ gem 'settingslogic', '~> 2.0.9' ...@@ -156,7 +156,7 @@ gem 'settingslogic', '~> 2.0.9'
gem 'version_sorter', '~> 2.0.0' gem 'version_sorter', '~> 2.0.0'
# Cache # Cache
gem "redis-rails", '~> 4.0.0' gem 'redis-rails', '~> 4.0.0'
# Redis # Redis
gem 'redis', '~> 3.2' gem 'redis', '~> 3.2'
...@@ -169,13 +169,13 @@ gem 'tinder', '~> 1.10.0' ...@@ -169,13 +169,13 @@ gem 'tinder', '~> 1.10.0'
gem 'hipchat', '~> 1.5.0' gem 'hipchat', '~> 1.5.0'
# Flowdock integration # Flowdock integration
gem "gitlab-flowdock-git-hook", "~> 1.0.1" gem 'gitlab-flowdock-git-hook', '~> 1.0.1'
# Gemnasium integration # Gemnasium integration
gem "gemnasium-gitlab-service", "~> 0.2" gem 'gemnasium-gitlab-service', '~> 0.2'
# Slack integration # Slack integration
gem "slack-notifier", "~> 1.2.0" gem 'slack-notifier', '~> 1.2.0'
# Asana integration # Asana integration
gem 'asana', '~> 0.4.0' gem 'asana', '~> 0.4.0'
...@@ -187,20 +187,20 @@ gem 'ruby-fogbugz', '~> 0.2.1' ...@@ -187,20 +187,20 @@ gem 'ruby-fogbugz', '~> 0.2.1'
gem 'd3_rails', '~> 3.5.0' gem 'd3_rails', '~> 3.5.0'
# underscore-rails # underscore-rails
gem "underscore-rails", "~> 1.8.0" gem 'underscore-rails', '~> 1.8.0'
# Sanitize user input # Sanitize user input
gem "sanitize", '~> 2.0' gem 'sanitize', '~> 2.0'
gem 'babosa', '~> 1.0.2' gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input # Sanitizes SVG input
gem "loofah", "~> 2.0.3" gem 'loofah', '~> 2.0.3'
# Working with license # Working with license
gem 'licensee', '~> 8.0.0' gem 'licensee', '~> 8.0.0'
# Protect against bruteforcing # Protect against bruteforcing
gem "rack-attack", '~> 4.3.1' gem 'rack-attack', '~> 4.3.1'
# Ace editor # Ace editor
gem 'ace-rails-ap', '~> 4.0.2' gem 'ace-rails-ap', '~> 4.0.2'
...@@ -214,16 +214,16 @@ gem 'charlock_holmes', '~> 0.7.3' ...@@ -214,16 +214,16 @@ gem 'charlock_holmes', '~> 0.7.3'
# Parse duration # Parse duration
gem 'chronic_duration', '~> 0.10.6' gem 'chronic_duration', '~> 0.10.6'
gem "sass-rails", '~> 5.0.0' gem 'sass-rails', '~> 5.0.0'
gem "coffee-rails", '~> 4.1.0' gem 'coffee-rails', '~> 4.1.0'
gem "uglifier", '~> 2.7.2' gem 'uglifier', '~> 2.7.2'
gem 'turbolinks', '~> 2.5.0' gem 'turbolinks', '~> 2.5.0'
gem 'jquery-turbolinks', '~> 2.1.0' gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8' gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0' gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.6.1' gem 'font-awesome-rails', '~> 4.6.1'
gem 'gitlab_emoji', '~> 0.3.0' gem 'gemojione', '~> 2.6'
gem 'gon', '~> 6.0.1' gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2' gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.1.0' gem 'jquery-rails', '~> 4.1.0'
...@@ -247,13 +247,13 @@ group :metrics do ...@@ -247,13 +247,13 @@ group :metrics do
end end
group :development do group :development do
gem "foreman" gem 'foreman', '~> 0.78.0'
gem 'brakeman', '~> 3.3.0', require: false gem 'brakeman', '~> 3.3.0', require: false
gem 'letter_opener_web', '~> 1.3.0' gem 'letter_opener_web', '~> 1.3.0'
gem 'rerun', '~> 0.11.0' gem 'rerun', '~> 0.11.0'
gem 'bullet', require: false gem 'bullet', '~> 5.0.0', require: false
gem 'rblineprof', platform: :mri, require: false gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
gem 'web-console', '~> 2.0' gem 'web-console', '~> 2.0'
# Better errors handler # Better errors handler
...@@ -261,15 +261,15 @@ group :development do ...@@ -261,15 +261,15 @@ group :development do
gem 'binding_of_caller', '~> 0.7.2' gem 'binding_of_caller', '~> 0.7.2'
# Docs generator # Docs generator
gem "sdoc", '~> 0.3.20' gem 'sdoc', '~> 0.3.20'
# thin instead webrick # thin instead webrick
gem 'thin', '~> 1.7.0' gem 'thin', '~> 1.7.0'
end end
group :development, :test do group :development, :test do
gem 'byebug', platform: :mri gem 'byebug', '~> 8.2.1', platform: :mri
gem 'pry-rails' gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.2.0', require: false gem 'awesome_print', '~> 1.2.0', require: false
gem 'fuubar', '~> 2.0.0' gem 'fuubar', '~> 2.0.0'
...@@ -277,7 +277,7 @@ group :development, :test do ...@@ -277,7 +277,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.4.0' gem 'database_cleaner', '~> 1.4.0'
gem 'factory_girl_rails', '~> 4.6.0' gem 'factory_girl_rails', '~> 4.6.0'
gem 'rspec-rails', '~> 3.5.0' gem 'rspec-rails', '~> 3.5.0'
gem 'rspec-retry' gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1' gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2' gem 'spinach-rerun-reporter', '~> 0.0.2'
...@@ -299,18 +299,18 @@ group :development, :test do ...@@ -299,18 +299,18 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0' gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.40.0', require: false gem 'rubocop', '~> 0.41.2', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'simplecov', '~> 0.11.0', require: false gem 'simplecov', '~> 0.11.0', require: false
gem 'flog', require: false gem 'flog', '~> 4.3.2', require: false
gem 'flay', require: false gem 'flay', '~> 2.6.1', require: false
gem 'bundler-audit', require: false gem 'bundler-audit', '~> 0.5.0', require: false
gem 'benchmark-ips', require: false gem 'benchmark-ips', '~> 2.3.0', require: false
gem "license_finder", require: false gem 'license_finder', '~> 2.1.0', require: false
gem 'knapsack' gem 'knapsack', '~> 1.11.0'
end end
group :test do group :test do
...@@ -318,33 +318,33 @@ group :test do ...@@ -318,33 +318,33 @@ group :test do
gem 'email_spec', '~> 1.6.0' gem 'email_spec', '~> 1.6.0'
gem 'webmock', '~> 1.21.0' gem 'webmock', '~> 1.21.0'
gem 'test_after_commit', '~> 0.4.2' gem 'test_after_commit', '~> 0.4.2'
gem 'sham_rack' gem 'sham_rack', '~> 1.3.6'
end end
group :production do group :production do
gem "gitlab_meta", '7.0' gem 'gitlab_meta', '7.0'
end end
gem "newrelic_rpm", '~> 3.14' gem 'newrelic_rpm', '~> 3.14'
gem 'octokit', '~> 4.3.0' gem 'octokit', '~> 4.3.0'
gem "mail_room", "~> 0.8" gem 'mail_room', '~> 0.8'
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'
## CI ## CI
gem 'activerecord-session_store', '~> 1.0.0' gem 'activerecord-session_store', '~> 1.0.0'
gem "nested_form", '~> 0.3.2' gem 'nested_form', '~> 0.3.2'
# OAuth # OAuth
gem 'oauth2', '~> 1.0.0' gem 'oauth2', '~> 1.2.0'
# Soft deletion # Soft deletion
gem "paranoia", "~> 2.0" gem 'paranoia', '~> 2.0'
# Health check # Health check
gem 'health_check', '~> 1.5.1' gem 'health_check', '~> 2.1.0'
# System information # System information
gem 'vmstat', '~> 2.1.0' gem 'vmstat', '~> 2.1.0'
......
This diff is collapsed.
app/assets/images/emoji.png

257 KB | W: | H:

app/assets/images/emoji.png

1000 KB | W: | H:

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

673 KB | W: | H:

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

2.38 MB | W: | H:

app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
app/assets/images/emoji@2x.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
groupPath: "/api/:version/groups/:id.json" groupPath: "/api/:version/groups/:id.json"
namespacesPath: "/api/:version/namespaces.json" namespacesPath: "/api/:version/namespaces.json"
groupProjectsPath: "/api/:version/groups/:id/projects.json" groupProjectsPath: "/api/:version/groups/:id/projects.json"
projectsPath: "/api/:version/projects.json" projectsPath: "/api/:version/projects.json?simple=true"
labelsPath: "/api/:version/projects/:id/labels" labelsPath: "/api/:version/projects/:id/labels"
licensePath: "/api/:version/licenses/:key" licensePath: "/api/:version/licenses/:key"
gitignorePath: "/api/:version/gitignores/:key" gitignorePath: "/api/:version/gitignores/:key"
......
...@@ -47,15 +47,12 @@ ...@@ -47,15 +47,12 @@
#= require date.format #= require date.format
#= require_directory ./behaviors #= require_directory ./behaviors
#= require_directory ./blob #= require_directory ./blob
#= require_directory ./ci
#= require_directory ./commit #= require_directory ./commit
#= require_directory ./extensions #= require_directory ./extensions
#= require_directory ./lib/utils #= require_directory ./lib/utils
#= require_directory ./u2f #= require_directory ./u2f
#= require_directory . #= require_directory .
#= require fuzzaldrin-plus #= require fuzzaldrin-plus
#= require cropper
#= require u2f
window.slugify = (text) -> window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......
class @CiBuild class @Build
@interval: null @interval: null
@state: null @state: null
constructor: (@build_url, @build_status, @state) -> constructor: (@page_url, @build_url, @build_status, @state) ->
clearInterval(CiBuild.interval) clearInterval(Build.interval)
# Init breakpoint checker # Init breakpoint checker
@bp = Breakpoints.get() @bp = Breakpoints.get()
...@@ -40,8 +40,8 @@ class @CiBuild ...@@ -40,8 +40,8 @@ class @CiBuild
# Check for new build output if user still watching build page # Check for new build output if user still watching build page
# Only valid for runnig build when output changes during time # Only valid for runnig build when output changes during time
# #
CiBuild.interval = setInterval => Build.interval = setInterval =>
if window.location.href.split("#").first() is @build_url if window.location.href.split("#").first() is @page_url
@getBuildTrace() @getBuildTrace()
, 4000 , 4000
...@@ -57,7 +57,7 @@ class @CiBuild ...@@ -57,7 +57,7 @@ class @CiBuild
getBuildTrace: -> getBuildTrace: ->
$.ajax $.ajax
url: "#{@build_url}/trace.json?state=#{encodeURIComponent(@state)}" url: "#{@page_url}/trace.json?state=#{encodeURIComponent(@state)}"
dataType: "json" dataType: "json"
success: (log) => success: (log) =>
if log.state if log.state
...@@ -70,7 +70,7 @@ class @CiBuild ...@@ -70,7 +70,7 @@ class @CiBuild
$('.js-build-output').html log.html $('.js-build-output').html log.html
@checkAutoscroll() @checkAutoscroll()
else if log.status isnt @build_status else if log.status isnt @build_status
Turbolinks.visit @build_url Turbolinks.visit @page_url
checkAutoscroll: -> checkAutoscroll: ->
$("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state") $("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state")
......
#= require pager
#= require jquery_nested_form
#= require_tree .
$(document).on 'click', '.assign-all-runner', ->
$(this).replaceWith('<i class="fa fa-refresh fa-spin"></i> Assign in progress..')
window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
document.addEventListener("page:fetch", unbindEvents)
$(document).on 'click', '.badge-codes-toggle', ->
$('.badge-codes-block').toggleClass("hide")
return false
class @CompareAutocomplete
constructor: ->
@initDropdown()
initDropdown: ->
$('.js-compare-dropdown').each ->
$dropdown = $(@)
selected = $dropdown.data('selected')
$dropdown.glDropdown(
data: (term, callback) ->
$.ajax(
url: $dropdown.data('refs-url')
data:
ref: $dropdown.data('ref')
).done (refs) ->
callback(refs)
selectable: true
filterable: true
filterByText: true
fieldName: $dropdown.attr('name')
filterInput: 'input[type="text"]'
renderRow: (ref) ->
if ref.header?
$('<li />')
.addClass('dropdown-header')
.text(ref.header)
else
link = $('<a />')
.attr('href', '#')
.addClass(if ref is selected then 'is-active' else '')
.text(ref)
.attr('data-ref', escape(ref))
$('<li />')
.append(link)
id: (obj, $el) ->
$el.attr('data-ref')
toggleLabel: (obj, $el) ->
$el.text().trim()
)
class @Diff class @Diff
UNFOLD_COUNT = 20 UNFOLD_COUNT = 20
constructor: -> constructor: ->
$('.files .diff-file').singleFileDiff()
@filesCommentButton = $('.files .diff-file').filesCommentButton()
$(document).off('click', '.js-unfold') $(document).off('click', '.js-unfold')
$(document).on('click', '.js-unfold', (event) => $(document).on('click', '.js-unfold', (event) =>
target = $(event.target) target = $(event.target)
...@@ -36,7 +39,7 @@ class @Diff ...@@ -36,7 +39,7 @@ class @Diff
# see https://gitlab.com/gitlab-org/gitlab-ce/issues/707 # see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent: 1 indent: 1
$.get(link, params, (response) => $.get(link, params, (response) ->
target.parent().replaceWith(response) target.parent().replaceWith(response)
) )
) )
......
...@@ -127,17 +127,18 @@ class Dispatcher ...@@ -127,17 +127,18 @@ class Dispatcher
when 'groups' when 'groups'
new UsersSelect() new UsersSelect()
when 'projects' when 'projects'
new NamespaceSelect() new NamespaceSelects()
when 'dashboard', 'root' when 'dashboard', 'root'
shortcut_handler = new ShortcutsDashboardNavigation() shortcut_handler = new ShortcutsDashboardNavigation()
when 'profiles' when 'profiles'
new Profile()
new NotificationsForm() new NotificationsForm()
new NotificationsDropdown() new NotificationsDropdown()
when 'projects' when 'projects'
new Project() new Project()
new ProjectAvatar() new ProjectAvatar()
switch path[1] switch path[1]
when 'compare'
new CompareAutocomplete()
when 'edit' when 'edit'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ProjectNew() new ProjectNew()
......
...@@ -70,12 +70,12 @@ class @DropzoneInput ...@@ -70,12 +70,12 @@ class @DropzoneInput
pasteText response.link.markdown pasteText response.link.markdown
return return
error: (temp, errorMessage) -> error: (temp) ->
errorAlert = $(form).find('.error-alert') errorAlert = $(form).find('.error-alert')
checkIfMsgExists = errorAlert.children().length checkIfMsgExists = errorAlert.children().length
if checkIfMsgExists is 0 if checkIfMsgExists is 0
errorAlert.append divAlert errorAlert.append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage $(".div-dropzone-alert").append "#{btnAlert}Attaching the file failed."
return return
totaluploadprogress: (totalUploadProgress) -> totaluploadprogress: (totalUploadProgress) ->
......
class @FilesCommentButton
COMMENT_BUTTON_CLASS = '.add-diff-note'
COMMENT_BUTTON_TEMPLATE = _.template '<button name="button" type="submit" class="btn <%- COMMENT_BUTTON_CLASS %> js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>'
LINE_HOLDER_CLASS = '.line_holder'
LINE_NUMBER_CLASS = 'diff-line-num'
LINE_CONTENT_CLASS = 'line_content'
UNFOLDABLE_LINE_CLASS = 'js-unfold'
EMPTY_CELL_CLASS = 'empty-cell'
OLD_LINE_CLASS = 'old_line'
NEW_CLASS = 'new'
LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content"
TEXT_FILE_SELECTOR = '.text-file'
DEBOUNCE_TIMEOUT_DURATION = 100
constructor: (@filesContainerElement) ->
@VIEW_TYPE = $('input#view[type=hidden]').val()
debounce = _.debounce @render, DEBOUNCE_TIMEOUT_DURATION
$(document)
.on 'mouseover', LINE_COLUMN_CLASSES, debounce
.on 'mouseleave', LINE_COLUMN_CLASSES, @destroy
render: (e) =>
$currentTarget = $(e.currentTarget)
buttonParentElement = @getButtonParent $currentTarget
return unless @shouldRender e, buttonParentElement
textFileElement = @getTextFileElement $currentTarget
lineContentElement = @getLineContent $currentTarget
buttonParentElement.append @buildButton
noteableType: textFileElement.attr 'data-noteable-type'
noteableID: textFileElement.attr 'data-noteable-id'
commitID: textFileElement.attr 'data-commit-id'
noteType: lineContentElement.attr 'data-note-type'
position: lineContentElement.attr 'data-position'
lineType: lineContentElement.attr 'data-line-type'
discussionID: lineContentElement.attr 'data-discussion-id'
lineCode: lineContentElement.attr 'data-line-code'
return
destroy: (e) =>
return if @isMovingToSameType e
$(COMMENT_BUTTON_CLASS, @getButtonParent $(e.currentTarget)).remove()
return
buildButton: (buttonAttributes) ->
initializedButtonTemplate = COMMENT_BUTTON_TEMPLATE
COMMENT_BUTTON_CLASS: COMMENT_BUTTON_CLASS.substr 1
$(initializedButtonTemplate).attr
'data-noteable-type': buttonAttributes.noteableType
'data-noteable-id': buttonAttributes.noteableID
'data-commit-id': buttonAttributes.commitID
'data-note-type': buttonAttributes.noteType
'data-line-code': buttonAttributes.lineCode
'data-position': buttonAttributes.position
'data-discussion-id': buttonAttributes.discussionID
'data-line-type': buttonAttributes.lineType
getTextFileElement: (hoveredElement) ->
$(hoveredElement.closest TEXT_FILE_SELECTOR)
getLineContent: (hoveredElement) ->
return hoveredElement if hoveredElement.hasClass LINE_CONTENT_CLASS
$(".#{LINE_CONTENT_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
getButtonParent: (hoveredElement) ->
if @VIEW_TYPE is 'inline'
return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS
$(".#{OLD_LINE_CLASS}", hoveredElement.parent())
else
return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS
$(".#{LINE_NUMBER_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
diffTypeClass: (hoveredElement) ->
if hoveredElement.hasClass(NEW_CLASS) then '.new' else '.old'
isMovingToSameType: (e) ->
newButtonParent = @getButtonParent $(e.toElement)
return false unless newButtonParent
newButtonParent.is @getButtonParent $(e.currentTarget)
shouldRender: (e, buttonParentElement) ->
(not buttonParentElement.hasClass(EMPTY_CELL_CLASS) and \
not buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS) and \
$(COMMENT_BUTTON_CLASS, buttonParentElement).length is 0)
$.fn.filesCommentButton = ->
return unless this and @parent().data('can-create-note')?
@each ->
unless $.data this, 'filesCommentButton'
$.data this, 'filesCommentButton', new FilesCommentButton $(this)
class @Flash class @Flash
constructor: (message, type = 'alert')-> hideFlash = -> $(@).fadeOut()
@flash = $(".flash-container")
@flash.html("")
innerDiv = $('<div/>', constructor: (message, type = 'alert', parent = null)->
if parent
@flashContainer = parent.find('.flash-container')
else
@flashContainer = $('.flash-container-page')
@flashContainer.html('')
flash = $('<div/>',
class: "flash-#{type}" class: "flash-#{type}"
) )
innerDiv.appendTo(".flash-container") flash.on 'click', hideFlash
textDiv = $("<div/>", textDiv = $('<div/>',
class: "flash-text", class: 'flash-text',
text: message text: message
) )
textDiv.appendTo(innerDiv) textDiv.appendTo(flash)
if @flash.parent().hasClass('content-wrapper') if @flashContainer.parent().hasClass('content-wrapper')
textDiv.addClass('container-fluid container-limited') textDiv.addClass('container-fluid container-limited')
@flash.click -> $(@).fadeOut() flash.appendTo(@flashContainer)
@flash.show() @flashContainer.show()
pinTo: (selector) ->
@flash.detach().appendTo(selector)
...@@ -190,7 +190,7 @@ GitLab.GfmAutoComplete = ...@@ -190,7 +190,7 @@ GitLab.GfmAutoComplete =
callbacks: callbacks:
beforeSave: (merges) -> beforeSave: (merges) ->
sanitizeLabelTitle = (title)-> sanitizeLabelTitle = (title)->
if /\w+\s+\w+/g.test(title) if /[\w\?&]+\s+[\w\?&]+/g.test(title)
"\"#{sanitize(title)}\"" "\"#{sanitize(title)}\""
else else
sanitize(title) sanitize(title)
......
...@@ -56,6 +56,7 @@ class GitLabDropdownFilter ...@@ -56,6 +56,7 @@ class GitLabDropdownFilter
return BLUR_KEYCODES.indexOf(keyCode) >= 0 return BLUR_KEYCODES.indexOf(keyCode) >= 0
filter: (search_text) -> filter: (search_text) ->
@options.onFilter(search_text) if @options.onFilter
data = @options.data() data = @options.data()
if data? and not @options.filterByText if data? and not @options.filterByText
...@@ -195,6 +196,7 @@ class GitLabDropdown ...@@ -195,6 +196,7 @@ class GitLabDropdown
@filter = new GitLabDropdownFilter @filterInput, @filter = new GitLabDropdownFilter @filterInput,
filterInputBlur: @filterInputBlur filterInputBlur: @filterInputBlur
filterByText: @options.filterByText filterByText: @options.filterByText
onFilter: @options.onFilter
remote: @options.filterRemote remote: @options.filterRemote
query: @options.data query: @options.data
keys: searchFields keys: searchFields
...@@ -454,6 +456,8 @@ class GitLabDropdown ...@@ -454,6 +456,8 @@ class GitLabDropdown
rowClicked: (el) -> rowClicked: (el) ->
fieldName = @options.fieldName fieldName = @options.fieldName
isInput = $(@el).is('input')
if @renderedData if @renderedData
groupName = el.data('group') groupName = el.data('group')
if groupName if groupName
...@@ -464,10 +468,19 @@ class GitLabDropdown ...@@ -464,10 +468,19 @@ class GitLabDropdown
selectedObject = @renderedData[selectedIndex] selectedObject = @renderedData[selectedIndex]
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if isInput
field = $(@el)
else
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
if el.hasClass(ACTIVE_CLASS) if el.hasClass(ACTIVE_CLASS)
el.removeClass(ACTIVE_CLASS) el.removeClass(ACTIVE_CLASS)
field.remove()
if isInput
field.val('')
else
field.remove()
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
...@@ -488,7 +501,9 @@ class GitLabDropdown ...@@ -488,7 +501,9 @@ class GitLabDropdown
else else
if not @options.multiSelect or el.hasClass('dropdown-clear-active') if not @options.multiSelect or el.hasClass('dropdown-clear-active')
@dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS @dropdown.find(".#{ACTIVE_CLASS}").removeClass ACTIVE_CLASS
@dropdown.parent().find("input[name='#{fieldName}']").remove()
unless isInput
@dropdown.parent().find("input[name='#{fieldName}']").remove()
if !value? if !value?
field.remove() field.remove()
...@@ -503,7 +518,9 @@ class GitLabDropdown ...@@ -503,7 +518,9 @@ class GitLabDropdown
if !field.length and fieldName if !field.length and fieldName
@addInput(fieldName, value) @addInput(fieldName, value)
else else
field.val value field
.val value
.trigger 'change'
return selectedObject return selectedObject
...@@ -530,7 +547,7 @@ class GitLabDropdown ...@@ -530,7 +547,7 @@ class GitLabDropdown
if $el.length if $el.length
e.preventDefault() e.preventDefault()
e.stopImmediatePropagation() e.stopImmediatePropagation()
$(selector, @dropdown)[0].click() $el.first().trigger('click')
addArrowKeyEvent: -> addArrowKeyEvent: ->
ARROW_KEY_CODES = [38, 40] ARROW_KEY_CODES = [38, 40]
......
...@@ -11,11 +11,11 @@ issuable_created = false ...@@ -11,11 +11,11 @@ issuable_created = false
initTemplates: -> initTemplates: ->
Issuable.labelRow = _.template( Issuable.labelRow = _.template(
'<% _.each(labels, function(label){ %> '<% _.each(labels, function(label){ %>
<span class="label-row btn-group" role="group" aria-label="<%= _.escape(label.title) %>" style="color: <%= label.text_color %>;"> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;">
<a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%= label.color %>;" title="<%= _.escape(label.description) %>" data-container="body"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body">
<%= _.escape(label.title) %> <%- label.title %>
</a> </a>
<button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%= label.color %>;" data-label="<%= _.escape(label.title) %>"> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>">
<i class="fa fa-times"></i> <i class="fa fa-times"></i>
</button> </button>
</span> </span>
...@@ -32,13 +32,11 @@ issuable_created = false ...@@ -32,13 +32,11 @@ issuable_created = false
$search = $('#issue_search') $search = $('#issue_search')
$form = $('.js-filter-form') $form = $('.js-filter-form')
$input = $("input[name='#{$search.attr('name')}']", $form) $input = $("input[name='#{$search.attr('name')}']", $form)
if $input.length is 0 if $input.length is 0
$form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>" $form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>"
else else
$input.val $search.val() $input.val $search.val()
Issuable.filterResults $form if $search.val() isnt ''
Issuable.filterResults $form
, 500) , 500)
initLabelFilterRemove: -> initLabelFilterRemove: ->
......
...@@ -32,9 +32,9 @@ class @LabelsSelect ...@@ -32,9 +32,9 @@ class @LabelsSelect
if issueUpdateURL if issueUpdateURL
labelHTMLTemplate = _.template( labelHTMLTemplate = _.template(
'<% _.each(labels, function(label){ %> '<% _.each(labels, function(label){ %>
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%= _.escape(label.title) %>"> <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>">
<span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;"> <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;">
<%= _.escape(label.title) %> <%- label.title %>
</span> </span>
</a> </a>
<% }); %>' <% }); %>'
...@@ -261,7 +261,7 @@ class @LabelsSelect ...@@ -261,7 +261,7 @@ class @LabelsSelect
$a.attr('data-label-id', label.id) $a.attr('data-label-id', label.id)
$a.addClass(selectedClass.join(' ')) $a.addClass(selectedClass.join(' '))
.html("#{colorEl} #{_.escape(label.title)}") .html("#{colorEl} #{label.title}")
# Return generated html # Return generated html
$li.html($a).prop('outerHTML') $li.html($a).prop('outerHTML')
...@@ -288,7 +288,7 @@ class @LabelsSelect ...@@ -288,7 +288,7 @@ class @LabelsSelect
fieldName: $dropdown.data('field-name') fieldName: $dropdown.data('field-name')
id: (label) -> id: (label) ->
if $dropdown.hasClass("js-filter-submit") and not label.isAny? if $dropdown.hasClass("js-filter-submit") and not label.isAny?
_.escape label.title label.title
else else
label.id label.id
......
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
w.gl.utils.isInGroupsPage = -> w.gl.utils.isInGroupsPage = ->
return $('body').data('page').split(':')[0] is 'groups' return gl.utils.getPagePath() is 'groups'
w.gl.utils.isInProjectPage = -> w.gl.utils.isInProjectPage = ->
return $('body').data('page').split(':')[0] is 'projects' return gl.utils.getPagePath() is 'projects'
w.gl.utils.getProjectSlug = -> w.gl.utils.getProjectSlug = ->
...@@ -40,6 +40,9 @@ ...@@ -40,6 +40,9 @@
e.stopImmediatePropagation() e.stopImmediatePropagation()
return false return false
gl.utils.getPagePath = ->
return $('body').data('page').split(':')[0]
jQuery.timefor = (time, suffix, expiredLabel) -> jQuery.timefor = (time, suffix, expiredLabel) ->
......
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
w.gl ?= {} w.gl ?= {}
w.gl.utils ?= {} w.gl.utils ?= {}
w.gl.utils.days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
w.gl.utils.formatDate = (datetime) -> w.gl.utils.formatDate = (datetime) ->
dateFormat(datetime, 'mmm d, yyyy h:MMtt Z') dateFormat(datetime, 'mmm d, yyyy h:MMtt Z')
w.gl.utils.getDayName = (date) ->
this.days[date.getDay()]
w.gl.utils.localTimeAgo = ($timeagoEls, setTimeago = true) -> w.gl.utils.localTimeAgo = ($timeagoEls, setTimeago = true) ->
$timeagoEls.each( -> $timeagoEls.each( ->
$el = $(@) $el = $(@)
......
...@@ -49,8 +49,9 @@ ...@@ -49,8 +49,9 @@
insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}" insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}"
if document.queryCommandSupported('insertText') if document.queryCommandSupported('insertText')
document.execCommand 'insertText', false, insertText inserted = document.execCommand 'insertText', false, insertText
else
unless inserted
try try
document.execCommand("ms-beginUndoUnit") document.execCommand("ms-beginUndoUnit")
......
...@@ -153,17 +153,18 @@ class @MergeRequestTabs ...@@ -153,17 +153,18 @@ class @MergeRequestTabs
loadDiff: (source) -> loadDiff: (source) ->
return if @diffsLoaded return if @diffsLoaded
@_get @_get
url: "#{source}.json" + @_location.search url: "#{source}.json" + @_location.search
success: (data) => success: (data) =>
$('#diffs').html data.html $('#diffs').html data.html
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs')) gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'))
$('#diffs .js-syntax-highlight').syntaxHighlight() $('#diffs .js-syntax-highlight').syntaxHighlight()
$('#diffs .diff-file').singleFileDiff()
@expandViewContainer() if @diffViewType() is 'parallel' @expandViewContainer() if @diffViewType() is 'parallel'
@diffsLoaded = true @diffsLoaded = true
@scrollToElement("#diffs") @scrollToElement("#diffs")
@highlighSelectedLine() @highlighSelectedLine()
@filesCommentButton = $('.files .diff-file').filesCommentButton()
$(document) $(document)
.off 'click', '.diff-line-num a' .off 'click', '.diff-line-num a'
......
...@@ -24,14 +24,14 @@ class @MilestoneSelect ...@@ -24,14 +24,14 @@ class @MilestoneSelect
if issueUpdateURL if issueUpdateURL
milestoneLinkTemplate = _.template( milestoneLinkTemplate = _.template(
'<a href="/<%= namespace %>/<%= path %>/milestones/<%= iid %>" class="bold has-tooltip" data-container="body" title="<%= remaining %>"><%= _.escape(title) %></a>' '<a href="/<%- namespace %>/<%- path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'
) )
milestoneLinkNoneTemplate = '<span class="no-value">None</span>' milestoneLinkNoneTemplate = '<span class="no-value">None</span>'
collapsedSidebarLabelTemplate = _.template( collapsedSidebarLabelTemplate = _.template(
'<span class="has-tooltip" data-container="body" title="<%= remaining %>" data-placement="left"> '<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left">
<%= _.escape(title) %> <%- title %>
</span>' </span>'
) )
......
class @NamespaceSelect class @NamespaceSelect
constructor: -> constructor: (opts) ->
namespaceFormatResult = (namespace) -> {
markup = "<div class='namespace-result'>" @dropdown
markup += "<span class='namespace-kind'>" + namespace.kind + "</span>" } = opts
markup += "<span class='namespace-path'>" + namespace.path + "</span>"
markup += "</div>" showAny = true
markup fieldName = 'namespace_id'
formatSelection = (namespace) -> if @dropdown.attr 'data-field-name'
namespace.kind + ": " + namespace.path fieldName = @dropdown.data 'fieldName'
$('.ajax-namespace-select').each (i, select) -> if @dropdown.attr 'data-show-any'
$(select).select2 showAny = @dropdown.data 'showAny'
placeholder: "Search for namespace"
multiple: $(select).hasClass('multiselect') @dropdown.glDropdown(
minimumInputLength: 0 filterable: true
query: (query) -> selectable: true
Api.namespaces query.term, (namespaces) -> filterRemote: true
data = { results: namespaces } search:
query.callback(data) fields: ['path']
fieldName: fieldName
dropdownCssClass: "ajax-namespace-dropdown" toggleLabel: (selected) ->
formatResult: namespaceFormatResult return if not selected.id? then selected.text else "#{selected.kind}: #{selected.path}"
formatSelection: formatSelection data: (term, dataCallback) ->
Api.namespaces term, (namespaces) ->
if showAny
anyNamespace =
text: 'Any namespace'
id: null
namespaces.unshift(anyNamespace)
namespaces.splice 1, 0, 'divider'
dataCallback(namespaces)
text: (namespace) ->
return if not namespace.id? then namespace.text else "#{namespace.kind}: #{namespace.path}"
renderRow: @renderRow
clicked: @onSelectItem
)
onSelectItem: (item, el, e) =>
e.preventDefault()
class @NamespaceSelects
constructor: (opts = {}) ->
{
@$dropdowns = $('.js-namespace-select')
} = opts
@$dropdowns.each (i, dropdown) ->
$dropdown = $(dropdown)
new NamespaceSelect(
dropdown: $dropdown
)
...@@ -100,13 +100,40 @@ class @Notes ...@@ -100,13 +100,40 @@ class @Notes
$('.note .js-task-list-container').taskList('disable') $('.note .js-task-list-container').taskList('disable')
$(document).off 'tasklist:changed', '.note .js-task-list-container' $(document).off 'tasklist:changed', '.note .js-task-list-container'
keydownNoteText: (e) -> keydownNoteText: (e) =>
$this = $(this) return if isMetaKey e
if $this.val() is '' and e.which is 38 and not isMetaKey e
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last") $textarea = $(e.target)
if myLastNote.length
myLastNoteEditBtn = myLastNote.find('.js-note-edit') # Edit previous note when UP arrow is hit
myLastNoteEditBtn.trigger('click', [true, myLastNote]) switch e.which
when 38
return unless $textarea.val() is ''
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
if myLastNote.length
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
myLastNoteEditBtn.trigger('click', [true, myLastNote])
# Cancel creating diff note or editing any note when ESCAPE is hit
when 27
discussionNoteForm = $textarea.closest('.js-discussion-note-form')
if discussionNoteForm.length
if $textarea.val() isnt ''
return unless confirm('Are you sure you want to cancel creating this comment?')
@removeDiscussionNoteForm(discussionNoteForm)
return
editNote = $textarea.closest('.note')
if editNote.length
originalText = $textarea.closest('form').data('original-note')
newText = $textarea.val()
if originalText isnt newText
return unless confirm('Are you sure you want to cancel editing this comment?')
@removeNoteEditForm(editNote)
isMetaKey = (e) -> isMetaKey = (e) ->
(e.metaKey or e.ctrlKey or e.altKey or e.shiftKey) (e.metaKey or e.ctrlKey or e.altKey or e.shiftKey)
...@@ -167,8 +194,7 @@ class @Notes ...@@ -167,8 +194,7 @@ class @Notes
renderNote: (note) -> renderNote: (note) ->
unless note.valid unless note.valid
if note.award if note.award
flash = new Flash('You have already awarded this emoji!', 'alert') new Flash('You have already awarded this emoji!', 'alert')
flash.pinTo('.header-content')
return return
if note.award if note.award
...@@ -213,12 +239,16 @@ class @Notes ...@@ -213,12 +239,16 @@ class @Notes
@note_ids.push(note.id) @note_ids.push(note.id)
form = $("#new-discussion-note-form-#{note.discussion_id}") form = $("#new-discussion-note-form-#{note.discussion_id}")
if note.original_discussion_id? and form.length is 0
form = $("#new-discussion-note-form-#{note.original_discussion_id}")
row = form.closest("tr") row = form.closest("tr")
note_html = $(note.html) note_html = $(note.html)
note_html.syntaxHighlight() note_html.syntaxHighlight()
# is this the first note of discussion? # is this the first note of discussion?
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']") discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']")
if note.original_discussion_id? and discussionContainer.length is 0
discussionContainer = $(".notes[data-discussion-id='" + note.original_discussion_id + "']")
if discussionContainer.length is 0 if discussionContainer.length is 0
# insert the note and the reply button after the temp row # insert the note and the reply button after the temp row
row.after note.discussion_html row.after note.discussion_html
...@@ -291,8 +321,11 @@ class @Notes ...@@ -291,8 +321,11 @@ class @Notes
form.addClass "js-main-target-form" form.addClass "js-main-target-form"
form.find("#note_line_code").remove() form.find("#note_line_code").remove()
form.find("#note_position").remove()
form.find("#note_type").remove() form.find("#note_type").remove()
@parentTimeline = form.parents('.timeline')
### ###
General note form setup. General note form setup.
...@@ -308,10 +341,12 @@ class @Notes ...@@ -308,10 +341,12 @@ class @Notes
new Autosave textarea, [ new Autosave textarea, [
"Note" "Note"
form.find("#note_commit_id").val()
form.find("#note_line_code").val()
form.find("#note_noteable_type").val() form.find("#note_noteable_type").val()
form.find("#note_noteable_id").val() form.find("#note_noteable_id").val()
form.find("#note_commit_id").val()
form.find("#note_type").val()
form.find("#note_line_code").val()
form.find("#note_position").val()
] ]
### ###
...@@ -323,8 +358,7 @@ class @Notes ...@@ -323,8 +358,7 @@ class @Notes
@renderNote(note) @renderNote(note)
addNoteError: (xhr, note, status) => addNoteError: (xhr, note, status) =>
flash = new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert') new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', @parentTimeline)
flash.pinTo('.md-area')
### ###
Called in response to the new note form being submitted Called in response to the new note form being submitted
...@@ -401,9 +435,12 @@ class @Notes ...@@ -401,9 +435,12 @@ class @Notes
Hides edit form and restores the original note text to the editor textarea. Hides edit form and restores the original note text to the editor textarea.
### ###
cancelEdit: (e) -> cancelEdit: (e) =>
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(e.target).closest('.note')
@removeNoteEditForm(note)
removeNoteEditForm: (note) ->
form = note.find(".current-note-edit-form") form = note.find(".current-note-edit-form")
note.removeClass "is-editting" note.removeClass "is-editting"
form.removeClass("current-note-edit-form") form.removeClass("current-note-edit-form")
...@@ -482,10 +519,12 @@ class @Notes ...@@ -482,10 +519,12 @@ class @Notes
setupDiscussionNoteForm: (dataHolder, form) => setupDiscussionNoteForm: (dataHolder, form) =>
# setup note target # setup note target
form.attr 'id', "new-discussion-note-form-#{dataHolder.data("discussionId")}" form.attr 'id', "new-discussion-note-form-#{dataHolder.data("discussionId")}"
form.attr "data-line-code", dataHolder.data("lineCode")
form.find("#note_type").val dataHolder.data("noteType") form.find("#note_type").val dataHolder.data("noteType")
form.find("#line_type").val dataHolder.data("lineType") form.find("#line_type").val dataHolder.data("lineType")
form.find("#note_commit_id").val dataHolder.data("commitId") form.find("#note_commit_id").val dataHolder.data("commitId")
form.find("#note_line_code").val dataHolder.data("lineCode") form.find("#note_line_code").val dataHolder.data("lineCode")
form.find("#note_position").val dataHolder.attr("data-position")
form.find("#note_noteable_type").val dataHolder.data("noteableType") form.find("#note_noteable_type").val dataHolder.data("noteableType")
form.find("#note_noteable_id").val dataHolder.data("noteableId") form.find("#note_noteable_id").val dataHolder.data("noteableId")
form.find('.js-note-discard') form.find('.js-note-discard')
......
...@@ -78,3 +78,6 @@ $ -> ...@@ -78,3 +78,6 @@ $ ->
if comment && comment.length > 1 && $title.val() == '' if comment && comment.length > 1 && $title.val() == ''
$title.val(comment[1]).change() $title.val(comment[1]).change()
if gl.utils.getPagePath() == 'profiles'
new Profile()
...@@ -5,13 +5,12 @@ ...@@ -5,13 +5,12 @@
this.initPagination() this.initPagination()
initSearch: -> initSearch: ->
@timer = null projectsListFilter = $('.projects-list-filter')
$(".projects-list-filter").on('keyup', -> debounceFilter = _.debounce ProjectsList.filterResults, 500
clearTimeout(@timer) projectsListFilter.on 'keyup', (e) ->
@timer = setTimeout(ProjectsList.filterResults, 500) debounceFilter() if projectsListFilter.val() isnt ''
)
filterResults: => filterResults: ->
$('.projects-list-holder').fadeTo(250, 0.5) $('.projects-list-holder').fadeTo(250, 0.5)
form = null form = null
......
class @ProtectedBranchSelect
constructor: (currentProject) ->
$('.dropdown-footer').hide();
@dropdown = $('.js-protected-branch-select').glDropdown(
data: @getProtectedBranches
filterable: true
remote: false
search:
fields: ['title']
selectable: true
toggleLabel: (selected) -> if (selected and 'id' of selected) then selected.title else 'Protected Branch'
fieldName: 'protected_branch[name]'
text: (protected_branch) -> _.escape(protected_branch.title)
id: (protected_branch) -> _.escape(protected_branch.id)
onFilter: @toggleCreateNewButton
clicked: () -> $('.protect-branch-btn').attr('disabled', false)
)
$('.create-new-protected-branch').on 'click', (event) =>
# Refresh the dropdown's data, which ends up calling `getProtectedBranches`
@dropdown.data('glDropdown').remote.execute()
@dropdown.data('glDropdown').selectRowAtIndex(event, 0)
getProtectedBranches: (term, callback) =>
if @selectedBranch
callback(gon.open_branches.concat(@selectedBranch))
else
callback(gon.open_branches)
toggleCreateNewButton: (branchName) =>
@selectedBranch = { title: branchName, id: branchName, text: branchName }
if branchName is ''
$('.protected-branch-select-footer-list').addClass('hidden')
$('.dropdown-footer').hide();
else
$('.create-new-protected-branch').text("Create Protected Branch: #{branchName}")
$('.protected-branch-select-footer-list').removeClass('hidden')
$('.dropdown-footer').show();
...@@ -11,7 +11,8 @@ $ -> ...@@ -11,7 +11,8 @@ $ ->
dataType: "json" dataType: "json"
data: data:
id: id id: id
developers_can_push: checked protected_branch:
developers_can_push: checked
success: -> success: ->
row = $(e.target) row = $(e.target)
......
...@@ -2,9 +2,10 @@ class @Shortcuts ...@@ -2,9 +2,10 @@ class @Shortcuts
constructor: (skipResetBindings) -> constructor: (skipResetBindings) ->
@enabledHelp = [] @enabledHelp = []
Mousetrap.reset() if not skipResetBindings Mousetrap.reset() if not skipResetBindings
Mousetrap.bind('?', @onToggleHelp) Mousetrap.bind '?', @onToggleHelp
Mousetrap.bind('s', Shortcuts.focusSearch) Mousetrap.bind 's', Shortcuts.focusSearch
Mousetrap.bind(['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview) Mousetrap.bind 'f', (e) => @focusFilter e
Mousetrap.bind ['ctrl+shift+p', 'command+shift+p'], @toggleMarkdownPreview
Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL? Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL?
onToggleHelp: (e) => onToggleHelp: (e) =>
...@@ -32,10 +33,16 @@ class @Shortcuts ...@@ -32,10 +33,16 @@ class @Shortcuts
$('.js-more-help-button').remove() $('.js-more-help-button').remove()
) )
focusFilter: (e) ->
@filterInput ?= $('input[type=search]', '.nav-controls')
@filterInput.focus()
e.preventDefault()
@focusSearch: (e) -> @focusSearch: (e) ->
$('#search').focus() $('#search').focus()
e.preventDefault() e.preventDefault()
$(document).on 'click.more_help', '.js-more-help-button', (e) -> $(document).on 'click.more_help', '.js-more-help-button', (e) ->
$(@).remove() $(@).remove()
$('.hidden-shortcut').show() $('.hidden-shortcut').show()
......
class @SingleFileDiff
WRAPPER = '<div class="diff-content diff-wrap-lines"></div>'
LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'
ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'
COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. Click to expand it.</div>'
constructor: (@file) ->
@content = $('.diff-content', @file)
@diffForPath = @content.find('[data-diff-for-path]').data 'diff-for-path'
@isOpen = !@diffForPath
if @diffForPath
@collapsedContent = @content
@loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide()
@content = null
@collapsedContent.after(@loadingContent)
else
@collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide()
@content.after(@collapsedContent)
@collapsedContent.on 'click', @toggleDiff
$('.file-title > a', @file).on 'click', @toggleDiff
toggleDiff: (e) =>
@isOpen = !@isOpen
if not @isOpen and not @hasError
@content.hide()
@collapsedContent.show()
else if @content
@collapsedContent.hide()
@content.show()
else
@getContentHTML()
getContentHTML: ->
@collapsedContent.hide()
@loadingContent.show()
$.get @diffForPath, (data) =>
@loadingContent.hide()
if data.html
@content = $(data.html)
@content.syntaxHighlight()
else
@hasError = true
@content = $(ERROR_HTML)
@collapsedContent.after(@content)
return
$.fn.singleFileDiff = ->
return @each ->
if not $.data this, 'singleFileDiff'
$.data this, 'singleFileDiff', new SingleFileDiff this
...@@ -6,8 +6,20 @@ ...@@ -6,8 +6,20 @@
class @U2FAuthenticate class @U2FAuthenticate
constructor: (@container, u2fParams) -> constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id @appId = u2fParams.app_id
@challenges = u2fParams.challenges @challenge = u2fParams.challenge
@signRequests = u2fParams.sign_requests
# The U2F Javascript API v1.1 requires a single challenge, with
# _no challenges per-request_. The U2F Javascript API v1.0 requires a
# challenge per-request, which is done by copying the single challenge
# into every request.
#
# In either case, we don't need the per-request challenges that the server
# has generated, so we can remove them.
#
# Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
# This can be removed once we upgrade.
# https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
@signRequests = u2fParams.sign_requests.map (request) -> _(request).omit('challenge')
start: () => start: () =>
if U2FUtil.isU2FSupported() if U2FUtil.isU2FSupported()
...@@ -16,7 +28,7 @@ class @U2FAuthenticate ...@@ -16,7 +28,7 @@ class @U2FAuthenticate
@renderNotSupported() @renderNotSupported()
authenticate: () => authenticate: () =>
u2f.sign(@appId, @challenges, @signRequests, (response) => u2f.sign(@appId, @challenge, @signRequests, (response) =>
if response.errorCode if response.errorCode
error = new U2FError(response.errorCode) error = new U2FError(response.errorCode)
@renderError(error); @renderError(error);
......
class @U2FUtil
@isU2FSupported: ->
window.u2f
# Helper class for U2F (universal 2nd factor) device registration and authentication.
class @U2FUtil
@isU2FSupported: ->
if @testMode
true
else
gon.u2f.browser_supports_u2f
@enableTestMode: ->
@testMode = true
<% if Rails.env.test? %>
U2FUtil.enableTestMode();
<% end %>
...@@ -87,14 +87,15 @@ class @Calendar ...@@ -87,14 +87,15 @@ class @Calendar
.attr 'width', @daySize .attr 'width', @daySize
.attr 'height', @daySize .attr 'height', @daySize
.attr 'title', (stamp) => .attr 'title', (stamp) =>
date = new Date(stamp.date)
contribText = 'No contributions' contribText = 'No contributions'
if stamp.count > 0 if stamp.count > 0
contribText = "#{stamp.count} contribution#{if stamp.count > 1 then 's' else ''}" contribText = "#{stamp.count} contribution#{if stamp.count > 1 then 's' else ''}"
date = dateFormat(stamp.date, 'mmm d, yyyy') dateText = dateFormat(date, 'mmm d, yyyy')
"#{contribText}<br />#{date}" "#{contribText}<br />#{gl.utils.getDayName(date)} #{dateText}"
.attr 'class', 'user-contrib-cell js-tooltip' .attr 'class', 'user-contrib-cell js-tooltip'
.attr 'fill', (stamp) => .attr 'fill', (stamp) =>
if stamp.count isnt 0 if stamp.count isnt 0
......
...@@ -56,14 +56,18 @@ class @UsersSelect ...@@ -56,14 +56,18 @@ class @UsersSelect
username: '' username: ''
avatar: '' avatar: ''
$value.html(assigneeTemplate(user)) $value.html(assigneeTemplate(user))
$collapsedSidebar
.attr('title', user.name)
.tooltip('fixTitle')
$collapsedSidebar.html(collapsedAssigneeTemplate(user)) $collapsedSidebar.html(collapsedAssigneeTemplate(user))
collapsedAssigneeTemplate = _.template( collapsedAssigneeTemplate = _.template(
'<% if( avatar ) { %> '<% if( avatar ) { %>
<a class="author_link" href="/u/<%= username %>"> <a class="author_link" href="/u/<%- username %>">
<img width="24" class="avatar avatar-inline s24" alt="" src="<%= avatar %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>">
<span class="author">Toni Boehm</span>
</a> </a>
<% } else { %> <% } else { %>
<i class="fa fa-user"></i> <i class="fa fa-user"></i>
...@@ -72,13 +76,13 @@ class @UsersSelect ...@@ -72,13 +76,13 @@ class @UsersSelect
assigneeTemplate = _.template( assigneeTemplate = _.template(
'<% if (username) { %> '<% if (username) { %>
<a class="author_link bold" href="/u/<%= username %>"> <a class="author_link bold" href="/u/<%- username %>">
<% if( avatar ) { %> <% if( avatar ) { %>
<img width="32" class="avatar avatar-inline s32" alt="" src="<%= avatar %>"> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>">
<% } %> <% } %>
<span class="author"><%= name %></span> <span class="author"><%- name %></span>
<span class="username"> <span class="username">
@<%= username %> @<%- username %>
</span> </span>
</a> </a>
<% } else { %> <% } else { %>
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
} }
&.s16 { width: 16px; height: 16px; margin-right: 6px; } &.s16 { width: 16px; height: 16px; margin-right: 6px; }
&.s20 { width: 20px; height: 20px; margin-right: 7px; }
&.s24 { width: 24px; height: 24px; margin-right: 8px; } &.s24 { width: 24px; height: 24px; margin-right: 8px; }
&.s26 { width: 26px; height: 26px; margin-right: 8px; } &.s26 { width: 26px; height: 26px; margin-right: 8px; }
&.s32 { width: 32px; height: 32px; margin-right: 10px; } &.s32 { width: 32px; height: 32px; margin-right: 10px; }
......
.blank-state-welcome {
text-align: center;
border-bottom: 1px solid $border-color;
.blank-state-text {
margin-bottom: 0;
}
}
.blank-state { .blank-state {
padding-top: 20px; padding-top: 20px;
padding-bottom: 20px; padding-bottom: 20px;
...@@ -6,7 +15,18 @@ ...@@ -6,7 +15,18 @@
.blank-state-no-icon { .blank-state-no-icon {
padding-top: 40px; padding-top: 40px;
padding-bottom: 40px; padding-bottom: 40px;
}
.blank-state-icon {
padding-bottom: 20px;
color: $gray-darkest;
font-size: 56px;
path,
polygon {
fill: currentColor;
}
} }
.blank-state-title { .blank-state-title {
...@@ -20,4 +40,12 @@ ...@@ -20,4 +40,12 @@
margin-top: 0; margin-top: 0;
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
font-size: 15px; font-size: 15px;
> strong {
font-weight: 600;
}
}
.blank-state-welcome-title {
font-size: 24px;
} }
...@@ -16,6 +16,15 @@ ...@@ -16,6 +16,15 @@
font-weight: normal; font-weight: normal;
font-size: 16px; font-size: 16px;
line-height: 36px; line-height: 36px;
&.diff-collapsed {
padding: 5px;
cursor: pointer;
&:hover {
background-color: $row-hover;
}
}
} }
.row-content-block { .row-content-block {
......
...@@ -281,3 +281,21 @@ ...@@ -281,3 +281,21 @@
color: $gl-icon-color; color: $gl-icon-color;
} }
} }
.clone-dropdown-btn a {
color: $dropdown-link-color;
&:hover {
text-decoration: none;
}
}
.btn-static {
background-color: $background-color !important;
border: 1px solid lightgrey;
cursor: default;
&:active {
-moz-box-shadow: inset 0 0 0 white;
-webkit-box-shadow: inset 0 0 0 white;
box-shadow: inset 0 0 0 white;
}
}
...@@ -270,21 +270,6 @@ table { ...@@ -270,21 +270,6 @@ table {
} }
} }
.dashboard-intro-icon {
float: left;
text-align: center;
font-size: 32px;
color: #aaa;
width: 60px;
}
.dashboard-intro-text {
display: inline-block;
margin-left: -60px;
padding-left: 60px;
width: 100%;
}
.btn-sign-in { .btn-sign-in {
text-shadow: none; text-shadow: none;
......
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
.dropdown-menu, .dropdown-menu,
.dropdown-menu-nav { .dropdown-menu-nav {
display: block; display: block;
@media (max-width: $screen-xs-max) {
width: 100%;
}
} }
.dropdown-menu-toggle { .dropdown-menu-toggle {
...@@ -65,6 +68,10 @@ ...@@ -65,6 +68,10 @@
color: $dropdown-toggle-hover-icon-color; color: $dropdown-toggle-hover-icon-color;
} }
} }
&.large {
width: 200px;
}
} }
.dropdown-menu, .dropdown-menu,
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
} }
a:not(.btn) { a:not(.btn) {
color: $gl-dark-link-color; color: $gl-text-color;
} }
.left-options { .left-options {
......
.flash-container { .flash-container {
cursor: pointer; cursor: pointer;
margin: 0; margin: 0;
margin-bottom: $gl-padding;
font-size: 14px; font-size: 14px;
width: 100%;
z-index: 100; z-index: 100;
.flash-notice { .flash-notice {
...@@ -18,9 +18,27 @@ ...@@ -18,9 +18,27 @@
} }
.flash-notice, .flash-alert { .flash-notice, .flash-alert {
.container-fluid.flash-text { border-radius: $border-radius-default;
.container-fluid.container-limited.flash-text {
background: transparent; background: transparent;
} }
} }
&.flash-container-page {
margin-bottom: 0;
.flash-notice, .flash-alert {
border-radius: 0;
}
}
}
@media (max-width: $screen-md-min) {
ul.notes {
.flash-container.timeline-content {
margin-left: 0;
}
}
} }
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
.toggle-nav-collapse, .toggle-nav-collapse,
.pin-nav-btn { .pin-nav-btn {
color: $color-light; color: $color-light;
background: $color;
&:hover { &:hover {
color: $white-light; color: $white-light;
...@@ -20,17 +19,6 @@ ...@@ -20,17 +19,6 @@
.sidebar-wrapper { .sidebar-wrapper {
background: $color-darker; background: $color-darker;
.sidebar-user {
background: $color-darker;
color: $color-light;
&:hover {
background-color: $color-dark;
color: $white-light;
text-decoration: none;
}
}
} }
.nav-sidebar li { .nav-sidebar li {
......
...@@ -137,6 +137,15 @@ ul.content-list { ...@@ -137,6 +137,15 @@ ul.content-list {
padding-top: 1px; padding-top: 1px;
float: right; float: right;
> .control-text {
margin-right: $gl-padding-top;
line-height: 40px;
&:last-child {
margin-right: 0;
}
}
> .btn, > .btn,
> .btn-group { > .btn-group {
margin-right: $gl-padding-top; margin-right: $gl-padding-top;
...@@ -166,6 +175,12 @@ ul.content-list { ...@@ -166,6 +175,12 @@ ul.content-list {
.panel > .content-list > li { .panel > .content-list > li {
padding: $gl-padding-top $gl-padding; padding: $gl-padding-top $gl-padding;
&.commit {
@media (min-width: $screen-sm-min) {
padding-left: 46px + $gl-padding;
}
}
} }
ul.controls { ul.controls {
......
...@@ -77,10 +77,10 @@ ...@@ -77,10 +77,10 @@
&.sub-nav { &.sub-nav {
text-align: center; text-align: center;
background-color: $background-color; background-color: $dark-background-color;
.container-fluid { .container-fluid {
background-color: $background-color; background-color: $dark-background-color;
margin-bottom: 0; margin-bottom: 0;
} }
...@@ -134,6 +134,11 @@ ...@@ -134,6 +134,11 @@
margin-bottom: 0; margin-bottom: 0;
border-bottom: none; border-bottom: none;
&.wide {
width: 100%;
display: block;
}
li a { li a {
padding: 16px 10px 11px; padding: 16px 10px 11px;
} }
...@@ -164,6 +169,7 @@ ...@@ -164,6 +169,7 @@
> .btn { > .btn {
margin-right: $gl-padding-top; margin-right: $gl-padding-top;
display: inline-block; display: inline-block;
vertical-align: top;
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;
......
...@@ -3,6 +3,12 @@ ...@@ -3,6 +3,12 @@
padding-bottom: 25px; padding-bottom: 25px;
transition: padding $sidebar-transition-duration; transition: padding $sidebar-transition-duration;
&.page-sidebar-pinned {
.sidebar-wrapper {
@include box-shadow(none);
}
}
.sidebar-wrapper { .sidebar-wrapper {
position: fixed; position: fixed;
top: 0; top: 0;
...@@ -11,6 +17,7 @@ ...@@ -11,6 +17,7 @@
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
transition: width $sidebar-transition-duration; transition: width $sidebar-transition-duration;
@include box-shadow(2px 0 16px 0 $black-transparent);
} }
} }
...@@ -40,34 +47,14 @@ ...@@ -40,34 +47,14 @@
} }
} }
.sidebar-user {
padding: 15px;
position: absolute;
left: 0;
bottom: 0;
width: $sidebar_width;
overflow: hidden;
font-size: 16px;
line-height: 36px;
transition: width $sidebar-transition-duration, padding $sidebar-transition-duration;
@media (min-width: $sidebar-breakpoint) {
bottom: 50px;
}
}
.nav-sidebar { .nav-sidebar {
position: absolute; position: absolute;
top: 50px; top: 50px;
bottom: 65px; bottom: 0;
width: $sidebar_width; width: $sidebar_width;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
@media (min-width: $sidebar-breakpoint) {
bottom: 115px;
}
&.navbar-collapse { &.navbar-collapse {
padding: 0 !important; padding: 0 !important;
} }
...@@ -85,7 +72,7 @@ ...@@ -85,7 +72,7 @@
} }
a { a {
padding: 7px 15px 7px 12px; padding: 7px $gl-sidebar-padding;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 24px; line-height: 24px;
display: block; display: block;
...@@ -117,7 +104,7 @@ ...@@ -117,7 +104,7 @@
} }
} }
.toggle-nav-collapse { .sidebar-action-buttons {
width: $sidebar_width; width: $sidebar_width;
position: absolute; position: absolute;
top: 0; top: 0;
...@@ -126,12 +113,37 @@ ...@@ -126,12 +113,37 @@
padding: 5px 0; padding: 5px 0;
font-size: 18px; font-size: 18px;
line-height: 30px; line-height: 30px;
.toggle-nav-collapse {
left: 0;
}
.pin-nav-btn {
right: 0;
display: none;
@media (min-width: $sidebar-breakpoint) {
display: block;
}
.fa {
transition: transform .15s;
}
&.is-active {
.fa {
transform: rotate(90deg);
}
}
}
} }
.nav-header-btn { .nav-header-btn {
padding: 10px 5px; padding: 10px $gl-sidebar-padding;
color: inherit; color: inherit;
transition-duration: .3s; transition-duration: .3s;
position: absolute;
top: 0;
&:hover, &:hover,
&:focus { &:focus {
...@@ -140,30 +152,6 @@ ...@@ -140,30 +152,6 @@
} }
} }
.pin-nav-btn {
display: none;
position: absolute;
left: 0;
bottom: 0;
height: 50px;
width: $sidebar_width;
line-height: 30px;
@media (min-width: $sidebar-breakpoint) {
display: block;
}
.fa {
transition: transform .15s;
}
&.is-active {
.fa {
transform: rotate(90deg);
}
}
}
.page-sidebar-collapsed { .page-sidebar-collapsed {
padding-left: 0; padding-left: 0;
......
...@@ -12,10 +12,12 @@ $sidebar-breakpoint: 1024px; ...@@ -12,10 +12,12 @@ $sidebar-breakpoint: 1024px;
/* /*
* UI elements * UI elements
*/ */
$border-color: #e5e5e5; $border-color: #e5e5e5;
$focus-border-color: #3aabf0; $focus-border-color: #3aabf0;
$table-border-color: #f0f0f0; $table-border-color: #f0f0f0;
$background-color: #fafafa; $background-color: #fafafa;
$dark-background-color: #f7f7f7;
$table-text-gray: #8f8f8f;
/* /*
* Text * Text
...@@ -62,6 +64,7 @@ $gl-btn-padding: 10px; ...@@ -62,6 +64,7 @@ $gl-btn-padding: 10px;
$gl-input-padding: 10px; $gl-input-padding: 10px;
$gl-vert-padding: 6px; $gl-vert-padding: 6px;
$gl-padding-top: 10px; $gl-padding-top: 10px;
$gl-sidebar-padding: 22px;
/* /*
* Misc * Misc
...@@ -153,9 +156,6 @@ $warning-message-bg: #fbf2d9; ...@@ -153,9 +156,6 @@ $warning-message-bg: #fbf2d9;
$warning-message-color: #9e8e60; $warning-message-color: #9e8e60;
$warning-message-border: #f0e2bb; $warning-message-border: #f0e2bb;
/* header */
$light-grey-header: #faf9f9;
/* tanuki logo colors */ /* tanuki logo colors */
$tanuki-red: #e24329; $tanuki-red: #e24329;
$tanuki-orange: #fc6d26; $tanuki-orange: #fc6d26;
......
...@@ -71,3 +71,30 @@ ...@@ -71,3 +71,30 @@
@extend .broadcast-message; @extend .broadcast-message;
margin-bottom: 20px; margin-bottom: 20px;
} }
// Users List
.users-list {
.user-row {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.user-details {
flex: 1 1 auto;
}
.user-name {
display: inline-block;
font-weight: 600;
}
.dropdown {
.btn-block {
margin-bottom: 0;
line-height: inherit;
}
}
}
...@@ -83,14 +83,6 @@ ...@@ -83,14 +83,6 @@
} }
} }
table.builds {
.build-link {
a {
color: $gl-dark-link-color;
}
}
}
.build-trace { .build-trace {
background: $ci-output-bg; background: $ci-output-bg;
color: $ci-text-color; color: $ci-text-color;
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
font-size: 0; font-size: 0;
} }
.btn-transparent { .btn-clipboard, .btn-transparent {
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
} }
......
...@@ -434,13 +434,3 @@ ...@@ -434,13 +434,3 @@
} }
} }
} }
.discussion {
.diff-content {
.diff-line-num {
&:before {
content: attr(data-linenumber);
}
}
}
}
...@@ -40,6 +40,12 @@ ...@@ -40,6 +40,12 @@
} }
} }
.ldap-group-links {
.form-actions {
margin-bottom: $gl-padding;
}
}
.groups-cover-block { .groups-cover-block {
.container-fluid { .container-fluid {
position: relative; position: relative;
......
...@@ -63,8 +63,8 @@ form.edit-issue { ...@@ -63,8 +63,8 @@ form.edit-issue {
.merge-request, .merge-request,
.issue { .issue {
&.today { &.today {
background: #efe; background: #f8feef;
border-color: #cec; border-color: #e1e8d5;
} }
&.closed { &.closed {
......
...@@ -162,9 +162,15 @@ ...@@ -162,9 +162,15 @@
} }
.filtered-labels { .filtered-labels {
font-size: 0;
padding: 12px 16px;
.label-row { .label-row {
margin-top: 4px;
margin-bottom: 4px;
&:not(:last-child) { &:not(:last-child) {
margin-right: 5px; margin-right: 8px;
} }
} }
......
...@@ -73,11 +73,14 @@ ...@@ -73,11 +73,14 @@
color: #888; color: #888;
} }
&.ci-pending, &.ci-pending {
&.ci-running {
color: $gl-warning; color: $gl-warning;
} }
&.ci-running {
color: $blue-normal;
}
&.ci-failed, &.ci-failed,
&.ci-error { &.ci-error {
color: $gl-danger; color: $gl-danger;
...@@ -167,7 +170,8 @@ ...@@ -167,7 +170,8 @@
.commit { .commit {
margin: 0; margin: 0;
padding: 2px 0; padding-top: 2px;
padding-bottom: 2px;
list-style: none; list-style: none;
&:hover { &:hover {
background: none; background: none;
......
.pipelines { .pipelines {
.stage { .stage {
max-width: 100px; max-width: 80px;
width: 80px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.duration, .finished_at {
margin: 4px 0;
}
.commit-title { .commit-title {
margin: 0; margin: 0;
} }
...@@ -22,3 +19,156 @@ ...@@ -22,3 +19,156 @@
margin: 4px; margin: 4px;
} }
} }
.content-list {
&.pipelines,
&.builds-content-list {
width: 100%;
overflow: auto;
}
}
.table.builds {
min-width: 1100px;
tr {
th {
padding: 16px;
border: none;
}
}
tbody {
border-top-width: 1px;
}
.commit-link {
a:hover {
text-decoration: none;
}
}
.branch-commit {
.branch-name {
margin-left: 8px;
font-weight: bold;
max-width: 180px;
overflow: hidden;
display: inline-block;
white-space: nowrap;
vertical-align: top;
text-overflow: ellipsis;
}
svg {
margin: 0 6px;
height: 14px;
width: auto;
vertical-align: middle;
}
.commit-id {
color: $gl-link-color;
margin-right: 8px;
}
.commit-title {
margin-top: 4px;
max-width: 320px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.avatar {
margin-left: 0;
}
.label {
margin-right: 4px;
}
.label-container {
font-size: 0;
.label {
margin-top: 5px;
}
}
}
.duration,
.finished-at {
color: $table-text-gray;
margin: 4px 0;
.fa {
font-size: 12px;
}
svg {
height: 12px;
width: auto;
vertical-align: middle;
}
.fa,
svg {
margin-right: 5px;
}
}
.pipeline-actions {
.btn {
margin: 0;
color: $table-text-gray;
}
.cancel-retry-btns {
vertical-align: middle;
.btn:not(:first-child) {
margin-left: 8px;
}
}
.dropdown-toggle,
.dropdown-menu {
color: $table-text-gray;
.fa {
color: $table-text-gray;
margin-right: 6px;
font-size: 14px;
}
}
.btn-remove {
color: $white-light;
}
.btn-group {
&.open {
.btn-default {
background-color: $white-normal;
border-color: $border-white-normal;
}
}
}
}
.build-link {
a {
color: $gl-dark-link-color;
}
}
.btn-group.open .dropdown-toggle {
box-shadow: none;
}
}
...@@ -64,86 +64,49 @@ ...@@ -64,86 +64,49 @@
} }
.project-home-panel { .project-home-panel {
background: $white-light; padding-top: 24px;
text-align: left; padding-bottom: 24px;
padding: 24px 0;
.container-fluid { @media (min-width: $screen-sm-min) {
position: relative; border-bottom: 1px solid $border-color;
@media (min-width: $screen-lg-min) {
.row {
display: flex;
-ms-flex-align: center;
-webkit-align-items: center;
-webkit-box-align: center;
}
}
} }
.cover-controls { .project-avatar {
.project-settings-dropdown { float: none;
margin-left: 10px; margin-left: auto;
display: inline-block; margin-right: auto;
.dropdown-menu { &.identicon {
left: auto; border-radius: 50%;
width: auto;
right: 0;
max-width: 240px;
}
} }
} }
.cover-title { .project-title {
margin-bottom: 0; margin-top: 10px;
} margin-bottom: 10px;
font-size: 24px;
.project-image-container { font-weight: 400;
@include make-sm-column(1); line-height: 1;
max-width: 86px;
min-width: 86px;
padding-right: 0;
@media (max-width: $screen-md-max) {
padding-left: 0;
margin: 0 0 10px;
max-width: none;
min-width: none;
.avatar.s70 { .fa {
margin: auto; margin-left: 2px;
} font-size: 12px;
vertical-align: middle;
} }
} }
.project-info { .project-home-desc {
@include make-sm-column(10); margin-left: auto;
margin-right: auto;
margin-bottom: 15px;
max-width: 480px;
h1 { > p {
font-size: 24px; margin-bottom: 0;
font-weight: normal;
margin: 0;
} }
.project-home-desc {
p {
margin: 0;
}
}
}
.identicon {
float: left;
@include border-radius(50%);
}
.avatar {
float: none;
} }
.notifications-btn { .notifications-btn {
.fa-bell, .fa-bell,
.fa-spinner { .fa-spinner {
margin-right: 6px; margin-right: 6px;
...@@ -153,127 +116,106 @@ ...@@ -153,127 +116,106 @@
margin-left: 6px; margin-left: 6px;
} }
} }
}
.project-repo-buttons { .project-repo-buttons {
font-size: 0; font-size: 0;
.btn {
@include btn-gray;
padding: 3px 10px;
text-transform: none;
background-color: $background-color;
.fa { .btn {
color: $layout-link-gray; @include btn-gray;
} padding: 3px 10px;
.fa-caret-down { .fa {
margin-left: 3px; color: $layout-link-gray;
}
} }
form { .fa-caret-down {
margin-left: 10px; margin-left: 3px;
} }
}
.count-buttons { .project-repo-btn-group,
display: inline-block; .notification-dropdown {
vertical-align: top; margin-left: 10px;
margin-top: 16px; }
}
.project-clone-holder { .count-buttons {
display: inline-block; display: inline-block;
margin-top: 16px; vertical-align: top;
}
input { .project-clone-holder {
height: 29px; display: inline-block;
}
input {
height: 29px;
} }
}
.count-with-arrow { .count-with-arrow {
display: inline-block; display: inline-block;
position: relative; position: relative;
margin-left: 4px; margin-left: 4px;
.arrow {
&:before {
content: '';
display: inline-block;
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 0;
margin-top: -6px;
border-width: 7px 5px 7px 0;
border-right-color: #dce0e5;
}
&:after { .arrow {
content: ''; &:before {
position: absolute; content: '';
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 1px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
border-right-color: #fff;
}
}
.count {
@include btn-gray;
display: inline-block; display: inline-block;
background: white; position: absolute;
border-radius: 2px; width: 0;
border-width: 1px; height: 0;
border-color: transparent;
border-style: solid; border-style: solid;
font-size: 13px; top: 50%;
font-weight: 600; left: 0;
line-height: 13px; margin-top: -6px;
padding: $gl-vert-padding $gl-padding; border-width: 7px 5px 7px 0;
letter-spacing: .4px; border-right-color: #dce0e5;
padding: 7px 14px; pointer-events: none;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
background-image: none;
white-space: nowrap;
margin: 0 10px 0 4px;
a {
color: inherit;
}
&:hover {
background: #fff;
}
} }
}
}
.project-right-buttons {
position: absolute;
right: 16px;
bottom: 0;
@media (max-width: $screen-md-max) { &:after {
top: 0; content: '';
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 1px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
border-right-color: #fff;
pointer-events: none;
}
} }
} .count {
@include btn-gray;
@media (max-width: $screen-md-max) { display: inline-block;
text-align: center; background: white;
border-radius: 2px;
border-width: 1px;
border-style: solid;
font-size: 13px;
font-weight: 600;
line-height: 13px;
padding: $gl-vert-padding $gl-padding;
letter-spacing: .4px;
padding: 7px 14px;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
background-image: none;
white-space: nowrap;
margin: 0 10px 0 4px;
a {
color: inherit;
}
.project-info, &:hover {
.project-image-container { background: #fff;
width: 100%; }
} }
} }
} }
...@@ -398,59 +340,72 @@ a.deploy-project-label { ...@@ -398,59 +340,72 @@ a.deploy-project-label {
.project-import { .project-import {
.form-group { .form-group {
margin-bottom: 0; margin-bottom: 5px;
} }
.import-buttons { .import-buttons {
padding-left: 0; padding-left: 0;
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
-webkit-flex-wrap: wrap; -webkit-flex-wrap: wrap;
flex-wrap: wrap; flex-wrap: wrap;
.btn { .btn {
margin-right: 10px; margin: 0 10px 10px 0;
padding: 8px 12px; padding: 8px;
} }
&> div {
margin-bottom: 14px; > div {
padding-left: 0; padding-left: 0;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
.btn {
margin-right: 0;
}
} }
} }
} }
} }
.project-stats { .project-stats {
margin-top: $gl-padding;
margin-bottom: 0;
padding: 0;
background-color: $white-light;
font-size: 0; font-size: 0;
border-bottom: 1px solid $border-color;
ul.nav { .nav {
display: inline-block; padding-top: 12px;
padding-bottom: 12px;
} }
.nav li { .nav > li {
display: inline-block; display: inline-block;
margin: 16px 0;
margin-right: 16px; &:not(:last-child) {
margin-right: $gl-padding;
}
&.project-repo-buttons-right {
margin-top: 10px;
@media (min-width: $screen-md-min) {
float: right;
margin-top: 0;
}
}
} }
.nav > li > a { .nav > li > a {
padding: 0;
background-color: transparent; background-color: transparent;
padding: 5px 10px;
font-size: 15px; font-size: 15px;
line-height: 29px;
color: $notes-light-color; color: $notes-light-color;
}
li { &:hover,
display: inline; &:focus {
} color: darken($notes-light-color, 15%);
}
a {
float: left;
font-size: 17px;
} }
li.missing { li.missing {
...@@ -458,6 +413,8 @@ a.deploy-project-label { ...@@ -458,6 +413,8 @@ a.deploy-project-label {
border-radius: $border-radius-default; border-radius: $border-radius-default;
a { a {
padding-left: 10px;
padding-right: 10px;
color: $notes-light-color; color: $notes-light-color;
display: block; display: block;
} }
...@@ -466,10 +423,6 @@ a.deploy-project-label { ...@@ -466,10 +423,6 @@ a.deploy-project-label {
background-color: $gray-normal; background-color: $gray-normal;
} }
} }
&.row-content-block.second-block {
margin-top: 0;
}
} }
pre.light-well { pre.light-well {
...@@ -557,8 +510,32 @@ pre.light-well { ...@@ -557,8 +510,32 @@ pre.light-well {
} }
.project-last-commit { .project-last-commit {
@media (min-width: $screen-sm-min) {
margin-top: $gl-padding;
}
&.container-fluid {
padding-top: 12px;
padding-bottom: 12px;
background-color: $background-color;
border: 1px solid $border-color;
border-right-width: 0;
border-left-width: 0;
@media (min-width: $screen-sm-min) {
border-right-width: 1px;
border-left-width: 1px;
}
}
&.container-limited {
@media (min-width: 1281px) {
border-radius: $border-radius-base;
}
}
.ci-status { .ci-status {
margin-right: 16px; margin-right: $gl-padding;
} }
.commit-row-message { .commit-row-message {
...@@ -566,19 +543,12 @@ pre.light-well { ...@@ -566,19 +543,12 @@ pre.light-well {
} }
.commit_short_id { .commit_short_id {
margin: 0 5px; margin-right: 5px;
color: $gl-link-color; color: $gl-link-color;
font-weight: 600; font-weight: 600;
} }
.commit-author-link { .commit-author-link {
margin-left: 7px;
text-decoration: none;
.avatar {
float: none;
margin-right: 4px;
}
.commit-author-name { .commit-author-name {
font-weight: 600; font-weight: 600;
} }
...@@ -601,15 +571,10 @@ pre.light-well { ...@@ -601,15 +571,10 @@ pre.light-well {
} }
.git-clone-holder { .git-clone-holder {
width: 498px; width: 380px;
.btn-clipboard { .btn-clipboard {
border: 1px solid $border-color; border: 1px solid $border-color;
padding: 6px $gl-padding;
}
.project-home-dropdown + & {
margin-right: 45px;
} }
.clone-options { .clone-options {
...@@ -678,3 +643,9 @@ pre.light-well { ...@@ -678,3 +643,9 @@ pre.light-well {
width: 300px; width: 300px;
} }
} }
.compare-form-group {
.dropdown-menu {
width: 300px;
}
}
...@@ -185,7 +185,7 @@ ...@@ -185,7 +185,7 @@
padding-right: $gl-padding + 15px; padding-right: $gl-padding + 15px;
} }
.btn-search { .btn-search, .btn-new {
width: 100%; width: 100%;
margin-top: 5px; margin-top: 5px;
...@@ -208,7 +208,7 @@ ...@@ -208,7 +208,7 @@
margin-top: 5px; margin-top: 5px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
width: 160px; width: 180px;
margin-top: 0; margin-top: 0;
} }
} }
......
...@@ -32,11 +32,15 @@ ...@@ -32,11 +32,15 @@
border-color: $gl-gray; border-color: $gl-gray;
} }
&.ci-pending, &.ci-pending {
&.ci-running {
color: $gl-warning; color: $gl-warning;
border-color: $gl-warning; border-color: $gl-warning;
} }
&.ci-running {
color: $blue-normal;
border-color: $blue-normal;
}
} }
.ci-status-icon-success { .ci-status-icon-success {
...@@ -45,10 +49,12 @@ ...@@ -45,10 +49,12 @@
.ci-status-icon-failed { .ci-status-icon-failed {
color: $gl-danger; color: $gl-danger;
} }
.ci-status-icon-running,
.ci-status-icon-pending { .ci-status-icon-pending {
color: $gl-warning; color: $gl-warning;
} }
.ci-status-icon-running {
color: $blue-normal;
}
.ci-status-icon-canceled, .ci-status-icon-canceled,
.ci-status-icon-disabled, .ci-status-icon-disabled,
.ci-status-icon-not-found, .ci-status-icon-not-found,
......
...@@ -23,12 +23,11 @@ ...@@ -23,12 +23,11 @@
} }
&:hover { &:hover {
cursor: pointer;
td { td {
background-color: $row-hover; background-color: $row-hover;
border-top: 1px solid $row-hover-border; border-top: 1px solid $row-hover-border;
border-bottom: 1px solid $row-hover-border; border-bottom: 1px solid $row-hover-border;
cursor: pointer;
} }
} }
......
...@@ -87,6 +87,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -87,6 +87,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:version_check_enabled, :version_check_enabled,
:admin_notification_email, :admin_notification_email,
:user_oauth_applications, :user_oauth_applications,
:user_default_external,
:shared_runners_enabled, :shared_runners_enabled,
:shared_runners_text, :shared_runners_text,
:max_artifacts_size, :max_artifacts_size,
...@@ -110,6 +111,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -110,6 +111,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:send_user_confirmation_email, :send_user_confirmation_email,
:container_registry_token_expire_delay, :container_registry_token_expire_delay,
:repository_storage, :repository_storage,
:enabled_git_access_protocol,
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: [], import_sources: [],
disabled_oauth_sign_in_sources: [] disabled_oauth_sign_in_sources: []
......
...@@ -5,11 +5,12 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -5,11 +5,12 @@ class Admin::ProjectsController < Admin::ApplicationController
def index def index
@projects = Project.all @projects = Project.all
@projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present? @projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present?
@projects = @projects.where("projects.visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.with_push if params[:with_push].present? @projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present? @projects = @projects.abandoned if params[:abandoned].present?
@projects = @projects.where(last_repository_check_failed: true) if params[:last_repository_check_failed].present? @projects = @projects.where(last_repository_check_failed: true) if params[:last_repository_check_failed].present?
@projects = @projects.non_archived unless params[:with_archived].present? @projects = @projects.non_archived unless params[:archived].present?
@projects = @projects.personal(current_user) if params[:personal].present?
@projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]) @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
......
...@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base ...@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base
session[:skip_tfa] && session[:skip_tfa] > Time.current session[:skip_tfa] && session[:skip_tfa] > Time.current
end end
def browser_supports_u2f?
browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
end
def redirect_to_home_page_url? def redirect_to_home_page_url?
# If user is not signed-in and tries to access root_path - redirect him to landing page # If user is not signed-in and tries to access root_path - redirect him to landing page
# Don't redirect to the default URL to prevent endless redirections # Don't redirect to the default URL to prevent endless redirections
......
...@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor ...@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor
# Authenticate using the response from a U2F (universal 2nd factor) device # Authenticate using the response from a U2F (universal 2nd factor) device
def authenticate_with_two_factor_via_u2f(user) def authenticate_with_two_factor_via_u2f(user)
if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenges]) if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge])
# Remove any lingering user data from login # Remove any lingering user data from login
session.delete(:otp_user_id) session.delete(:otp_user_id)
session.delete(:challenges) session.delete(:challenges)
...@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor ...@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor
if key_handles.present? if key_handles.present?
sign_requests = u2f.authentication_requests(key_handles) sign_requests = u2f.authentication_requests(key_handles)
challenges = sign_requests.map(&:challenge) session[:challenge] ||= u2f.challenge
session[:challenges] = challenges gon.push(u2f: { challenge: session[:challenge], app_id: u2f_app_id,
gon.push(u2f: { challenges: challenges, app_id: u2f_app_id, sign_requests: sign_requests })
sign_requests: sign_requests,
browser_supports_u2f: browser_supports_u2f? })
end end
end end
end end
module DiffForPath
extend ActiveSupport::Concern
def render_diff_for_path(diffs, diff_refs, project)
diff_file = safe_diff_files(diffs, diff_refs: diff_refs, repository: project.repository).find do |diff|
diff.old_path == params[:old_path] && diff.new_path == params[:new_path]
end
return render_404 unless diff_file
diff_commit = commit_for_diff(diff_file)
blob = diff_file.blob(diff_commit)
@expand_all_diffs = true
locals = {
diff_file: diff_file,
diff_commit: diff_commit,
diff_refs: diff_refs,
blob: blob,
project: project
}
render json: { html: view_to_html_string('projects/diffs/_content', locals) }
end
end
class Dashboard::TodosController < Dashboard::ApplicationController class Dashboard::TodosController < Dashboard::ApplicationController
include TodosHelper
before_action :find_todos, only: [:index, :destroy_all] before_action :find_todos, only: [:index, :destroy_all]
def index def index
...@@ -13,7 +11,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -13,7 +11,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format| respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' } format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
format.js { head :ok } format.js { head :ok }
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } } format.json { render json: todos_counts }
end end
end end
...@@ -23,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -23,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format| respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' } format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
format.js { head :ok } format.js { head :ok }
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } } format.json { render json: todos_counts }
end end
end end
...@@ -36,4 +34,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -36,4 +34,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def find_todos def find_todos
@todos ||= TodosFinder.new(current_user, params).execute @todos ||= TodosFinder.new(current_user, params).execute
end end
def todos_counts
{
count: TodosFinder.new(current_user, state: :pending).execute.count,
done_count: TodosFinder.new(current_user, state: :done).execute.count
}
end
end end
...@@ -12,13 +12,12 @@ class HelpController < ApplicationController ...@@ -12,13 +12,12 @@ class HelpController < ApplicationController
end end
def show def show
@category = clean_path_info(path_params[:category]) @path = clean_path_info(path_params[:path])
@file = path_params[:file]
respond_to do |format| respond_to do |format|
format.any(:markdown, :md, :html) do format.any(:markdown, :md, :html) do
# Note: We are purposefully NOT using `Rails.root.join` # Note: We are purposefully NOT using `Rails.root.join`
path = File.join(Rails.root, 'doc', @category, "#{@file}.md") path = File.join(Rails.root, 'doc', "#{@path}.md")
if File.exist?(path) if File.exist?(path)
@markdown = File.read(path) @markdown = File.read(path)
...@@ -33,7 +32,7 @@ class HelpController < ApplicationController ...@@ -33,7 +32,7 @@ class HelpController < ApplicationController
# Allow access to images in the doc folder # Allow access to images in the doc folder
format.any(:png, :gif, :jpeg) do format.any(:png, :gif, :jpeg) do
# Note: We are purposefully NOT using `Rails.root.join` # Note: We are purposefully NOT using `Rails.root.join`
path = File.join(Rails.root, 'doc', @category, "#{@file}.#{params[:format]}") path = File.join(Rails.root, 'doc', "#{@path}.#{params[:format]}")
if File.exist?(path) if File.exist?(path)
send_file(path, disposition: 'inline') send_file(path, disposition: 'inline')
...@@ -57,8 +56,7 @@ class HelpController < ApplicationController ...@@ -57,8 +56,7 @@ class HelpController < ApplicationController
private private
def path_params def path_params
params.require(:category) params.require(:path)
params.require(:file)
params params
end end
......
...@@ -27,10 +27,7 @@ class Import::GitlabProjectsController < Import::BaseController ...@@ -27,10 +27,7 @@ class Import::GitlabProjectsController < Import::BaseController
notice: "Project '#{@project.name}' is being imported." notice: "Project '#{@project.name}' is being imported."
) )
else else
redirect_to( redirect_back_or_default(options: { alert: "Project could not be imported: #{@project.errors.full_messages.join(', ')}" })
new_import_gitlab_project_path,
alert: "Project could not be imported: #{@project.errors.full_messages.join(', ')}"
)
end end
end end
......
...@@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Only allow properly saved users to login. # Only allow properly saved users to login.
if @user.persisted? && @user.valid? if @user.persisted? && @user.valid?
log_audit_event(@user, with: oauth['provider']) log_audit_event(@user, with: oauth['provider'])
sign_in_and_redirect(@user) if @user.two_factor_enabled?
prompt_for_two_factor(@user)
else
sign_in_and_redirect(@user)
end
else else
error_message = @user.errors.full_messages.to_sentence error_message = @user.errors.full_messages.to_sentence
......
...@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id, gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id,
register_requests: registration_requests, register_requests: registration_requests,
sign_requests: sign_requests, sign_requests: sign_requests })
browser_supports_u2f: browser_supports_u2f? })
end end
end end
...@@ -23,10 +23,9 @@ class Projects::ArtifactsController < Projects::ApplicationController ...@@ -23,10 +23,9 @@ class Projects::ArtifactsController < Projects::ApplicationController
entry = build.artifacts_metadata_entry(params[:path]) entry = build.artifacts_metadata_entry(params[:path])
if entry.exists? if entry.exists?
render json: { archive: build.artifacts_file.path, send_artifacts_entry(build, entry)
entry: Base64.encode64(entry.path) }
else else
render json: {}, status: 404 render_404
end end
end end
......
...@@ -57,7 +57,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -57,7 +57,7 @@ class Projects::BlobController < Projects::ApplicationController
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', include_diff_info: true) diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', include_diff_info: true)
diff_lines = diffy.diff.scan(/.*\n/)[2..-1] diff_lines = diffy.diff.scan(/.*\n/)[2..-1]
diff_lines = Gitlab::Diff::Parser.new.parse(diff_lines) diff_lines = Gitlab::Diff::Parser.new.parse(diff_lines)
@diff_lines = Gitlab::Diff::Highlight.new(diff_lines).highlight @diff_lines = Gitlab::Diff::Highlight.new(diff_lines, repository: @repository).highlight
render layout: false render layout: false
end end
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# Not to be confused with CommitsController, plural. # Not to be confused with CommitsController, plural.
class Projects::CommitController < Projects::ApplicationController class Projects::CommitController < Projects::ApplicationController
include CreatesCommit include CreatesCommit
include DiffForPath
include DiffHelper include DiffHelper
# Authorize # Authorize
...@@ -11,29 +12,14 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -11,29 +12,14 @@ class Projects::CommitController < Projects::ApplicationController
before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds] before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
before_action :authorize_read_commit_status!, only: [:builds] before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit before_action :commit
before_action :define_show_vars, only: [:show, :builds] before_action :define_commit_vars, only: [:show, :diff_for_path, :builds]
before_action :define_status_vars, only: [:show, :builds]
before_action :define_note_vars, only: [:show, :diff_for_path]
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick] before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
def show def show
apply_diff_view_cookie! apply_diff_view_cookie!
@grouped_diff_notes = commit.notes.grouped_diff_notes
@notes = commit.notes.non_diff_notes.fresh
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten + @notes,
@project,
current_user,
)
@note = @project.build_commit_note(commit)
@noteable = @commit
@comments_target = {
noteable_type: 'Commit',
commit_id: @commit.id
}
respond_to do |format| respond_to do |format|
format.html format.html
format.diff { render text: @commit.to_diff } format.diff { render text: @commit.to_diff }
...@@ -41,6 +27,10 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -41,6 +27,10 @@ class Projects::CommitController < Projects::ApplicationController
end end
end end
def diff_for_path
render_diff_for_path(@diffs, @commit.diff_refs, @project)
end
def builds def builds
end end
...@@ -114,16 +104,36 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -114,16 +104,36 @@ class Projects::CommitController < Projects::ApplicationController
@ci_builds ||= Ci::Build.where(pipeline: pipelines) @ci_builds ||= Ci::Build.where(pipeline: pipelines)
end end
def define_show_vars def define_commit_vars
return git_not_found! unless commit return git_not_found! unless commit
opts = diff_options opts = diff_options
opts[:ignore_whitespace_change] = true if params[:format] == 'diff' opts[:ignore_whitespace_change] = true if params[:format] == 'diff'
@diffs = commit.diffs(opts) @diffs = commit.diffs(opts)
@diff_refs = [commit.parent || commit, commit]
@notes_count = commit.notes.count @notes_count = commit.notes.count
end
def define_note_vars
@grouped_diff_notes = commit.notes.grouped_diff_notes
@notes = commit.notes.non_diff_notes.fresh
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten + @notes,
@project,
current_user,
)
@note = @project.build_commit_note(commit)
@noteable = @commit
@comments_target = {
noteable_type: 'Commit',
commit_id: @commit.id
}
end
def define_status_vars
@statuses = CommitStatus.where(pipeline: pipelines) @statuses = CommitStatus.where(pipeline: pipelines)
@builds = Ci::Build.where(pipeline: pipelines) @builds = Ci::Build.where(pipeline: pipelines)
end end
......
require 'addressable/uri' require 'addressable/uri'
class Projects::CompareController < Projects::ApplicationController class Projects::CompareController < Projects::ApplicationController
include DiffForPath
include DiffHelper include DiffHelper
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :assign_ref_vars, only: [:index, :show] before_action :define_ref_vars, only: [:index, :show, :diff_for_path]
before_action :define_diff_vars, only: [:show, :diff_for_path]
before_action :merge_request, only: [:index, :show] before_action :merge_request, only: [:index, :show]
def index def index
end end
def show def show
compare = CompareService.new. end
execute(@project, @head_ref, @project, @base_ref, diff_options)
if compare def diff_for_path
@commits = Commit.decorate(compare.commits, @project) return render_404 unless @compare
@commit = @project.commit(@head_ref)
@base_commit = @project.merge_base_commit(@base_ref, @head_ref) render_diff_for_path(@diffs, @diff_refs, @project)
@diffs = compare.diffs(diff_options)
@diff_refs = [@base_commit, @commit]
@diff_notes_disabled = true
@grouped_diff_notes = {}
end
end end
def create def create
...@@ -34,13 +30,35 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -34,13 +30,35 @@ class Projects::CompareController < Projects::ApplicationController
private private
def assign_ref_vars def define_ref_vars
@base_ref = Addressable::URI.unescape(params[:from]) @start_ref = Addressable::URI.unescape(params[:from])
@ref = @head_ref = Addressable::URI.unescape(params[:to]) @ref = @head_ref = Addressable::URI.unescape(params[:to])
end end
def define_diff_vars
@compare = CompareService.new.execute(@project, @head_ref, @project, @start_ref)
if @compare
@commits = Commit.decorate(@compare.commits, @project)
@start_commit = @project.commit(@start_ref)
@commit = @project.commit(@head_ref)
@base_commit = @project.merge_base_commit(@start_ref, @head_ref)
@diffs = @compare.diffs(diff_options)
@diff_refs = Gitlab::Diff::DiffRefs.new(
base_sha: @base_commit.try(:sha),
start_sha: @start_commit.try(:sha),
head_sha: @commit.try(:sha)
)
@diff_notes_disabled = true
@grouped_diff_notes = {}
end
end
def merge_request def merge_request
@merge_request ||= @project.merge_requests.opened. @merge_request ||= @project.merge_requests.opened.
find_by(source_project: @project, source_branch: @head_ref, target_branch: @base_ref) find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
end end
end end
...@@ -19,6 +19,8 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -19,6 +19,8 @@ class Projects::GitHttpController < Projects::ApplicationController
render_ok render_ok
elsif receive_pack? && receive_pack_allowed? elsif receive_pack? && receive_pack_allowed?
render_ok render_ok
elsif http_blocked?
render_not_allowed
else else
render_not_found render_not_found
end end
...@@ -154,6 +156,10 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -154,6 +156,10 @@ class Projects::GitHttpController < Projects::ApplicationController
render plain: 'Not Found', status: :not_found render plain: 'Not Found', status: :not_found
end end
def render_not_allowed
render plain: download_access.message, status: :forbidden
end
def ci? def ci?
@ci.present? @ci.present?
end end
...@@ -162,12 +168,28 @@ class Projects::GitHttpController < Projects::ApplicationController ...@@ -162,12 +168,28 @@ class Projects::GitHttpController < Projects::ApplicationController
return false unless Gitlab.config.gitlab_shell.upload_pack return false unless Gitlab.config.gitlab_shell.upload_pack
if user if user
Gitlab::GitAccess.new(user, project).download_access_check.allowed? download_access.allowed?
else else
ci? || project.public? ci? || project.public?
end end
end end
def access
return @access if defined?(@access)
@access = Gitlab::GitAccess.new(user, project, 'http')
end
def download_access
return @download_access if defined?(@download_access)
@download_access = access.check('git-upload-pack')
end
def http_blocked?
!access.protocol_allowed?
end
def receive_pack_allowed? def receive_pack_allowed?
return false unless Gitlab.config.gitlab_shell.receive_pack return false unless Gitlab.config.gitlab_shell.receive_pack
......
class Projects::MergeRequestsController < Projects::ApplicationController class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include DiffForPath
include DiffHelper include DiffHelper
include IssuableActions include IssuableActions
include ToggleAwardEmoji include ToggleAwardEmoji
...@@ -9,10 +10,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -9,10 +10,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check, :edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
:ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip
] ]
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds] before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
before_action :define_show_vars, only: [:show, :diffs, :commits, :builds] before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check] before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs]
before_action :define_diff_comment_vars, only: [:diffs]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds] before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds]
# Allow read any merge_request # Allow read any merge_request
...@@ -53,19 +55,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -53,19 +55,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def show def show
@note_counts = Note.where(commit_id: @merge_request.commits.map(&:id)).
group(:commit_id).count
respond_to do |format| respond_to do |format|
format.html format.html { define_discussion_vars }
format.json { render json: @merge_request }
format.json do
render json: @merge_request
end
format.patch do format.patch do
headers.store(*Gitlab::Workhorse.send_git_patch(@project.repository, return render_404 unless @merge_request.diff_refs
@merge_request.diff_base_commit.id,
@merge_request.last_commit.id)) send_git_patch @project.repository, @merge_request.diff_refs
headers['Content-Disposition'] = 'inline'
head :ok
end end
format.diff do format.diff do
return render_404 unless @merge_request.diff_refs return render_404 unless @merge_request.diff_refs
...@@ -77,52 +79,65 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -77,52 +79,65 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diffs def diffs
apply_diff_view_cookie! apply_diff_view_cookie!
@commit = @merge_request.last_commit @merge_request_diff = @merge_request.merge_request_diff
@base_commit = @merge_request.diff_base_commit
# MRs created before 8.4 don't have a diff_base_commit,
# but we need it for the "View file @ ..." link by deleted files
@base_commit ||= @merge_request.first_commit.parent || @merge_request.first_commit
@comments_target = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
@grouped_diff_notes = @merge_request.notes.grouped_diff_notes
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
respond_to do |format| respond_to do |format|
format.html format.html { define_discussion_vars }
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } } format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
end end
end end
# With an ID param, loads the MR at that ID. Otherwise, accepts the same params as #new
# and uses that (unsaved) MR.
#
def diff_for_path
if params[:id]
merge_request
define_diff_comment_vars
else
build_merge_request
@diff_notes_disabled = true
@grouped_diff_notes = {}
end
define_commit_vars
diffs = @merge_request.diffs(diff_options)
render_diff_for_path(diffs, @merge_request.diff_refs, @merge_request.project)
end
def commits def commits
respond_to do |format| respond_to do |format|
format.html { render 'show' } format.html do
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_commits') } } define_discussion_vars
render 'show'
end
format.json do
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
@note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
render json: { html: view_to_html_string('projects/merge_requests/show/_commits') }
end
end end
end end
def builds def builds
respond_to do |format| respond_to do |format|
format.html { render 'show' } format.html do
define_discussion_vars
render 'show'
end
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } } format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
end end
end end
def new def new
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project) build_merge_request
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
@noteable = @merge_request @noteable = @merge_request
@target_branches = if @merge_request.target_project @target_branches = if @merge_request.target_project
...@@ -134,7 +149,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -134,7 +149,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@target_project = merge_request.target_project @target_project = merge_request.target_project
@source_project = merge_request.source_project @source_project = merge_request.source_project
@commits = @merge_request.compare_commits.reverse @commits = @merge_request.compare_commits.reverse
@commit = @merge_request.last_commit @commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit @base_commit = @merge_request.diff_base_commit
@diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare @diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare
@diff_notes_disabled = true @diff_notes_disabled = true
...@@ -212,7 +227,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -212,7 +227,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
return return
end end
if params[:sha] != @merge_request.source_sha if params[:sha] != @merge_request.diff_head_sha
@status = :sha_mismatch @status = :sha_mismatch
return return
end end
...@@ -274,16 +289,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -274,16 +289,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController
status ||= "preparing" status ||= "preparing"
else else
ci_service = @merge_request.source_project.ci_service ci_service = @merge_request.source_project.ci_service
status = ci_service.commit_status(merge_request.last_commit.sha, merge_request.source_branch) if ci_service status = ci_service.commit_status(merge_request.diff_head_sha, merge_request.source_branch) if ci_service
if ci_service.respond_to?(:commit_coverage) if ci_service.respond_to?(:commit_coverage)
coverage = ci_service.commit_coverage(merge_request.last_commit.sha, merge_request.source_branch) coverage = ci_service.commit_coverage(merge_request.diff_head_sha, merge_request.source_branch)
end end
end end
response = { response = {
title: merge_request.title, title: merge_request.title,
sha: merge_request.last_commit_short_sha, sha: merge_request.diff_head_commit.short_id,
status: status, status: status,
coverage: coverage coverage: coverage
} }
...@@ -308,10 +323,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -308,10 +323,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
alias_method :issuable, :merge_request alias_method :issuable, :merge_request
alias_method :awardable, :merge_request alias_method :awardable, :merge_request
def closes_issues
@closes_issues ||= @merge_request.closes_issues
end
def authorize_update_merge_request! def authorize_update_merge_request!
return render_404 unless can?(current_user, :update_merge_request, @merge_request) return render_404 unless can?(current_user, :update_merge_request, @merge_request)
end end
...@@ -340,14 +351,30 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -340,14 +351,30 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def define_show_vars def define_show_vars
@noteable = @merge_request
@commits_count = @merge_request.commits.count
@pipeline = @merge_request.pipeline
@statuses = @pipeline.statuses if @pipeline
if @merge_request.locked_long_ago?
@merge_request.unlock_mr
@merge_request.close
end
end
# Discussion tab data is rendered on html responses of actions
# :show, :diff, :commits, :builds. but not when request the data through AJAX
def define_discussion_vars
# Build a note object for comment form # Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request) @note = @project.notes.new(noteable: @noteable)
@discussions = @merge_request.mr_and_commit_notes. @discussions = @noteable.mr_and_commit_notes.
inc_author_project_award_emoji. inc_author_project_award_emoji.
fresh. fresh.
discussions discussions
# This is not executed lazily
@notes = Banzai::NoteRenderer.render( @notes = Banzai::NoteRenderer.render(
@discussions.flatten, @discussions.flatten,
@project, @project,
...@@ -356,28 +383,35 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -356,28 +383,35 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@project_wiki, @project_wiki,
@ref @ref
) )
@noteable = @merge_request
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
@merge_request_diff = @merge_request.merge_request_diff
@pipeline = @merge_request.pipeline
@statuses = @pipeline.statuses if @pipeline
if @merge_request.locked_long_ago?
@merge_request.unlock_mr
@merge_request.close
end
end end
def define_widget_vars def define_widget_vars
@pipeline = @merge_request.pipeline @pipeline = @merge_request.pipeline
@pipelines = [@pipeline].compact @pipelines = [@pipeline].compact
closes_issues end
def define_commit_vars
@commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
end
def define_diff_comment_vars
@comments_target = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
@use_legacy_diff_notes = !@merge_request.support_new_diff_notes?
@grouped_diff_notes = @merge_request.notes.grouped_diff_notes
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
end end
def invalid_mr def invalid_mr
...@@ -408,4 +442,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -408,4 +442,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params[:merge_when_build_succeeds].present? && params[:merge_when_build_succeeds].present? &&
@merge_request.pipeline && @merge_request.pipeline.active? @merge_request.pipeline && @merge_request.pipeline.active?
end end
def build_merge_request
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
end
end end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment