Commit 999a374f authored by Valery Sizov's avatar Valery Sizov

Merge branch 'emoji-picker-search'into emoji_picker_frequently_used

parents d794ae8e e557c8d0
...@@ -76,7 +76,7 @@ Style/BlockEndNewline: ...@@ -76,7 +76,7 @@ Style/BlockEndNewline:
Description: 'Put end statement of multiline block on its own line.' Description: 'Put end statement of multiline block on its own line.'
Enabled: true Enabled: true
Style/Blocks: Style/BlockDelimiters:
Description: >- Description: >-
Avoid using {...} for multi-line blocks (multiline chaining is Avoid using {...} for multi-line blocks (multiline chaining is
always ugly). always ugly).
...@@ -232,6 +232,10 @@ Style/EvenOdd: ...@@ -232,6 +232,10 @@ Style/EvenOdd:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
Enabled: false Enabled: false
Style/ExtraSpacing:
Description: 'Do not use unnecessary spacing.'
Enabled: false
Style/FileName: Style/FileName:
Description: 'Use snake_case for source file names.' Description: 'Use snake_case for source file names.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files'
...@@ -431,6 +435,14 @@ Style/OpMethod: ...@@ -431,6 +435,14 @@ Style/OpMethod:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg'
Enabled: false Enabled: false
Style/ParallelAssignment:
Description: >-
Check for simple usages of parallel assignment.
It will only warn when the number of variables
matches on both sides of the assignment.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment'
Enabled: false
Style/ParenthesesAroundCondition: Style/ParenthesesAroundCondition:
Description: >- Description: >-
Don't use parentheses around the condition of an Don't use parentheses around the condition of an
...@@ -669,6 +681,13 @@ Style/TrailingWhitespace: ...@@ -669,6 +681,13 @@ Style/TrailingWhitespace:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace'
Enabled: false Enabled: false
Style/TrailingUnderscoreVariable:
Description: >-
Checks for the usage of unneeded trailing underscores at the
end of parallel variable assignment.
AllowNamedUnderscoreVariables: true
Enabled: false
Style/TrivialAccessors: Style/TrivialAccessors:
Description: 'Prefer attr_* methods to trivial readers/writers.' Description: 'Prefer attr_* methods to trivial readers/writers.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family'
...@@ -690,11 +709,6 @@ Style/UnneededPercentQ: ...@@ -690,11 +709,6 @@ Style/UnneededPercentQ:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q'
Enabled: false Enabled: false
Style/UnneededPercentX:
Description: 'Checks for %x when `` would do.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x'
Enabled: false
Style/VariableInterpolation: Style/VariableInterpolation:
Description: >- Description: >-
Don't interpolate global, instance and class variables Don't interpolate global, instance and class variables
...@@ -778,6 +792,10 @@ Metrics/MethodLength: ...@@ -778,6 +792,10 @@ Metrics/MethodLength:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods'
Enabled: false Enabled: false
Metrics/ModuleLength:
Description: 'Avoid modules longer than 100 lines of code.'
Enabled: false
#################### Lint ################################ #################### Lint ################################
### Warnings ### Warnings
...@@ -961,6 +979,12 @@ Rails/ActionFilter: ...@@ -961,6 +979,12 @@ Rails/ActionFilter:
Description: 'Enforces consistent use of action filter methods.' Description: 'Enforces consistent use of action filter methods.'
Enabled: true Enabled: true
Rails/Date:
Description: >-
Checks the correct usage of date aware methods,
such as Date.today, Date.current etc.
Enabled: false
Rails/DefaultScope: Rails/DefaultScope:
Description: 'Checks if the argument passed to default_scope is a block.' Description: 'Checks if the argument passed to default_scope is a block.'
Enabled: false Enabled: false
...@@ -987,6 +1011,12 @@ Rails/ScopeArgs: ...@@ -987,6 +1011,12 @@ Rails/ScopeArgs:
Description: 'Checks the arguments of ActiveRecord scopes.' Description: 'Checks the arguments of ActiveRecord scopes.'
Enabled: false Enabled: false
Rails/TimeZone:
Description: 'Checks the correct usage of time zone aware methods.'
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time'
Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
Enabled: false
Rails/Validation: Rails/Validation:
Description: 'Use validates :attribute, hash of validations.' Description: 'Use validates :attribute, hash of validations.'
Enabled: false Enabled: false
......
...@@ -4,11 +4,23 @@ v 8.4.0 (unreleased) ...@@ -4,11 +4,23 @@ v 8.4.0 (unreleased)
- Add "Frequently used" category to emoji picker - Add "Frequently used" category to emoji picker
v 8.3.0 (unreleased) v 8.3.0 (unreleased)
- Fix Error 500 when doing a search in dashboard before visiting any project (Stan Hu)
- Implement new UI for group page
- Implement search inside emoji picker
- Add project permissions to all project API endpoints (Stan Hu)
v 8.3.0
- Add CAS support (tduehr)
- Bump rack-attack to 4.3.1 for security fix (Stan Hu)
- API support for starred projects for authorized user (Zeger-Jan van de Weg)
- Add link to merge request on build detail page.
- Add open_issues_count to project API (Stan Hu)
- Expand character set of usernames created by Omniauth (Corey Hinshaw) - Expand character set of usernames created by Omniauth (Corey Hinshaw)
- Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg) - Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
- Merge when build succeeds (Zeger-Jan van de Weg)
- Provide better diagnostic message upon project creation errors (Stan Hu) - Provide better diagnostic message upon project creation errors (Stan Hu)
- Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu) - Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu)
- Remove api credentials from link to build_page
- Deprecate GitLabCiService making it to always be inactive
- Bump gollum-lib to 4.1.0 (Stan Hu) - Bump gollum-lib to 4.1.0 (Stan Hu)
- Fix broken group avatar upload under "New group" (Stan Hu) - Fix broken group avatar upload under "New group" (Stan Hu)
- Update project repositorize size and commit count during import:repos task (Stan Hu) - Update project repositorize size and commit count during import:repos task (Stan Hu)
...@@ -20,13 +32,16 @@ v 8.3.0 (unreleased) ...@@ -20,13 +32,16 @@ v 8.3.0 (unreleased)
- Fix 500 error when update group member permission - Fix 500 error when update group member permission
- Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
- Recognize issue/MR/snippet/commit links as references - Recognize issue/MR/snippet/commit links as references
- Backport JIRA features from EE to CE
- Add ignore whitespace change option to commit view - Add ignore whitespace change option to commit view
- Fire update hook from GitLab - Fire update hook from GitLab
- Allow account unlock via email
- Style warning about mentioning many people in a comment - Style warning about mentioning many people in a comment
- Fix: sort milestones by due date once again (Greg Smethells) - Fix: sort milestones by due date once again (Greg Smethells)
- Migrate all CI::Services and CI::WebHooks to Services and WebHooks - Migrate all CI::Services and CI::WebHooks to Services and WebHooks
- Don't show project fork event as "imported" - Don't show project fork event as "imported"
- Add API endpoint to fetch merge request commits list - Add API endpoint to fetch merge request commits list
- Don't create CI status for refs that doesn't have .gitlab-ci.yml, even if the builds are enabled
- Expose events API with comment information and author info - Expose events API with comment information and author info
- Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583
- Run custom Git hooks when branch is created or deleted. - Run custom Git hooks when branch is created or deleted.
...@@ -58,6 +73,7 @@ v 8.3.0 (unreleased) ...@@ -58,6 +73,7 @@ v 8.3.0 (unreleased)
- Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present - Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present
- Persist runners registration token in database - Persist runners registration token in database
- Fix online editor should not remove newlines at the end of the file - Fix online editor should not remove newlines at the end of the file
- Expose Git's version in the admin area
v 8.2.3 v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu) - Fix application settings cache not expiring after changes (Stan Hu)
...@@ -65,8 +81,6 @@ v 8.2.3 ...@@ -65,8 +81,6 @@ v 8.2.3
- Update documentation for "Guest" permissions - Update documentation for "Guest" permissions
- Properly convert Emoji-only comments into Award Emojis - Properly convert Emoji-only comments into Award Emojis
- Enable devise paranoid mode to prevent user enumeration attack - Enable devise paranoid mode to prevent user enumeration attack
v 8.2.3
- Webhook payload has an added, modified and removed properties for each commit - Webhook payload has an added, modified and removed properties for each commit
- Fix 500 error when creating a merge request that removes a submodule - Fix 500 error when creating a merge request that removes a submodule
......
...@@ -358,7 +358,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor ...@@ -358,7 +358,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[core team]: https://about.gitlab.com/core-team/ [core team]: https://about.gitlab.com/core-team/
[getting help page]: https://about.gitlab.com/getting-help/ [getting help page]: https://about.gitlab.com/getting-help/
[Codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq [Codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up+for+grabs [up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs
[medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455 [medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455
[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues [ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues [ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
......
...@@ -23,6 +23,7 @@ gem 'devise-async', '~> 0.9.0' ...@@ -23,6 +23,7 @@ gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.2.0' gem 'doorkeeper', '~> 2.2.0'
gem 'omniauth', '~> 1.2.2' gem 'omniauth', '~> 1.2.2'
gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-bitbucket', '~> 0.0.2'
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'
...@@ -101,6 +102,9 @@ gem 'wikicloth', '0.8.1' ...@@ -101,6 +102,9 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.10.1' gem 'rouge', '~> 1.10.1'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
gem 'nokogiri', '1.6.7.1'
# Diffs # Diffs
gem 'diffy', '~> 3.0.3' gem 'diffy', '~> 3.0.3'
...@@ -175,7 +179,7 @@ gem "sanitize", '~> 2.0' ...@@ -175,7 +179,7 @@ gem "sanitize", '~> 2.0'
gem 'babosa', '~> 1.0.2' gem 'babosa', '~> 1.0.2'
# Protect against bruteforcing # Protect against bruteforcing
gem "rack-attack", '~> 4.3.0' gem "rack-attack", '~> 4.3.1'
# Ace editor # Ace editor
gem 'ace-rails-ap', '~> 2.0.1' gem 'ace-rails-ap', '~> 2.0.1'
...@@ -186,7 +190,7 @@ gem 'mousetrap-rails', '~> 1.4.6' ...@@ -186,7 +190,7 @@ gem 'mousetrap-rails', '~> 1.4.6'
# Detect and convert string character encoding # Detect and convert string character encoding
gem 'charlock_holmes', '~> 0.7.3' gem 'charlock_holmes', '~> 0.7.3'
gem "sass-rails", '~> 4.0.5' 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'
...@@ -198,9 +202,9 @@ gem 'font-awesome-rails', '~> 4.2' ...@@ -198,9 +202,9 @@ gem 'font-awesome-rails', '~> 4.2'
gem 'gitlab_emoji', '~> 0.2.0' gem 'gitlab_emoji', '~> 0.2.0'
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', '~> 3.1.3' gem 'jquery-rails', '~> 4.0.0'
gem 'jquery-scrollto-rails', '~> 1.4.3' gem 'jquery-scrollto-rails', '~> 1.4.3'
gem 'jquery-ui-rails', '~> 4.2.1' gem 'jquery-ui-rails', '~> 5.0.0'
gem 'nprogress-rails', '~> 0.1.6.7' gem 'nprogress-rails', '~> 0.1.6.7'
gem 'raphael-rails', '~> 2.1.2' gem 'raphael-rails', '~> 2.1.2'
gem 'request_store', '~> 1.2.0' gem 'request_store', '~> 1.2.0'
...@@ -215,7 +219,7 @@ group :development do ...@@ -215,7 +219,7 @@ group :development do
gem "annotate", "~> 2.6.0" gem "annotate", "~> 2.6.0"
gem "letter_opener", '~> 1.1.2' gem "letter_opener", '~> 1.1.2'
gem 'quiet_assets', '~> 1.0.2' gem 'quiet_assets', '~> 1.0.2'
gem 'rerun', '~> 0.10.0' gem 'rerun', '~> 0.11.0'
gem 'bullet', require: false gem 'bullet', require: false
gem 'rblineprof', platform: :mri, require: false gem 'rblineprof', platform: :mri, require: false
gem 'web-console', '~> 2.0' gem 'web-console', '~> 2.0'
...@@ -251,7 +255,7 @@ group :development, :test do ...@@ -251,7 +255,7 @@ group :development, :test do
gem 'capybara', '~> 2.4.0' gem 'capybara', '~> 2.4.0'
gem 'capybara-screenshot', '~> 1.0.0' gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.6.0' gem 'poltergeist', '~> 1.8.1'
gem 'teaspoon', '~> 1.0.0' gem 'teaspoon', '~> 1.0.0'
gem 'teaspoon-jasmine', '~> 2.2.0' gem 'teaspoon-jasmine', '~> 2.2.0'
...@@ -261,7 +265,7 @@ group :development, :test do ...@@ -261,7 +265,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.0.0' gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.28.0', require: false gem 'rubocop', '~> 0.35.0', require: false
gem 'coveralls', '~> 0.8.2', require: false gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.10.0', require: false gem 'simplecov', '~> 0.10.0', require: false
gem 'flog', require: false gem 'flog', require: false
......
...@@ -117,23 +117,6 @@ GEM ...@@ -117,23 +117,6 @@ GEM
activemodel (>= 3.2.0) activemodel (>= 3.2.0)
activesupport (>= 3.2.0) activesupport (>= 3.2.0)
json (>= 1.7) json (>= 1.7)
celluloid (0.17.2)
celluloid-essentials
celluloid-extras
celluloid-fsm
celluloid-pool
celluloid-supervision
timers (>= 4.1.1)
celluloid-essentials (0.20.5)
timers (>= 4.1.1)
celluloid-extras (0.20.5)
timers (>= 4.1.1)
celluloid-fsm (0.20.5)
timers (>= 4.1.1)
celluloid-pool (0.20.5)
timers (>= 4.1.1)
celluloid-supervision (0.20.5)
timers (>= 4.1.1)
charlock_holmes (0.7.3) charlock_holmes (0.7.3)
chunky_png (1.3.5) chunky_png (1.3.5)
cliver (0.3.2) cliver (0.3.2)
...@@ -369,7 +352,6 @@ GEM ...@@ -369,7 +352,6 @@ GEM
hipchat (1.5.2) hipchat (1.5.2)
httparty httparty
mimemagic mimemagic
hitimes (1.2.3)
html-pipeline (1.11.0) html-pipeline (1.11.0)
activesupport (>= 2) activesupport (>= 2)
nokogiri (~> 1.4) nokogiri (~> 1.4)
...@@ -390,15 +372,16 @@ GEM ...@@ -390,15 +372,16 @@ GEM
inflecto (0.0.2) inflecto (0.0.2)
ipaddress (0.8.0) ipaddress (0.8.0)
jquery-atwho-rails (1.3.2) jquery-atwho-rails (1.3.2)
jquery-rails (3.1.4) jquery-rails (4.0.5)
railties (>= 3.0, < 5.0) rails-dom-testing (~> 1.0)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
jquery-scrollto-rails (1.4.3) jquery-scrollto-rails (1.4.3)
railties (> 3.1, < 5.0) railties (> 3.1, < 5.0)
jquery-turbolinks (2.1.0) jquery-turbolinks (2.1.0)
railties (>= 3.1.0) railties (>= 3.1.0)
turbolinks turbolinks
jquery-ui-rails (4.2.1) jquery-ui-rails (5.0.5)
railties (>= 3.2.16) railties (>= 3.2.16)
json (1.8.3) json (1.8.3)
jwt (1.5.2) jwt (1.5.2)
...@@ -410,8 +393,7 @@ GEM ...@@ -410,8 +393,7 @@ GEM
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.1.2) letter_opener (1.1.2)
launchy (~> 2.2) launchy (~> 2.2)
listen (2.9.0) listen (3.0.5)
celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9) rb-inotify (>= 0.9)
loofah (2.0.3) loofah (2.0.3)
...@@ -424,7 +406,7 @@ GEM ...@@ -424,7 +406,7 @@ GEM
method_source (0.8.2) method_source (0.8.2)
mime-types (1.25.1) mime-types (1.25.1)
mimemagic (0.3.0) mimemagic (0.3.0)
mini_portile (0.6.2) mini_portile2 (2.0.0)
minitest (5.7.0) minitest (5.7.0)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
multi_json (1.11.2) multi_json (1.11.2)
...@@ -439,8 +421,8 @@ GEM ...@@ -439,8 +421,8 @@ GEM
grape grape
newrelic_rpm newrelic_rpm
newrelic_rpm (3.9.4.245) newrelic_rpm (3.9.4.245)
nokogiri (1.6.6.4) nokogiri (1.6.7.1)
mini_portile (~> 0.6.0) mini_portile2 (~> 2.0.0.rc2)
nprogress-rails (0.1.6.7) nprogress-rails (0.1.6.7)
oauth (0.4.7) oauth (0.4.7)
oauth2 (1.0.0) oauth2 (1.0.0)
...@@ -458,6 +440,10 @@ GEM ...@@ -458,6 +440,10 @@ GEM
multi_json (~> 1.7) multi_json (~> 1.7)
omniauth (~> 1.1) omniauth (~> 1.1)
omniauth-oauth (~> 1.0) omniauth-oauth (~> 1.0)
omniauth-cas3 (1.1.3)
addressable (~> 2.3)
nokogiri (~> 1.6.6)
omniauth (~> 1.2)
omniauth-facebook (3.0.0) omniauth-facebook (3.0.0)
omniauth-oauth2 (~> 1.2) omniauth-oauth2 (~> 1.2)
omniauth-github (1.1.2) omniauth-github (1.1.2)
...@@ -507,13 +493,13 @@ GEM ...@@ -507,13 +493,13 @@ GEM
parser (2.2.3.0) parser (2.2.3.0)
ast (>= 1.1, < 3.0) ast (>= 1.1, < 3.0)
pg (0.18.4) pg (0.18.4)
poltergeist (1.6.0) poltergeist (1.8.1)
capybara (~> 2.1) capybara (~> 2.1)
cliver (~> 0.3.1) cliver (~> 0.3.1)
multi_json (~> 1.0) multi_json (~> 1.0)
websocket-driver (>= 0.2.0) websocket-driver (>= 0.2.0)
posix-spawn (0.3.11) posix-spawn (0.3.11)
powerpack (0.0.9) powerpack (0.1.1)
pry (0.10.3) pry (0.10.3)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
...@@ -526,7 +512,7 @@ GEM ...@@ -526,7 +512,7 @@ GEM
rack (1.6.4) rack (1.6.4)
rack-accept (0.4.5) rack-accept (0.4.5)
rack (>= 0.4) rack (>= 0.4)
rack-attack (4.3.0) rack-attack (4.3.1)
rack rack
rack-cors (0.4.0) rack-cors (0.4.0)
rack-mount (0.8.3) rack-mount (0.8.3)
...@@ -601,8 +587,8 @@ GEM ...@@ -601,8 +587,8 @@ GEM
redis-store (1.1.7) redis-store (1.1.7)
redis (>= 2.2) redis (>= 2.2)
request_store (1.2.1) request_store (1.2.1)
rerun (0.10.0) rerun (0.11.0)
listen (~> 2.7, >= 2.7.3) listen (~> 3.0)
responders (2.1.0) responders (2.1.0)
railties (>= 4.2.0, < 5) railties (>= 4.2.0, < 5)
rest-client (1.8.0) rest-client (1.8.0)
...@@ -637,12 +623,13 @@ GEM ...@@ -637,12 +623,13 @@ GEM
rspec-mocks (~> 3.3.0) rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0) rspec-support (~> 3.3.0)
rspec-support (3.3.0) rspec-support (3.3.0)
rubocop (0.28.0) rubocop (0.35.1)
astrolabe (~> 1.3) astrolabe (~> 1.3)
parser (>= 2.2.0.pre.7, < 3.0) parser (>= 2.2.3.0, < 3.0)
powerpack (~> 0.0.6) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.7)
tins (<= 1.6.0)
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-progressbar (1.7.5) ruby-progressbar (1.7.5)
...@@ -661,12 +648,13 @@ GEM ...@@ -661,12 +648,13 @@ GEM
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
sass (3.2.19) sass (3.4.20)
sass-rails (4.0.5) sass-rails (5.0.4)
railties (>= 4.0.0, < 5.0) railties (>= 4.0.0, < 5.0)
sass (~> 3.2.2) sass (~> 3.1)
sprockets (~> 2.8, < 3.0) sprockets (>= 2.8, < 4.0)
sprockets-rails (~> 2.0) sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sawyer (0.6.0) sawyer (0.6.0)
addressable (~> 2.3.5) addressable (~> 2.3.5)
faraday (~> 0.8, < 0.10) faraday (~> 0.8, < 0.10)
...@@ -758,8 +746,6 @@ GEM ...@@ -758,8 +746,6 @@ GEM
thor (0.19.1) thor (0.19.1)
thread_safe (0.3.5) thread_safe (0.3.5)
tilt (1.4.1) tilt (1.4.1)
timers (4.1.1)
hitimes
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
tinder (1.10.1) tinder (1.10.1)
eventmachine (~> 1.0) eventmachine (~> 1.0)
...@@ -894,10 +880,10 @@ DEPENDENCIES ...@@ -894,10 +880,10 @@ DEPENDENCIES
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
httparty (~> 0.13.3) httparty (~> 0.13.3)
jquery-atwho-rails (~> 1.3.2) jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 3.1.3) jquery-rails (~> 4.0.0)
jquery-scrollto-rails (~> 1.4.3) jquery-scrollto-rails (~> 1.4.3)
jquery-turbolinks (~> 2.1.0) jquery-turbolinks (~> 2.1.0)
jquery-ui-rails (~> 4.2.1) jquery-ui-rails (~> 5.0.0)
kaminari (~> 0.16.3) kaminari (~> 0.16.3)
letter_opener (~> 1.1.2) letter_opener (~> 1.1.2)
mail_room (~> 0.6.1) mail_room (~> 0.6.1)
...@@ -908,11 +894,13 @@ DEPENDENCIES ...@@ -908,11 +894,13 @@ DEPENDENCIES
net-ssh (~> 3.0.1) net-ssh (~> 3.0.1)
newrelic-grape newrelic-grape
newrelic_rpm (~> 3.9.4.245) newrelic_rpm (~> 3.9.4.245)
nokogiri (= 1.6.7.1)
nprogress-rails (~> 0.1.6.7) nprogress-rails (~> 0.1.6.7)
oauth2 (~> 1.0.0) oauth2 (~> 1.0.0)
octokit (~> 3.7.0) octokit (~> 3.7.0)
omniauth (~> 1.2.2) omniauth (~> 1.2.2)
omniauth-bitbucket (~> 0.0.2) omniauth-bitbucket (~> 0.0.2)
omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 3.0.0) omniauth-facebook (~> 3.0.0)
omniauth-github (~> 1.1.1) omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.0) omniauth-gitlab (~> 1.0.0)
...@@ -925,10 +913,10 @@ DEPENDENCIES ...@@ -925,10 +913,10 @@ DEPENDENCIES
org-ruby (~> 0.9.12) org-ruby (~> 0.9.12)
paranoia (~> 2.0) paranoia (~> 2.0)
pg (~> 0.18.2) pg (~> 0.18.2)
poltergeist (~> 1.6.0) poltergeist (~> 1.8.1)
pry-rails pry-rails
quiet_assets (~> 1.0.2) quiet_assets (~> 1.0.2)
rack-attack (~> 4.3.0) rack-attack (~> 4.3.1)
rack-cors (~> 0.4.0) rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1) rack-oauth2 (~> 1.2.1)
rails (= 4.2.4) rails (= 4.2.4)
...@@ -940,15 +928,15 @@ DEPENDENCIES ...@@ -940,15 +928,15 @@ DEPENDENCIES
redis-namespace redis-namespace
redis-rails (~> 4.0.0) redis-rails (~> 4.0.0)
request_store (~> 1.2.0) request_store (~> 1.2.0)
rerun (~> 0.10.0) rerun (~> 0.11.0)
responders (~> 2.0) responders (~> 2.0)
rouge (~> 1.10.1) rouge (~> 1.10.1)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0) rspec-rails (~> 3.3.0)
rubocop (~> 0.28.0) rubocop (~> 0.35.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 4.0.5) sass-rails (~> 5.0.0)
sdoc (~> 0.3.20) sdoc (~> 0.3.20)
seed-fu (~> 2.3.5) seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9) select2-rails (~> 3.5.9)
......
8.3.0.pre 8.4.0.pre
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
# the compiled file. # the compiled file.
# #
#= require jquery #= require jquery
#= require jquery.ui.all #= require jquery-ui
#= require jquery_ujs #= require jquery_ujs
#= require jquery.cookie #= require jquery.cookie
#= require jquery.endless-scroll #= require jquery.endless-scroll
......
...@@ -11,6 +11,7 @@ class @AwardsHandler ...@@ -11,6 +11,7 @@ class @AwardsHandler
$(".emoji-menu").hide() $(".emoji-menu").hide()
@renderFrequentlyUsedBlock() @renderFrequentlyUsedBlock()
@setupSearch()
addAward: (emoji) -> addAward: (emoji) ->
emoji = @normilizeEmojiName(emoji) emoji = @normilizeEmojiName(emoji)
...@@ -80,7 +81,7 @@ class @AwardsHandler ...@@ -80,7 +81,7 @@ class @AwardsHandler
nodes = [] nodes = []
nodes.push("<div class='award active' title='me'>") nodes.push("<div class='award active' title='me'>")
nodes.push("<div class='icon emoji-icon " + emojiCssClass + "' data-emoji='" + emoji + "'></div>") nodes.push("<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>")
nodes.push("<div class='counter'>1</div>") nodes.push("<div class='counter'>1</div>")
nodes.push("</div>") nodes.push("</div>")
...@@ -89,13 +90,19 @@ class @AwardsHandler ...@@ -89,13 +90,19 @@ class @AwardsHandler
$(".award").tooltip() $(".award").tooltip()
resolveNameToCssClass: (emoji) -> resolveNameToCssClass: (emoji) ->
unicodeName = $(".emoji-menu-content [data-emoji='?']".replace("?", emoji)).data("unicode-name") emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']")
"emoji-" + unicodeName if emoji_icon.length > 0
unicodeName = emoji_icon.data("unicode-name")
else
# Find by alias
unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data("unicode-name")
"emoji-#{unicodeName}"
postEmoji: (emoji, callback) -> postEmoji: (emoji, callback) ->
$.post @post_emoji_url, { note: { $.post @post_emoji_url, { note: {
note: ":" + emoji + ":" note: ":#{emoji}:"
noteable_type: @noteable_type noteable_type: @noteable_type
noteable_id: @noteable_id noteable_id: @noteable_id
}},(data) -> }},(data) ->
...@@ -103,7 +110,7 @@ class @AwardsHandler ...@@ -103,7 +110,7 @@ class @AwardsHandler
callback.call() callback.call()
findEmojiIcon: (emoji) -> findEmojiIcon: (emoji) ->
$(".award [data-emoji='" + emoji + "']") $(".award [data-emoji='#{emoji}']")
scrollToAwards: -> scrollToAwards: ->
$('body, html').animate({ $('body, html').animate({
...@@ -134,3 +141,22 @@ class @AwardsHandler ...@@ -134,3 +141,22 @@ class @AwardsHandler
$(".emoji-menu-content").prepend(ul).prepend($("<h4>").text("Frequently used")) $(".emoji-menu-content").prepend(ul).prepend($("<h4>").text("Frequently used"))
setupSearch: ->
$("input.emoji-search").keyup (ev) =>
term = $(ev.target).val()
# Clean previous search results
$("ul.emoji-search,h5.emoji-search").remove()
if term
# Generate search result block
h5 = $("<h5>").text("Search results").addClass("emoji-search")
found_emojis = @searchEmojis(term).show()
ul = $("<ul>").addClass("emoji-search").append(found_emojis)
$(".emoji-menu-content ul, .emoji-menu-content h5").hide()
$(".emoji-menu-content").append(h5).append(ul)
else
$(".emoji-menu-content").children().show()
searchEmojis: (term)->
$(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone()
...@@ -18,7 +18,7 @@ class @IssuableContext ...@@ -18,7 +18,7 @@ class @IssuableContext
$('.issuable-affix').affix offset: $('.issuable-affix').affix offset:
top: -> top: ->
@top = ($('.issuable-affix').offset().top - 60) @top = ($('.issuable-affix').offset().top - 70)
bottom: -> bottom: ->
@bottom = $('.footer').outerHeight(true) @bottom = $('.footer').outerHeight(true)
......
...@@ -10,12 +10,12 @@ class @Issue ...@@ -10,12 +10,12 @@ class @Issue
@initTaskList() @initTaskList()
initTaskList: -> initTaskList: ->
$('.issue-details .js-task-list-container').taskList('enable') $('.detail-page-description .js-task-list-container').taskList('enable')
$(document).on 'tasklist:changed', '.issue-details .js-task-list-container', @updateTaskList $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
disableTaskList: -> disableTaskList: ->
$('.issue-details .js-task-list-container').taskList('disable') $('.detail-page-description .js-task-list-container').taskList('disable')
$(document).off 'tasklist:changed', '.issue-details .js-task-list-container' $(document).off 'tasklist:changed', '.detail-page-description .js-task-list-container'
# TODO (rspeicher): Make the issue description inline-editable like a note so # TODO (rspeicher): Make the issue description inline-editable like a note so
# that we can re-use its form here # that we can re-use its form here
......
...@@ -40,12 +40,12 @@ class @MergeRequest ...@@ -40,12 +40,12 @@ class @MergeRequest
this.$('.all-commits').removeClass 'hide' this.$('.all-commits').removeClass 'hide'
initTaskList: -> initTaskList: ->
$('.merge-request-details .js-task-list-container').taskList('enable') $('.detail-page-description .js-task-list-container').taskList('enable')
$(document).on 'tasklist:changed', '.merge-request-details .js-task-list-container', @updateTaskList $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
disableTaskList: -> disableTaskList: ->
$('.merge-request-details .js-task-list-container').taskList('disable') $('.detail-page-description .js-task-list-container').taskList('disable')
$(document).off 'tasklist:changed', '.merge-request-details .js-task-list-container' $(document).off 'tasklist:changed', '.detail-page-description .js-task-list-container'
# TODO (rspeicher): Make the merge request description inline-editable like a # TODO (rspeicher): Make the merge request description inline-editable like a
# note so that we can re-use its form here # note so that we can re-use its form here
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* This is a manifest file that'll automatically include all the stylesheets available in this directory * This is a manifest file that'll automatically include all the stylesheets available in this directory
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
* the top of the compiled file, but it's generally better to create a new file per style scope. * the top of the compiled file, but it's generally better to create a new file per style scope.
*= require jquery.ui.datepicker *= require jquery-ui/datepicker
*= require jquery.ui.autocomplete *= require jquery-ui/autocomplete
*= require jquery.atwho *= require jquery.atwho
*= require select2 *= require select2
*= require_self *= require_self
...@@ -48,4 +48,4 @@ ...@@ -48,4 +48,4 @@
/* /*
* Styles for JS behaviors. * Styles for JS behaviors.
*/ */
@import "behaviors.scss"; @import "behaviors.scss";
\ No newline at end of file
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
.cover-block { .cover-block {
text-align: center; text-align: center;
background: #f7f8fa; background: $background-color;
margin: -$gl-padding; margin: -$gl-padding;
margin-bottom: 0; margin-bottom: 0;
padding: 44px $gl-padding; padding: 44px $gl-padding;
......
@mixin btn-default { @mixin btn-default {
@include border-radius(2px); @include border-radius(3px);
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
text-transform: uppercase; font-size: 15px;
font-size: 13px; font-weight: 500;
font-weight: 600;
line-height: 18px; line-height: 18px;
padding: 11px $gl-padding; padding: 11px $gl-padding;
letter-spacing: .4px; letter-spacing: .4px;
...@@ -18,7 +17,7 @@ ...@@ -18,7 +17,7 @@
@mixin btn-middle { @mixin btn-middle {
@include btn-default; @include btn-default;
@include border-radius(2px); @include border-radius(3px);
padding: 11px 24px; padding: 11px 24px;
} }
...@@ -51,6 +50,10 @@ ...@@ -51,6 +50,10 @@
@include btn-color($blue-light, $border-blue-light, $blue-normal, $border-blue-normal, $blue-dark, $border-blue-dark, #FFFFFF); @include btn-color($blue-light, $border-blue-light, $blue-normal, $border-blue-normal, $blue-dark, $border-blue-dark, #FFFFFF);
} }
@mixin btn-blue-medium {
@include btn-color($blue-medium-light, $border-blue-light, $blue-medium, $border-blue-normal, $blue-medium-dark, $border-blue-dark, #FFFFFF);
}
@mixin btn-orange { @mixin btn-orange {
@include btn-color($orange-light, $border-orange-light, $orange-normal, $border-orange-normal, $orange-dark, $border-orange-dark, #FFFFFF); @include btn-color($orange-light, $border-orange-light, $orange-normal, $border-orange-normal, $orange-dark, $border-orange-dark, #FFFFFF);
} }
...@@ -60,7 +63,7 @@ ...@@ -60,7 +63,7 @@
} }
@mixin btn-gray { @mixin btn-gray {
@include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, #313236); @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, #313236);
} }
@mixin btn-white { @mixin btn-white {
...@@ -75,6 +78,10 @@ ...@@ -75,6 +78,10 @@
padding: 5px 10px; padding: 5px 10px;
} }
&.btn-nr {
padding: 7px 10px;
}
&.btn-xs { &.btn-xs {
padding: 1px 5px; padding: 1px 5px;
} }
...@@ -91,11 +98,15 @@ ...@@ -91,11 +98,15 @@
@include btn-gray; @include btn-gray;
} }
&.btn-primary, &.btn-primary {
@include btn-blue-medium;
}
&.btn-info { &.btn-info {
@include btn-blue; @include btn-blue;
} }
&.btn-close,
&.btn-warning { &.btn-warning {
@include btn-orange; @include btn-orange;
} }
...@@ -110,20 +121,8 @@ ...@@ -110,20 +121,8 @@
float: right; float: right;
} }
&.btn-close {
color: $gl-danger;
border-color: $gl-danger;
&:hover {
color: #B94A48;
}
}
&.btn-reopen { &.btn-reopen {
color: $gl-success; /* should be same as parent class for now */
border-color: $gl-success;
&:hover {
color: #468847;
}
} }
&.btn-grouped { &.btn-grouped {
......
...@@ -374,7 +374,7 @@ table { ...@@ -374,7 +374,7 @@ table {
} }
} }
.center-top-menu { .center-top-menu, .left-top-menu {
@include nav-menu; @include nav-menu;
text-align: center; text-align: center;
margin-top: 5px; margin-top: 5px;
...@@ -401,6 +401,16 @@ table { ...@@ -401,6 +401,16 @@ table {
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
height: 57px; height: 57px;
} }
&.wide {
margin-left: -$gl-padding;
margin-right: -$gl-padding;
}
}
.left-top-menu {
text-align: left;
border-bottom: 1px solid #EEE;
} }
.center-middle-menu { .center-middle-menu {
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
* *
*/ */
.issue-box { .status-box {
@include border-radius(2px); @include border-radius(3px);
display: block; display: block;
float: left; float: left;
...@@ -14,22 +14,22 @@ ...@@ -14,22 +14,22 @@
margin-right: 10px; margin-right: 10px;
font-size: $gl-font-size; font-size: $gl-font-size;
&.issue-box-closed { &.status-box-closed {
background-color: $gl-danger; background-color: $gl-danger;
color: #FFF; color: #FFF;
} }
&.issue-box-merged { &.status-box-merged {
background-color: $gl-primary; background-color: $gl-primary;
color: #FFF; color: #FFF;
} }
&.issue-box-open { &.status-box-open {
background-color: #019875; background-color: $green-light;
color: #FFF; color: #FFF;
} }
&.issue-box-expired { &.status-box-expired {
background: #cea61b; background: #cea61b;
color: #FFF; color: #FFF;
} }
......
...@@ -5,7 +5,7 @@ html { ...@@ -5,7 +5,7 @@ html {
} }
body { body {
background-color: #EAEBEC !important; background-color: #F3F3F3 !important;
&.navless { &.navless {
background-color: white !important; background-color: white !important;
......
...@@ -143,7 +143,11 @@ ul.controls { ...@@ -143,7 +143,11 @@ ul.controls {
> li { > li {
float: left; float: left;
padding-right: 10px; margin-right: 10px;
&:last-child {
margin-right: 0;
}
.author_link { .author_link {
display: inline-block; display: inline-block;
......
...@@ -87,7 +87,7 @@ ...@@ -87,7 +87,7 @@
.new_note, .new_note,
.edit_note, .edit_note,
.issuable-description, .detail-page-description,
.milestone-description, .milestone-description,
.wiki-content, .wiki-content,
.merge-request-form { .merge-request-form {
......
...@@ -123,7 +123,6 @@ ...@@ -123,7 +123,6 @@
padding: 0; padding: 0;
margin: 0; margin: 0;
list-style: none; list-style: none;
margin-top: 5px;
height: 56px; height: 56px;
li { li {
...@@ -131,9 +130,9 @@ ...@@ -131,9 +130,9 @@
a { a {
padding: 14px; padding: 14px;
font-size: 17px; font-size: 15px;
line-height: 28px; line-height: 28px;
color: #7f8fa4; color: #959494;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
&:hover, &:active, &:focus { &:hover, &:active, &:focus {
...@@ -143,8 +142,8 @@ ...@@ -143,8 +142,8 @@
} }
&.active a { &.active a {
color: #4c4e54; color: #616060;
border-bottom: 2px solid #1cacfc; border-bottom: 2px solid #4688f1;
} }
.badge { .badge {
......
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
display: none; display: none;
} }
.center-top-menu { .center-top-menu, .left-top-menu {
li a { li a {
font-size: 14px; font-size: 14px;
padding: 19px 10px; padding: 19px 10px;
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
.panel-heading { .panel-heading {
padding: 7px $gl-padding; padding: 7px $gl-padding;
line-height: 42px !important;
} }
.panel-body { .panel-body {
...@@ -15,3 +14,7 @@ ...@@ -15,3 +14,7 @@
} }
} }
} }
.container-blank .panel .panel-heading {
line-height: 42px !important;
}
...@@ -10,8 +10,7 @@ ...@@ -10,8 +10,7 @@
margin-left: -$gl-padding; margin-left: -$gl-padding;
margin-right: -$gl-padding; margin-right: -$gl-padding;
color: $gl-gray; color: $gl-gray;
border-bottom: 1px solid #ECEEF1; border-bottom: 1px solid $border-white-light;
border-right: 1px solid #ECEEF1;
&:target { &:target {
background: $hover; background: $hover;
......
$hover: #FFFAF1; $hover: #faf9f9;
$gl-text-color: #54565B; $gl-text-color: #54565B;
$gl-text-green: #4A2; $gl-text-green: #4A2;
$gl-text-red: #D12F19; $gl-text-red: #D12F19;
$gl-text-orange: #D90; $gl-text-orange: #D90;
$gl-header-color: #4c4e54; $gl-header-color: #323232;
$gl-link-color: #333c48; $gl-link-color: #333c48;
$md-text-color: #444; $md-text-color: #444;
$md-link-color: #3084bb; $md-link-color: #3084bb;
...@@ -15,13 +15,14 @@ $sidebar_width: 230px; ...@@ -15,13 +15,14 @@ $sidebar_width: 230px;
$avatar_radius: 50%; $avatar_radius: 50%;
$code_font_size: 13px; $code_font_size: 13px;
$code_line_height: 1.5; $code_line_height: 1.5;
$border-color: #dce0e6; $border-color: #efeff1;
$table-border-color: #eef0f2; $table-border-color: #eef0f2;
$background-color: #F7F8FA; $background-color: #faf9f9;
$header-height: 58px; $header-height: 58px;
$fixed-layout-width: 1200px; $fixed-layout-width: 1280px;
$gl-gray: #7f8fa4; $gl-gray: #5a5a5a;
$gl-padding: 16px; $gl-padding: 16px;
$gl-padding-top:10px;
$gl-avatar-size: 46px; $gl-avatar-size: 46px;
/* /*
...@@ -29,12 +30,12 @@ $gl-avatar-size: 46px; ...@@ -29,12 +30,12 @@ $gl-avatar-size: 46px;
*/ */
$white-light: #FFFFFF; $white-light: #FFFFFF;
$white-normal: #DCE0E5; $white-normal: #ededed;
$white-dark: #E4E7ED; $white-dark: #ededed;
$gray-light: #F0F2F5; $gray-light: #f7f7f7;
$gray-normal: #DCE0E5; $gray-normal: #ededed;
$gray-dark: #E4E7ED; $gray-dark: #ededed;
$green-light: #31AF64; $green-light: #31AF64;
$green-normal: #2FAA60; $green-normal: #2FAA60;
...@@ -44,6 +45,10 @@ $blue-light: #2EA8E5; ...@@ -44,6 +45,10 @@ $blue-light: #2EA8E5;
$blue-normal: #2D9FD8; $blue-normal: #2D9FD8;
$blue-dark: #2897CE; $blue-dark: #2897CE;
$blue-medium-light: #3498CB;
$blue-medium: #2F8EBF;
$blue-medium-dark: #2D86B4;
$orange-light: #FC6443; $orange-light: #FC6443;
$orange-normal: #E75E40; $orange-normal: #E75E40;
$orange-dark: #CE5237; $orange-dark: #CE5237;
...@@ -52,11 +57,11 @@ $red-light: #F43263; ...@@ -52,11 +57,11 @@ $red-light: #F43263;
$red-normal: #E52C5A; $red-normal: #E52C5A;
$red-dark: #D22852; $red-dark: #D22852;
$border-white-light: #E3E7EC; $border-white-light: #F1F2F4;
$border-white-normal: #D6DAE2; $border-white-normal: #D6DAE2;
$border-white-dark: #C6CACF; $border-white-dark: #C6CACF;
$border-gray-light: #DCE0E5; $border-gray-light: #d1d1d1;
$border-gray-normal: #D6DAE2; $border-gray-normal: #D6DAE2;
$border-gray-dark: #C6CACF; $border-gray-dark: #C6CACF;
...@@ -76,6 +81,8 @@ $border-red-light: #E52C5A; ...@@ -76,6 +81,8 @@ $border-red-light: #E52C5A;
$border-red-normal: #D22852; $border-red-normal: #D22852;
$border-red-dark: #CA264F; $border-red-dark: #CA264F;
/* header */
$light-grey-header: #faf9f9;
/* /*
* State colors: * State colors:
......
...@@ -90,13 +90,19 @@ ...@@ -90,13 +90,19 @@
height: 300px; height: 300px;
overflow-y: scroll; overflow-y: scroll;
h4 { h5 {
clear: left; clear: left;
} }
ul { ul {
list-style-type: none; list-style-type: none;
margin-left: -20px; margin-left: -20px;
margin-bottom: 20px;
overflow: auto;
}
input.emoji-search{
background: image-url(/assets/icon-search.png) 240px no-repeat;
} }
li { li {
......
.detail-page-header {
margin: -$gl-padding;
padding: 7px $gl-padding;
margin-bottom: 0px;
border-bottom: 1px solid $border-color;
color: #5c5d5e;
font-size: 16px;
line-height: 34px;
.author {
color: #5c5d5e;
}
.identifier {
color: #5c5d5e;
}
}
.detail-page-description {
.title {
margin: 0;
font-size: 23px;
color: #313236;
}
.description {
margin-top: 6px;
p:last-child {
margin-bottom: 0;
}
}
}
...@@ -4,7 +4,7 @@ The source: gemojione gem. ...@@ -4,7 +4,7 @@ The source: gemojione gem.
*/ */
.emoji-icon{ .emoji-icon{
background-image: url(emoji.png); background-image: image-url(emoji.png);
background-repeat: no-repeat; background-repeat: no-repeat;
} }
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
&.affix { &.affix {
position: fixed; position: fixed;
top: 60px; top: 70px;
margin-right: 35px; margin-right: 35px;
} }
} }
...@@ -36,33 +36,12 @@ ...@@ -36,33 +36,12 @@
} }
.issuable-details { .issuable-details {
.issue-title {
margin: 0;
font-size: 23px;
color: #313236;
}
.description {
margin-top: 6px;
p:last-child {
margin-bottom: 0;
}
}
section { section {
border-right: 1px solid #ECEEF1; border-right: 1px solid $border-white-light;
> .tab-content { .issuable-discussion {
margin-right: 1px; margin-right: 1px;
} }
.issue-discussion > .gray-content-block,
> .gray-content-block {
margin-top: 0;
border-top: none;
margin-right: -15px;
}
} }
} }
...@@ -136,21 +115,3 @@ ...@@ -136,21 +115,3 @@
margin-right: 2px; margin-right: 2px;
} }
} }
.issuable-title {
margin: -$gl-padding;
padding: 7px $gl-padding;
margin-bottom: 0px;
border-bottom: 1px solid $border-color;
color: #5c5d5e;
font-size: 16px;
line-height: 42px;
.author {
color: #5c5d5e;
}
.issuable-id {
color: #5c5d5e;
}
}
...@@ -141,11 +141,6 @@ form.edit-issue { ...@@ -141,11 +141,6 @@ form.edit-issue {
} }
} }
.issue-closed-by-widget {
padding: 16px 0;
margin: 0px;
}
.issue-form .select2-container { .issue-form .select2-container {
width: 250px !important; width: 250px !important;
} }
...@@ -191,7 +191,7 @@ ...@@ -191,7 +191,7 @@
.btn-clipboard { .btn-clipboard {
@extend .pull-right; @extend .pull-right;
margin-right: 18px; margin-right: 20px;
margin-top: 5px; margin-top: 5px;
position: absolute; position: absolute;
right: 0; right: 0;
......
...@@ -75,17 +75,15 @@ ...@@ -75,17 +75,15 @@
.common-note-form { .common-note-form {
margin: 0; margin: 0;
background: #F7F8FA; background: #fff;
padding: $gl-padding; padding: $gl-padding;
margin-left: -$gl-padding; margin-left: -$gl-padding;
margin-right: -$gl-padding; margin-right: -$gl-padding;
border-right: 1px solid $border-color;
border-top: 1px solid $border-color;
margin-bottom: -$gl-padding; margin-bottom: -$gl-padding;
} }
.note-form-actions { .note-form-actions {
background: #F9F9F9; background: #fff;
.note-form-option { .note-form-option {
margin-top: 8px; margin-top: 8px;
......
...@@ -128,7 +128,7 @@ ul.notes { ...@@ -128,7 +128,7 @@ ul.notes {
} }
&:last-child { &:last-child {
border-bottom: none; border-bottom: 1px solid $border-color;
} }
} }
} }
......
...@@ -335,6 +335,36 @@ ul.nav.nav-projects-tabs { ...@@ -335,6 +335,36 @@ ul.nav.nav-projects-tabs {
} }
} }
.top-area {
border-bottom: 1px solid #EEE;
ul.left-top-menu {
display: inline-block;
width: 50%;
margin-bottom: 0px;
border-bottom: none;
}
.projects-search-form {
width: 50%;
display: inline-block;
float: right;
padding-top: 7px;
text-align: right;
.btn-green {
margin-top: -2px;
margin-left: 10px;
}
}
@media (max-width: $screen-xs-max) {
.projects-search-form {
padding-top: 15px;
}
}
}
.fork-namespaces { .fork-namespaces {
.fork-thumbnail { .fork-thumbnail {
text-align: center; text-align: center;
...@@ -412,11 +442,18 @@ pre.light-well { ...@@ -412,11 +442,18 @@ pre.light-well {
.projects-search-form { .projects-search-form {
margin: -$gl-padding; margin: -$gl-padding;
background-color: #f8fafc;
padding: $gl-padding; padding: $gl-padding;
margin-bottom: 0px; margin-bottom: 0px;
border-top: 1px solid #e7e9ed;
border-bottom: 1px solid #e7e9ed; input {
display: inline-block;
width: calc(100% - 151px);
}
.btn {
display: inline-block;
width: 135px;
}
} }
.git-empty { .git-empty {
......
...@@ -35,3 +35,20 @@ ...@@ -35,3 +35,20 @@
border-color: $gl-warning; border-color: $gl-warning;
} }
} }
.ci-status-icon-success {
@extend .cgreen;
}
.ci-status-icon-failed {
@extend .cred;
}
.ci-status-icon-running,
.ci-status-icon-pending {
// These are standard text color
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found,
.ci-status-icon-skipped {
@extend .cgray;
}
class Admin::IdentitiesController < Admin::ApplicationController class Admin::IdentitiesController < Admin::ApplicationController
before_action :user before_action :user
before_action :identity, except: :index before_action :identity, except: [:index, :new, :create]
def new
@identity = Identity.new
end
def create
@identity = Identity.new(identity_params)
@identity.user_id = user.id
if @identity.save
redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully created.'
else
render :new
end
end
def index def index
@identities = @user.identities @identities = @user.identities
......
...@@ -10,6 +10,7 @@ class ApplicationController < ActionController::Base ...@@ -10,6 +10,7 @@ class ApplicationController < ActionController::Base
before_action :authenticate_user_from_token! before_action :authenticate_user_from_token!
before_action :authenticate_user! before_action :authenticate_user!
before_action :validate_user_service_ticket!
before_action :reject_blocked! before_action :reject_blocked!
before_action :check_password_expiration before_action :check_password_expiration
before_action :ldap_security_check before_action :ldap_security_check
...@@ -202,6 +203,20 @@ class ApplicationController < ActionController::Base ...@@ -202,6 +203,20 @@ class ApplicationController < ActionController::Base
end end
end end
def validate_user_service_ticket!
return unless signed_in? && session[:service_tickets]
valid = session[:service_tickets].all? do |provider, ticket|
Gitlab::OAuth::Session.valid?(provider, ticket)
end
unless valid
session[:service_tickets] = nil
sign_out current_user
redirect_to new_user_session_path
end
end
def check_password_expiration def check_password_expiration
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user? if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
redirect_to new_profile_password_path and return redirect_to new_profile_password_path and return
......
...@@ -19,8 +19,10 @@ module Ci ...@@ -19,8 +19,10 @@ module Ci
@error = e.message @error = e.message
@status = false @status = false
rescue rescue
@error = "Undefined error" @error = 'Undefined error'
@status = false @status = false
ensure
render :show
end end
end end
end end
class Dashboard::SnippetsController < Dashboard::ApplicationController class Dashboard::SnippetsController < Dashboard::ApplicationController
def index def index
@snippets = SnippetsFinder.new.execute(current_user, @snippets = SnippetsFinder.new.execute(
current_user,
filter: :by_user, filter: :by_user,
user: current_user, user: current_user,
scope: params[:scope] scope: params[:scope]
......
class OmniauthCallbacksController < Devise::OmniauthCallbacksController class OmniauthCallbacksController < Devise::OmniauthCallbacksController
protect_from_forgery except: [:kerberos, :saml] protect_from_forgery except: [:kerberos, :saml, :cas3]
Gitlab.config.omniauth.providers.each do |provider| Gitlab.config.omniauth.providers.each do |provider|
define_method provider['name'] do define_method provider['name'] do
...@@ -42,6 +42,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -42,6 +42,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
render 'errors/omniauth_error', layout: "errors", status: 422 render 'errors/omniauth_error', layout: "errors", status: 422
end end
def cas3
ticket = params['ticket']
if ticket
handle_service_ticket oauth['provider'], ticket
end
handle_omniauth
end
private private
def handle_omniauth def handle_omniauth
...@@ -84,6 +92,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -84,6 +92,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to new_user_session_path redirect_to new_user_session_path
end end
def handle_service_ticket provider, ticket
Gitlab::OAuth::Session.create provider, ticket
session[:service_tickets] ||= {}
session[:service_tickets][provider] = ticket
end
def oauth def oauth
@oauth ||= request.env['omniauth.auth'] @oauth ||= request.env['omniauth.auth']
end end
......
...@@ -7,7 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -7,7 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds] 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] before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
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
...@@ -153,11 +153,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -153,11 +153,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def merge_check def merge_check
if @merge_request.unchecked? @merge_request.check_if_can_be_merged if @merge_request.unchecked?
@merge_request.check_if_can_be_merged
end
closes_issues
render partial: "projects/merge_requests/widget/show.html.haml", layout: false render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end end
...@@ -178,7 +174,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -178,7 +174,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds] && @merge_request.ci_commit && @merge_request.ci_commit.active? if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
.execute(@merge_request) .execute(@merge_request)
@status = :merge_when_build_succeeds @status = :merge_when_build_succeeds
...@@ -299,6 +295,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -299,6 +295,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_widget_vars def define_widget_vars
@ci_commit = @merge_request.ci_commit @ci_commit = @merge_request.ci_commit
closes_issues
end end
def invalid_mr def invalid_mr
......
...@@ -13,7 +13,8 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -13,7 +13,8 @@ class Projects::NotesController < Projects::ApplicationController
@notes.each do |note| @notes.each do |note|
notes_json[:notes] << { notes_json[:notes] << {
id: note.id, id: note.id,
html: note_to_html(note) html: note_to_html(note),
valid: note.valid?
} }
end end
...@@ -68,7 +69,7 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -68,7 +69,7 @@ class Projects::NotesController < Projects::ApplicationController
data = { data = {
author: current_user, author: current_user,
is_award: true, is_award: true,
note: note_params[:note].gsub(":", '') note: note_params[:note].delete(":")
} }
note = noteable.notes.find_by(data) note = noteable.notes.find_by(data)
......
...@@ -21,7 +21,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController ...@@ -21,7 +21,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
if protected_branch && if protected_branch &&
protected_branch.update_attributes( protected_branch.update_attributes(
developers_can_push: params[:developers_can_push] developers_can_push: params[:developers_can_push]
) )
respond_to do |format| respond_to do |format|
......
class Projects::ServicesController < Projects::ApplicationController class Projects::ServicesController < Projects::ApplicationController
ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_version, :subdomain, ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain,
:room, :recipients, :project_url, :webhook, :room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password, :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :drone_url, :build_type, :build_key, :server, :teamcity_url, :drone_url, :build_type,
...@@ -10,7 +10,8 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -10,7 +10,8 @@ class Projects::ServicesController < Projects::ApplicationController
:notify_only_broken_builds, :add_pusher, :notify_only_broken_builds, :add_pusher,
:send_from_committer_email, :disable_diffs, :external_wiki_url, :send_from_committer_email, :disable_diffs, :external_wiki_url,
:notify, :color, :notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification] :server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id]
# Parameters to ignore if no value is specified # Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password] FILTER_BLANK_PARAMS = [:password]
......
...@@ -61,7 +61,7 @@ module ApplicationHelper ...@@ -61,7 +61,7 @@ module ApplicationHelper
options[:class] ||= '' options[:class] ||= ''
options[:class] << ' identicon' options[:class] << ' identicon'
bg_key = project.id % 7 bg_key = project.id % 7
style = "background-color: ##{ allowed_colors.values[bg_key] }; color: #555" style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555"
content_tag(:div, class: options[:class], style: style) do content_tag(:div, class: options[:class], style: style) do
project.name[0, 1].upcase project.name[0, 1].upcase
...@@ -204,12 +204,16 @@ module ApplicationHelper ...@@ -204,12 +204,16 @@ module ApplicationHelper
# Returns an HTML-safe String # Returns an HTML-safe String
def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false) def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false)
element = content_tag :time, time.to_s, element = content_tag :time, time.to_s,
class: "#{html_class} js-timeago", class: "#{html_class} js-timeago js-timeago-pending",
datetime: time.getutc.iso8601, datetime: time.getutc.iso8601,
title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'), title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
data: { toggle: 'tooltip', placement: placement, container: 'body' } data: { toggle: 'tooltip', placement: placement, container: 'body' }
element += javascript_tag "$('.js-timeago').last().timeago()" unless skip_js unless skip_js
element << javascript_tag(
"$('.js-timeago-pending').removeClass('js-timeago-pending').timeago()"
)
end
element element
end end
......
...@@ -10,8 +10,8 @@ module ButtonHelper ...@@ -10,8 +10,8 @@ module ButtonHelper
# # => "<button class='...' data-clipboard-text='Foo'>...</button>" # # => "<button class='...' data-clipboard-text='Foo'>...</button>"
# #
# # Define the target element # # Define the target element
# clipboard_button(clipboard_target: "#foo") # clipboard_button(clipboard_target: "div#foo")
# # => "<button class='...' data-clipboard-target='#foo'>...</button>" # # => "<button class='...' data-clipboard-target='div#foo'>...</button>"
# #
# See http://clipboardjs.com/#usage # See http://clipboardjs.com/#usage
def clipboard_button(data = {}) def clipboard_button(data = {})
......
...@@ -12,19 +12,6 @@ module CiStatusHelper ...@@ -12,19 +12,6 @@ module CiStatusHelper
ci_label_for_status(ci_commit.status) ci_label_for_status(ci_commit.status)
end end
def ci_status_color(ci_commit)
case ci_commit.status
when 'success'
'green'
when 'failed'
'red'
when 'running', 'pending'
'yellow'
else
'gray'
end
end
def ci_status_with_icon(status) def ci_status_with_icon(status)
content_tag :span, class: "ci-status ci-#{status}" do content_tag :span, class: "ci-status ci-#{status}" do
ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status) ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status)
...@@ -56,12 +43,11 @@ module CiStatusHelper ...@@ -56,12 +43,11 @@ module CiStatusHelper
end end
def render_ci_status(ci_commit) def render_ci_status(ci_commit)
link_to ci_status_path(ci_commit), link_to ci_status_icon(ci_commit),
class: "ci-status-link c#{ci_status_color(ci_commit)}", ci_status_path(ci_commit),
class: "ci-status-link ci-status-icon-#{ci_commit.status.dasherize}",
title: "Build #{ci_status_label(ci_commit)}", title: "Build #{ci_status_label(ci_commit)}",
data: { toggle: 'tooltip', placement: 'left' } do data: { toggle: 'tooltip', placement: 'left' }
ci_status_icon(ci_commit)
end
end end
def no_runners_for_project?(project) def no_runners_for_project?(project)
......
module ExternalWikiHelper module ExternalWikiHelper
def get_project_wiki_path(project) def get_project_wiki_path(project)
external_wiki_service = project.services. external_wiki_service = project.services.
select { |service| service.to_param == 'external_wiki' }.first find { |service| service.to_param == 'external_wiki' }
if external_wiki_service.present? && external_wiki_service.active? if external_wiki_service.present? && external_wiki_service.active?
external_wiki_service.properties['external_wiki_url'] external_wiki_service.properties['external_wiki_url']
else else
......
...@@ -20,7 +20,7 @@ module GitlabMarkdownHelper ...@@ -20,7 +20,7 @@ module GitlabMarkdownHelper
end end
user = current_user if defined?(current_user) user = current_user if defined?(current_user)
gfm_body = Gitlab::Markdown.render(escaped_body, project: @project, current_user: user, pipeline: :single_line) gfm_body = Banzai.render(escaped_body, project: @project, current_user: user, pipeline: :single_line)
fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body) fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body)
if fragment.children.size == 1 && fragment.children[0].name == 'a' if fragment.children.size == 1 && fragment.children[0].name == 'a'
...@@ -50,7 +50,7 @@ module GitlabMarkdownHelper ...@@ -50,7 +50,7 @@ module GitlabMarkdownHelper
context[:project] ||= @project context[:project] ||= @project
html = Gitlab::Markdown.render(text, context) html = Banzai.render(text, context)
context.merge!( context.merge!(
current_user: (current_user if defined?(current_user)), current_user: (current_user if defined?(current_user)),
...@@ -61,11 +61,12 @@ module GitlabMarkdownHelper ...@@ -61,11 +61,12 @@ module GitlabMarkdownHelper
ref: @ref ref: @ref
) )
Gitlab::Markdown.post_process(html, context) Banzai.post_process(html, context)
end end
def asciidoc(text) def asciidoc(text)
Gitlab::Asciidoc.render(text, Gitlab::Asciidoc.render(
text,
project: @project, project: @project,
current_user: (current_user if defined?(current_user)), current_user: (current_user if defined?(current_user)),
......
...@@ -57,15 +57,15 @@ module IssuesHelper ...@@ -57,15 +57,15 @@ module IssuesHelper
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id) options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
end end
def issue_box_class(item) def status_box_class(item)
if item.respond_to?(:expired?) && item.expired? if item.respond_to?(:expired?) && item.expired?
'issue-box-expired' 'status-box-expired'
elsif item.respond_to?(:merged?) && item.merged? elsif item.respond_to?(:merged?) && item.merged?
'issue-box-merged' 'status-box-merged'
elsif item.closed? elsif item.closed?
'issue-box-closed' 'status-box-closed'
else else
'issue-box-open' 'status-box-open'
end end
end end
...@@ -94,12 +94,13 @@ module IssuesHelper ...@@ -94,12 +94,13 @@ module IssuesHelper
end.sort.to_sentence(last_word_connector: ', or ') end.sort.to_sentence(last_word_connector: ', or ')
end end
def emoji_icon(name, unicode = nil) def emoji_icon(name, unicode = nil, aliases = [])
unicode ||= Emoji.emoji_filename(name) unicode ||= Emoji.emoji_filename(name)
content_tag :div, "", content_tag :div, "",
class: "icon emoji-icon emoji-#{unicode}", class: "icon emoji-icon emoji-#{unicode}",
"data-emoji" => name, "data-emoji" => name,
"data-aliases" => aliases.join(" "),
"data-unicode-name" => unicode "data-unicode-name" => unicode
end end
...@@ -119,6 +120,6 @@ module IssuesHelper ...@@ -119,6 +120,6 @@ module IssuesHelper
end end
end end
# Required for Gitlab::Markdown::IssueReferenceFilter # Required for Banzai::Filter::IssueReferenceFilter
module_function :url_for_issue module_function :url_for_issue
end end
...@@ -107,6 +107,6 @@ module LabelsHelper ...@@ -107,6 +107,6 @@ module LabelsHelper
options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name]) options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name])
end end
# Required for Gitlab::Markdown::LabelReferenceFilter # Required for Banzai::Filter::LabelReferenceFilter
module_function :render_colored_label, :text_color_for_bg, :escape_once module_function :render_colored_label, :text_color_for_bg, :escape_once
end end
...@@ -27,7 +27,16 @@ module MergeRequestsHelper ...@@ -27,7 +27,16 @@ module MergeRequestsHelper
end end
def ci_build_details_path(merge_request) def ci_build_details_path(merge_request)
merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch) build_url = merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch)
return nil unless build_url
parsed_url = URI.parse(build_url)
unless parsed_url.userinfo.blank?
parsed_url.userinfo = ''
end
parsed_url.to_s
end end
def merge_path_description(merge_request, separator) def merge_path_description(merge_request, separator)
......
...@@ -105,6 +105,14 @@ module ProjectsHelper ...@@ -105,6 +105,14 @@ module ProjectsHelper
end end
end end
def user_max_access_in_project(user_id, project)
level = project.team.max_member_access(user_id)
if level
Gitlab::Access.options_with_owner.key(level)
end
end
private private
def get_project_nav_tabs(project, current_user) def get_project_nav_tabs(project, current_user)
...@@ -277,14 +285,6 @@ module ProjectsHelper ...@@ -277,14 +285,6 @@ module ProjectsHelper
end end
end end
def user_max_access_in_project(user, project)
level = project.team.max_member_access(user)
if level
Gitlab::Access.options_with_owner.key(level)
end
end
def leave_project_message(project) def leave_project_message(project)
"Are you sure you want to leave \"#{project.name}\" project?" "Are you sure you want to leave \"#{project.name}\" project?"
end end
...@@ -330,10 +330,9 @@ module ProjectsHelper ...@@ -330,10 +330,9 @@ module ProjectsHelper
def filename_path(project, filename) def filename_path(project, filename)
if project && blob = project.repository.send(filename) if project && blob = project.repository.send(filename)
namespace_project_blob_path( namespace_project_blob_path(
project.namespace, project.namespace,
project, project,
tree_join(project.default_branch, tree_join(project.default_branch, blob.name)
blob.name)
) )
end end
end end
......
...@@ -79,7 +79,7 @@ module TreeHelper ...@@ -79,7 +79,7 @@ module TreeHelper
part_path = File.join(part_path, part) unless part_path.empty? part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty? part_path = part if part_path.empty?
next unless parts.last(2).include?(part) if parts.count > max_links next if parts.count > max_links && !parts.last(2).include?(part)
yield(part, tree_join(@ref, part_path)) yield(part, tree_join(@ref, part_path))
end end
end end
......
...@@ -17,7 +17,7 @@ class Notify < BaseMailer ...@@ -17,7 +17,7 @@ class Notify < BaseMailer
subject: subject, subject: subject,
body: body.html_safe, body: body.html_safe,
content_type: 'text/html' content_type: 'text/html'
) )
end end
# Splits "gitlab.corp.company.com" up into "gitlab.corp.company.com", # Splits "gitlab.corp.company.com" up into "gitlab.corp.company.com",
......
...@@ -126,12 +126,16 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -126,12 +126,16 @@ class ApplicationSetting < ActiveRecord::Base
def restricted_signup_domains_raw=(values) def restricted_signup_domains_raw=(values)
self.restricted_signup_domains = [] self.restricted_signup_domains = []
self.restricted_signup_domains = values.split( self.restricted_signup_domains = values.split(
/\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace /\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
| # or | # or
\s # any whitespace character \s # any whitespace character
| # or | # or
[\r\n] # any number of newline characters [\r\n] # any number of newline characters
/x) /x)
self.restricted_signup_domains.reject! { |d| d.empty? } self.restricted_signup_domains.reject! { |d| d.empty? }
end end
def runners_registration_token
ensure_runners_registration_token!
end
end end
...@@ -135,6 +135,16 @@ module Ci ...@@ -135,6 +135,16 @@ module Ci
predefined_variables + yaml_variables + project_variables + trigger_variables predefined_variables + yaml_variables + project_variables + trigger_variables
end end
def merge_request
merge_requests = MergeRequest.includes(:merge_request_diff)
.where(source_branch: ref, source_project_id: commit.gl_project_id)
.reorder(iid: :asc)
merge_requests.find do |merge_request|
merge_request.commits.any? { |ci| ci.id == commit.sha }
end
end
def project def project
commit.project commit.project
end end
...@@ -170,7 +180,8 @@ module Ci ...@@ -170,7 +180,8 @@ module Ci
def extract_coverage(text, regex) def extract_coverage(text, regex)
begin begin
matches = text.gsub(Regexp.new(regex)).to_a.last matches = text.scan(Regexp.new(regex)).last
matches = matches.last if matches.kind_of?(Array)
coverage = matches.gsub(/\d+(\.\d+)?/).first coverage = matches.gsub(/\d+(\.\d+)?/).first
if coverage.present? if coverage.present?
......
...@@ -218,16 +218,6 @@ module Ci ...@@ -218,16 +218,6 @@ module Ci
update!(committed_at: DateTime.now) update!(committed_at: DateTime.now)
end end
##
# This method checks if build status should be displayed.
#
# Build status should be available only if builds are enabled
# on project level and `.gitlab-ci.yml` file is present.
#
def show_build_status?
project.builds_enabled? && ci_yaml_file
end
private private
def save_yaml_error(error) def save_yaml_error(error)
......
...@@ -23,7 +23,7 @@ module Mentionable ...@@ -23,7 +23,7 @@ module Mentionable
included do included do
if self < Participable if self < Participable
participant ->(current_user) { mentioned_users(current_user, load_lazy_references: false) } participant ->(current_user) { mentioned_users(current_user) }
end end
end end
...@@ -43,15 +43,15 @@ module Mentionable ...@@ -43,15 +43,15 @@ module Mentionable
self self
end end
def all_references(current_user = self.author, text = nil, load_lazy_references: true) def all_references(current_user = self.author, text = nil)
ext = Gitlab::ReferenceExtractor.new(self.project, current_user, load_lazy_references: load_lazy_references) ext = Gitlab::ReferenceExtractor.new(self.project, current_user)
if text if text
ext.analyze(text) ext.analyze(text)
else else
self.class.mentionable_attrs.each do |attr, options| self.class.mentionable_attrs.each do |attr, options|
text = send(attr) text = send(attr)
options[:cache_key] = [self, attr] if options.delete(:cache) options[:cache_key] = [self, attr] if options.delete(:cache) && self.persisted?
ext.analyze(text, options) ext.analyze(text, options)
end end
end end
...@@ -59,13 +59,13 @@ module Mentionable ...@@ -59,13 +59,13 @@ module Mentionable
ext ext
end end
def mentioned_users(current_user = nil, load_lazy_references: true) def mentioned_users(current_user = nil)
all_references(current_user, load_lazy_references: load_lazy_references).users all_references(current_user).users
end end
# Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference.
def referenced_mentionables(current_user = self.author, text = nil, load_lazy_references: true) def referenced_mentionables(current_user = self.author, text = nil)
refs = all_references(current_user, text, load_lazy_references: load_lazy_references) refs = all_references(current_user, text)
refs = (refs.issues + refs.merge_requests + refs.commits) refs = (refs.issues + refs.merge_requests + refs.commits)
# We're using this method instead of Array diffing because that requires # We're using this method instead of Array diffing because that requires
......
...@@ -37,21 +37,22 @@ module Participable ...@@ -37,21 +37,22 @@ module Participable
# Be aware that this method makes a lot of sql queries. # Be aware that this method makes a lot of sql queries.
# Save result into variable if you are going to reuse it inside same request # Save result into variable if you are going to reuse it inside same request
def participants(current_user = self.author, load_lazy_references: true) def participants(current_user = self.author)
participants = self.class.participant_attrs.flat_map do |attr| participants =
value = Gitlab::ReferenceExtractor.lazily do
if attr.respond_to?(:call) self.class.participant_attrs.flat_map do |attr|
instance_exec(current_user, &attr) value =
else if attr.respond_to?(:call)
send(attr) instance_exec(current_user, &attr)
end else
send(attr)
end
participants_for(value, current_user) participants_for(value, current_user)
end.compact.uniq end.compact.uniq
end
if load_lazy_references
participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq
unless Gitlab::ReferenceExtractor.lazy?
participants.select! do |user| participants.select! do |user|
user.can?(:read_project, project) user.can?(:read_project, project)
end end
...@@ -64,12 +65,12 @@ module Participable ...@@ -64,12 +65,12 @@ module Participable
def participants_for(value, current_user = nil) def participants_for(value, current_user = nil)
case value case value
when User, Gitlab::Markdown::ReferenceFilter::LazyReference when User, Banzai::LazyReference
[value] [value]
when Enumerable, ActiveRecord::Relation when Enumerable, ActiveRecord::Relation
value.flat_map { |v| participants_for(v, current_user) } value.flat_map { |v| participants_for(v, current_user) }
when Participable when Participable
value.participants(current_user, load_lazy_references: false) value.participants(current_user)
end end
end end
end end
...@@ -13,20 +13,21 @@ module TokenAuthenticatable ...@@ -13,20 +13,21 @@ module TokenAuthenticatable
@token_fields << token_field @token_fields << token_field
define_singleton_method("find_by_#{token_field}") do |token| define_singleton_method("find_by_#{token_field}") do |token|
where(token_field => token).first if token find_by(token_field => token) if token
end end
define_method("ensure_#{token_field}") do define_method("ensure_#{token_field}") do
current_token = read_attribute(token_field) current_token = read_attribute(token_field)
if current_token.blank? current_token.blank? ? write_new_token(token_field) : current_token
write_attribute(token_field, generate_token_for(token_field)) end
else
current_token define_method("ensure_#{token_field}!") do
end send("reset_#{token_field}!") if read_attribute(token_field).blank?
read_attribute(token_field)
end end
define_method("reset_#{token_field}!") do define_method("reset_#{token_field}!") do
write_attribute(token_field, generate_token_for(token_field)) write_new_token(token_field)
save! save!
end end
end end
...@@ -34,10 +35,15 @@ module TokenAuthenticatable ...@@ -34,10 +35,15 @@ module TokenAuthenticatable
private private
def generate_token_for(token_field) def write_new_token(token_field)
new_token = generate_token(token_field)
write_attribute(token_field, new_token)
end
def generate_token(token_field)
loop do loop do
token = Devise.friendly_token token = Devise.friendly_token
break token unless self.class.unscoped.where(token_field => token).first break token unless self.class.unscoped.find_by(token_field => token)
end end
end end
end end
...@@ -84,11 +84,11 @@ class Issue < ActiveRecord::Base ...@@ -84,11 +84,11 @@ class Issue < ActiveRecord::Base
end end
def referenced_merge_requests def referenced_merge_requests
references = [self, *notes].flat_map do |note| Gitlab::ReferenceExtractor.lazily do
note.all_references(load_lazy_references: false).merge_requests [self, *notes].flat_map do |note|
end.uniq note.all_references.merge_requests
end
Gitlab::Markdown::ReferenceFilter::LazyReference.load(references).uniq.sort_by(&:iid) end.sort_by(&:iid)
end end
# Reset issue events cache # Reset issue events cache
......
class JiraIssue < ExternalIssue
end
...@@ -194,9 +194,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -194,9 +194,7 @@ class MergeRequest < ActiveRecord::Base
similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
if similar_mrs.any? if similar_mrs.any?
errors.add :validate_branches, errors.add :validate_branches,
"Cannot Create: This merge request already exists: #{ "Cannot Create: This merge request already exists: #{similar_mrs.pluck(:title)}"
similar_mrs.pluck(:title)
}"
end end
end end
end end
...@@ -337,7 +335,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -337,7 +335,7 @@ class MergeRequest < ActiveRecord::Base
issues = commits.flat_map { |c| c.closes_issues(current_user) } issues = commits.flat_map { |c| c.closes_issues(current_user) }
issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user). issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user).
closed_by_message(description)) closed_by_message(description))
issues.uniq issues.uniq(&:id)
else else
[] []
end end
......
...@@ -45,7 +45,7 @@ class Namespace < ActiveRecord::Base ...@@ -45,7 +45,7 @@ class Namespace < ActiveRecord::Base
class << self class << self
def by_path(path) def by_path(path)
where('lower(path) = :value', value: path.downcase).first find_by('lower(path) = :value', value: path.downcase)
end end
# Case insensetive search for namespace by path or name # Case insensetive search for namespace by path or name
...@@ -148,6 +148,6 @@ class Namespace < ActiveRecord::Base ...@@ -148,6 +148,6 @@ class Namespace < ActiveRecord::Base
end end
def find_fork_of(project) def find_fork_of(project)
projects.joins(:forked_project_link).where('forked_project_links.forked_from_project_id = ?', project.id).first projects.joins(:forked_project_link).find_by('forked_project_links.forked_from_project_id = ?', project.id)
end end
end end
...@@ -373,11 +373,11 @@ class Note < ActiveRecord::Base ...@@ -373,11 +373,11 @@ class Note < ActiveRecord::Base
end end
def contains_emoji_only? def contains_emoji_only?
note =~ /\A#{Gitlab::Markdown::EmojiFilter.emoji_pattern}\s?\Z/ note =~ /\A#{Banzai::Filter::EmojiFilter.emoji_pattern}\s?\Z/
end end
def award_emoji_name def award_emoji_name
original_name = note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1] original_name = note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
AwardEmoji.normilize_emoji_name(original_name) AwardEmoji.normilize_emoji_name(original_name)
end end
end end
...@@ -265,7 +265,7 @@ class Project < ActiveRecord::Base ...@@ -265,7 +265,7 @@ class Project < ActiveRecord::Base
joins(:namespace). joins(:namespace).
iwhere('namespaces.path' => namespace_path) iwhere('namespaces.path' => namespace_path)
projects.where('projects.path' => project_path).take || projects.find_by('projects.path' => project_path) ||
projects.iwhere('projects.path' => project_path).take projects.iwhere('projects.path' => project_path).take
end end
...@@ -450,7 +450,7 @@ class Project < ActiveRecord::Base ...@@ -450,7 +450,7 @@ class Project < ActiveRecord::Base
end end
def external_issue_tracker def external_issue_tracker
@external_issues_tracker ||= external_issues_trackers.select(&:activated?).first @external_issues_tracker ||= external_issues_trackers.find(&:activated?)
end end
def can_have_issues_tracker_id? def can_have_issues_tracker_id?
...@@ -496,7 +496,11 @@ class Project < ActiveRecord::Base ...@@ -496,7 +496,11 @@ class Project < ActiveRecord::Base
end end
def ci_service def ci_service
@ci_service ||= ci_services.select(&:activated?).first @ci_service ||= ci_services.find(&:activated?)
end
def jira_tracker?
issues_tracker.to_param == 'jira'
end end
def avatar_type def avatar_type
...@@ -547,7 +551,7 @@ class Project < ActiveRecord::Base ...@@ -547,7 +551,7 @@ class Project < ActiveRecord::Base
end end
def project_member_by_name_or_email(name = nil, email = nil) def project_member_by_name_or_email(name = nil, email = nil)
user = users.where('name like ? or email like ?', name, email).first user = users.find_by('name like ? or email like ?', name, email)
project_members.where(user: user) if user project_members.where(user: user) if user
end end
...@@ -722,7 +726,7 @@ class Project < ActiveRecord::Base ...@@ -722,7 +726,7 @@ class Project < ActiveRecord::Base
end end
def project_member(user) def project_member(user)
project_members.where(user_id: user).first project_members.find_by(user_id: user)
end end
def default_branch def default_branch
...@@ -799,6 +803,10 @@ class Project < ActiveRecord::Base ...@@ -799,6 +803,10 @@ class Project < ActiveRecord::Base
false false
end end
def jira_tracker_active?
jira_tracker? && jira_service.active
end
def ci_commit(sha) def ci_commit(sha)
ci_commits.find_by(sha: sha) ci_commits.find_by(sha: sha)
end end
...@@ -850,4 +858,8 @@ class Project < ActiveRecord::Base ...@@ -850,4 +858,8 @@ class Project < ActiveRecord::Base
def build_timeout_in_minutes=(value) def build_timeout_in_minutes=(value)
self.build_timeout = value.to_i * 60 self.build_timeout = value.to_i * 60
end end
def open_issues_count
issues.opened.count
end
end end
...@@ -27,12 +27,10 @@ class BambooService < CiService ...@@ -27,12 +27,10 @@ class BambooService < CiService
validates :build_key, presence: true, if: :activated? validates :build_key, presence: true, if: :activated?
validates :username, validates :username,
presence: true, presence: true,
if: ->(service) { service.password? }, if: ->(service) { service.activated? && service.password }
if: :activated?
validates :password, validates :password,
presence: true, presence: true,
if: ->(service) { service.username? }, if: ->(service) { service.activated? && service.username }
if: :activated?
attr_accessor :response attr_accessor :response
......
...@@ -58,6 +58,6 @@ class FlowdockService < Service ...@@ -58,6 +58,6 @@ class FlowdockService < Service
repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}", repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}",
commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s", commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s", diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s",
) )
end end
end end
...@@ -57,6 +57,6 @@ class GemnasiumService < Service ...@@ -57,6 +57,6 @@ class GemnasiumService < Service
token: token, token: token,
api_key: api_key, api_key: api_key,
repo: project.repository.path_to_repo repo: project.repository.path_to_repo
) )
end end
end end
...@@ -18,6 +18,11 @@ ...@@ -18,6 +18,11 @@
# note_events :boolean default(TRUE), not null # note_events :boolean default(TRUE), not null
# #
# TODO(ayufan): The GitLabCiService is deprecated and the type should be removed when the database entries are removed
class GitlabCiService < CiService class GitlabCiService < CiService
# this is no longer used # We override the active accessor to always make GitLabCiService disabled
# Otherwise the GitLabCiService can be picked, but should never be since it's deprecated
def active
false
end
end end
...@@ -19,9 +19,24 @@ ...@@ -19,9 +19,24 @@
# #
class JiraService < IssueTrackerService class JiraService < IssueTrackerService
include HTTParty
include Gitlab::Application.routes.url_helpers include Gitlab::Application.routes.url_helpers
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url DEFAULT_API_VERSION = 2
prop_accessor :username, :password, :api_url, :jira_issue_transition_id,
:title, :description, :project_url, :issues_url, :new_issue_url
before_validation :set_api_url, :set_jira_issue_transition_id
before_update :reset_password
def reset_password
# don't reset the password if a new one is provided
if api_url_changed? && !password_touched?
self.password = nil
end
end
def help def help
line1 = 'Setting `project_url`, `issues_url` and `new_issue_url` will '\ line1 = 'Setting `project_url`, `issues_url` and `new_issue_url` will '\
...@@ -54,4 +69,228 @@ class JiraService < IssueTrackerService ...@@ -54,4 +69,228 @@ class JiraService < IssueTrackerService
def to_param def to_param
'jira' 'jira'
end end
def fields
super.push(
{ type: 'text', name: 'api_url', placeholder: 'https://jira.example.com/rest/api/2' },
{ type: 'text', name: 'username', placeholder: '' },
{ type: 'password', name: 'password', placeholder: '' },
{ type: 'text', name: 'jira_issue_transition_id', placeholder: '2' }
)
end
def execute(push, issue = nil)
if issue.nil?
# No specific issue, that means
# we just want to test settings
test_settings
else
close_issue(push, issue)
end
end
def create_cross_reference_note(mentioned, noteable, author)
issue_name = mentioned.id
project = self.project
noteable_name = noteable.class.name.underscore.downcase
noteable_id = if noteable.is_a?(Commit)
noteable.id
else
noteable.iid
end
entity_url = build_entity_url(noteable_name.to_sym, noteable_id)
data = {
user: {
name: author.name,
url: resource_url(user_path(author)),
},
project: {
name: project.path_with_namespace,
url: resource_url(namespace_project_path(project.namespace, project))
},
entity: {
name: noteable_name.humanize.downcase,
url: entity_url
}
}
add_comment(data, issue_name)
end
def test_settings
result = JiraService.get(
jira_api_test_url,
headers: {
'Content-Type' => 'application/json',
'Authorization' => "Basic #{auth}"
}
)
case result.code
when 201, 200
Rails.logger.info("#{self.class.name} SUCCESS #{result.code}: Successfully connected to #{api_url}.")
true
else
Rails.logger.info("#{self.class.name} ERROR #{result.code}: #{result.parsed_response}")
false
end
rescue Errno::ECONNREFUSED => e
Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{api_url}."
false
end
private
def build_api_url_from_project_url
server = URI(project_url)
default_ports = [["http",80],["https",443]].include?([server.scheme,server.port])
server_url = "#{server.scheme}://#{server.host}"
server_url.concat(":#{server.port}") unless default_ports
"#{server_url}/rest/api/#{DEFAULT_API_VERSION}"
rescue
"" # looks like project URL was not valid
end
def set_api_url
self.api_url = build_api_url_from_project_url if self.api_url.blank?
end
def set_jira_issue_transition_id
self.jira_issue_transition_id ||= "2"
end
def close_issue(entity, issue)
commit_id = if entity.is_a?(Commit)
entity.id
elsif entity.is_a?(MergeRequest)
entity.last_commit.id
end
commit_url = build_entity_url(:commit, commit_id)
# Depending on the JIRA project's workflow, a comment during transition
# may or may not be allowed. Split the operation in to two calls so the
# comment always works.
transition_issue(issue)
add_issue_solved_comment(issue, commit_id, commit_url)
end
def transition_issue(issue)
message = {
transition: {
id: jira_issue_transition_id
}
}
send_message(close_issue_url(issue.iid), message.to_json)
end
def add_issue_solved_comment(issue, commit_id, commit_url)
comment = {
body: "Issue solved with [#{commit_id}|#{commit_url}]."
}
send_message(comment_url(issue.iid), comment.to_json)
end
def add_comment(data, issue_name)
url = comment_url(issue_name)
user_name = data[:user][:name]
user_url = data[:user][:url]
entity_name = data[:entity][:name]
entity_url = data[:entity][:url]
project_name = data[:project][:name]
message = {
body: "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]."
}
unless existing_comment?(issue_name, message[:body])
send_message(url, message.to_json)
end
end
def auth
require 'base64'
Base64.urlsafe_encode64("#{self.username}:#{self.password}")
end
def send_message(url, message)
result = JiraService.post(
url,
body: message,
headers: {
'Content-Type' => 'application/json',
'Authorization' => "Basic #{auth}"
}
)
message = case result.code
when 201, 200, 204
"#{self.class.name} SUCCESS #{result.code}: Successfully posted to #{url}."
when 401
"#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again."
else
"#{self.class.name} ERROR #{result.code}: #{result.parsed_response}"
end
Rails.logger.info(message)
message
rescue URI::InvalidURIError, Errno::ECONNREFUSED => e
Rails.logger.info "#{self.class.name} ERROR: #{e.message}. Hostname: #{url}."
end
def existing_comment?(issue_name, new_comment)
result = JiraService.get(
comment_url(issue_name),
headers: {
'Content-Type' => 'application/json',
'Authorization' => "Basic #{auth}"
}
)
case result.code
when 201, 200
existing_comments = JSON.parse(result.body)['comments']
if existing_comments.present?
return existing_comments.map { |comment| comment['body'].include?(new_comment) }.any?
end
end
false
rescue JSON::ParserError
false
end
def resource_url(resource)
"#{Settings.gitlab['url'].chomp("/")}#{resource}"
end
def build_entity_url(entity_name, entity_id)
resource_url(
polymorphic_url(
[
self.project.namespace.becomes(Namespace),
self.project,
entity_name
],
id: entity_id,
routing_type: :path
)
)
end
def close_issue_url(issue_name)
"#{self.api_url}/issue/#{issue_name}/transitions"
end
def comment_url(issue_name)
"#{self.api_url}/issue/#{issue_name}/comment"
end
def jira_api_test_url
"#{self.api_url}/myself"
end
end end
...@@ -27,12 +27,10 @@ class TeamcityService < CiService ...@@ -27,12 +27,10 @@ class TeamcityService < CiService
validates :build_type, presence: true, if: :activated? validates :build_type, presence: true, if: :activated?
validates :username, validates :username,
presence: true, presence: true,
if: ->(service) { service.password? }, if: ->(service) { service.activated? && service.password }
if: :activated?
validates :password, validates :password,
presence: true, presence: true,
if: ->(service) { service.username? }, if: ->(service) { service.activated? && service.username }
if: :activated?
attr_accessor :response attr_accessor :response
...@@ -147,6 +145,6 @@ class TeamcityService < CiService ...@@ -147,6 +145,6 @@ class TeamcityService < CiService
'</build>', '</build>',
headers: { 'Content-type' => 'application/xml' }, headers: { 'Content-type' => 'application/xml' },
basic_auth: auth basic_auth: auth
) )
end end
end end
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
# bio :string(255) # bio :string(255)
# failed_attempts :integer default(0) # failed_attempts :integer default(0)
# locked_at :datetime # locked_at :datetime
# unlock_token :string(255)
# username :string(255) # username :string(255)
# can_create_group :boolean default(TRUE), not null # can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null # can_create_team :boolean default(TRUE), not null
...@@ -220,9 +221,9 @@ class User < ActiveRecord::Base ...@@ -220,9 +221,9 @@ class User < ActiveRecord::Base
def find_for_database_authentication(warden_conditions) def find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup conditions = warden_conditions.dup
if login = conditions.delete(:login) if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { value: login.downcase }]).first where(conditions).find_by("lower(username) = :value OR lower(email) = :value", value: login.downcase)
else else
where(conditions).first find_by(conditions)
end end
end end
...@@ -285,7 +286,7 @@ class User < ActiveRecord::Base ...@@ -285,7 +286,7 @@ class User < ActiveRecord::Base
end end
def by_username_or_id(name_or_id) def by_username_or_id(name_or_id)
where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first find_by('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i)
end end
def build_user(attrs = {}) def build_user(attrs = {})
......
...@@ -16,9 +16,23 @@ class CreateCommitBuildsService ...@@ -16,9 +16,23 @@ class CreateCommitBuildsService
return false return false
end end
tag = Gitlab::Git.tag_ref?(origin_ref) commit = project.ci_commit(sha)
commit = project.ensure_ci_commit(sha) unless commit
commit = project.ci_commits.new(sha: sha)
# Skip creating ci_commit when no gitlab-ci.yml is found
unless commit.ci_yaml_file
return false
end
# Create a new ci_commit
commit.save!
end
# Skip creating builds for commits that have [ci skip]
unless commit.skip_ci? unless commit.skip_ci?
# Create builds for commit
tag = Gitlab::Git.tag_ref?(origin_ref)
commit.update_committed! commit.update_committed!
commit.create_builds(ref, tag, user) commit.create_builds(ref, tag, user)
end end
......
module Issues module Issues
class CloseService < Issues::BaseService class CloseService < Issues::BaseService
def execute(issue, commit = nil) def execute(issue, commit = nil)
if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue)
return issue
end
if project.default_issues_tracker? && issue.close if project.default_issues_tracker? && issue.close
event_service.close_issue(issue, current_user) event_service.close_issue(issue, current_user)
create_note(issue, commit) create_note(issue, commit)
......
...@@ -112,7 +112,7 @@ module MergeRequests ...@@ -112,7 +112,7 @@ module MergeRequests
merge_requests_for_source_branch.each do |merge_request| merge_requests_for_source_branch.each do |merge_request|
SystemNoteService.change_branch_presence( SystemNoteService.change_branch_presence(
merge_request, merge_request.project, @current_user, merge_request, merge_request.project, @current_user,
:source, @branch_name, presence) :source, @branch_name, presence)
end end
end end
......
...@@ -241,9 +241,14 @@ class SystemNoteService ...@@ -241,9 +241,14 @@ class SystemNoteService
note_options.merge!(noteable: noteable) note_options.merge!(noteable: noteable)
end end
create_note(note_options) if noteable.is_a?(ExternalIssue)
noteable.project.issues_tracker.create_cross_reference_note(noteable, mentioner, author)
else
create_note(note_options)
end
end end
def self.cross_reference?(note_text) def self.cross_reference?(note_text)
note_text.start_with?(cross_reference_note_prefix) note_text.start_with?(cross_reference_note_prefix)
end end
...@@ -259,7 +264,7 @@ class SystemNoteService ...@@ -259,7 +264,7 @@ class SystemNoteService
# #
# Returns Boolean # Returns Boolean
def self.cross_reference_disallowed?(noteable, mentioner) def self.cross_reference_disallowed?(noteable, mentioner)
return true if noteable.is_a?(ExternalIssue) return true if noteable.is_a?(ExternalIssue) && !noteable.project.jira_tracker_active?
return false unless mentioner.is_a?(MergeRequest) return false unless mentioner.is_a?(MergeRequest)
return false unless noteable.is_a?(Commit) return false unless noteable.is_a?(Commit)
......
...@@ -79,6 +79,10 @@ ...@@ -79,6 +79,10 @@
GitLab API GitLab API
%span.pull-right %span.pull-right
= API::API::version = API::API::version
%p
Git
%span.pull-right
= Gitlab::Git.version
%p %p
Ruby Ruby
%span.pull-right %span.pull-right
......
- page_title "Identities", @user.name, "Users" - page_title "Identities", @user.name, "Users"
= render 'admin/users/head' = render 'admin/users/head'
= link_to 'New Identity', new_admin_user_identity_path, class: 'pull-right btn btn-new'
- if @identities.present? - if @identities.present?
.table-holder .table-holder
%table.table %table.table
......
- page_title "New Identity"
%h3.page-title New identity
%hr
= render 'form'
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
To register a new runner you should enter the following registration token. To register a new runner you should enter the following registration token.
With this token the runner will request a unique runner token and use that for future communication. With this token the runner will request a unique runner token and use that for future communication.
Registration token is Registration token is
%code{ id: 'runners-token' } #{current_application_settings.ensure_runners_registration_token} %code{ id: 'runners-token' } #{current_application_settings.runners_registration_token}
.bs-callout.clearfix .bs-callout.clearfix
.pull-left .pull-left
......
...@@ -41,5 +41,3 @@ ...@@ -41,5 +41,3 @@
%i.fa.fa-remove.incorrect-syntax %i.fa.fa-remove.incorrect-syntax
%b Error: %b Error:
= @error = @error
:plain
$(".results").html("#{escape_javascript(render "create")}")
\ No newline at end of file
%h2 Check your .gitlab-ci.yml %h2 Check your .gitlab-ci.yml
%hr %hr
= form_tag ci_lint_path, method: :post, remote: true do .row
.control-group = form_tag ci_lint_path, method: :post do
= label_tag :content, "Content of .gitlab-ci.yml", class: 'control-label' .form-group
.controls = label_tag :content, 'Content of .gitlab-ci.yml', class: 'control-label text-nowrap'
= text_area_tag :content, nil, class: 'form-control span1', rows: 7, require: true .col-sm-12
= text_area_tag :content, nil, class: 'form-control span1', rows: 7, require: true
.col-sm-12
.pull-left.prepend-top-10
= submit_tag 'Validate', class: 'btn btn-success submit-yml'
.control-group.clearfix .row.prepend-top-20
.controls.pull-left.prepend-top-10 .col-sm-12
= submit_tag "Validate", class: 'btn btn-success submit-yml' .results
= render partial: 'create' if defined?(@status)
%p.text-center.loading
%i.fa.fa-refresh.fa-spin
.results.prepend-top-20
:javascript
$(".loading").hide();
$('form').bind('ajax:beforeSend', function() {
$(".loading").show();
});
$('form').bind('ajax:complete', function() {
$(".loading").hide();
});
= content_for :flash_message do = content_for :flash_message do
= render 'shared/project_limit' = render 'shared/project_limit'
.top-area
%ul.left-top-menu
= nav_link(page: [dashboard_projects_path, root_path]) do
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
Your Projects
= nav_link(page: starred_dashboard_projects_path) do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
Starred Projects
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path], html_options: { class: 'hidden-xs' }) do
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
Explore Projects
%ul.center-top-menu .projects-search-form
= nav_link(page: [dashboard_projects_path, root_path]) do = search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs', spellcheck: false
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do - if current_user.can_create_project?
Your Projects = link_to new_project_path, class: 'btn btn-green' do
= nav_link(page: starred_dashboard_projects_path) do %i.fa.fa-plus
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do New Project
Starred Projects
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path], html_options: { class: 'hidden-xs' }) do
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
Explore Projects
- page_title @milestone.title, "Milestones" - page_title @milestone.title, "Milestones"
- header_title "Milestones", dashboard_milestones_path - header_title "Milestones", dashboard_milestones_path
.issuable-details .detail-page-header
.page-title .status-box{ class: "status-box-#{@milestone.closed? ? 'closed' : 'open'}" }
.issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } - if @milestone.closed?
- if @milestone.closed? Closed
Closed - else
- else Open
Open %span.identifier
Milestone #{@milestone.title} Milestone #{@milestone.title}
.gray-content-block.middle-block .detail-page-description.gray-content-block.second-block
%h2.issue-title %h2.title
= markdown escape_once(@milestone.title), pipeline: :single_line = markdown escape_once(@milestone.title), pipeline: :single_line
- if @milestone.complete? && @milestone.active? - if @milestone.complete? && @milestone.active?
.alert.alert-success.prepend-top-default .alert.alert-success.prepend-top-default
......
.projects-list-holder .projects-list-holder
.projects-search-form
.input-group
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
- if current_user.can_create_project?
%span.input-group-btn
= link_to new_project_path, class: 'btn btn-green' do
%i.fa.fa-plus
New Project
= render 'shared/projects/list', projects: @projects, ci: true = render 'shared/projects/list', projects: @projects, ci: true
<p>Hello <%= @resource.email %>!</p>
<p>Your account has been locked due to an excessive amount of unsuccessful sign in attempts.</p>
<p>Click the link below to unlock your account:</p>
<p><%= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token) %></p>
%p
Hello #{@resource.name}!
%p
Your GitLab account has been locked due to an excessive amount of unsuccessful
sign in attempts. Your account will automatically unlock in
= time_ago_in_words(Devise.unlock_in.from_now)
or you may click the link below to unlock now.
%p= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token)
<h2>Resend unlock instructions</h2>
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<div><%= f.submit "Resend unlock instructions" %></div>
<% end %>
<%= render partial: "devise/shared/links" %>
.login-box
.login-heading
%h3 Resend unlock email
.login-body
= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f|
.devise-errors
= devise_error_messages!
.clearfix.append-bottom-20
= f.email_field :email, class: 'form-control', placeholder: 'Email', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off'
.clearfix
= f.submit 'Resend unlock instructions', class: 'btn btn-success'
.clearfix.prepend-top-20
= render 'devise/shared/sign_in_link'
.panel.panel-default.projects-list-holder .projects-list-holder
.panel-heading.clearfix .projects-search-form
.input-group .input-group
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
- if can? current_user, :create_projects, @group - if can? current_user, :create_projects, @group
......
- page_title @milestone.title, "Milestones" - page_title @milestone.title, "Milestones"
= render "header_title" = render "header_title"
.issuable-details .detail-page-header
.page-title .status-box{ class: "status-box-#{@milestone.closed? ? 'closed' : 'open'}" }
.issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } - if @milestone.closed?
- if @milestone.closed? Closed
Closed - else
- else Open
Open %span.identifier
Milestone #{@milestone.title} Milestone #{@milestone.title}
.pull-right .pull-right
- if can?(current_user, :admin_milestones, @group) - if can?(current_user, :admin_milestones, @group)
- if @milestone.active? - if @milestone.active?
= link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close"
- else - else
= link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
.gray-content-block.middle-block .detail-page-description.gray-content-block.second-block
%h2.issue-title %h2.title
= markdown escape_once(@milestone.title), pipeline: :single_line = markdown escape_once(@milestone.title), pipeline: :single_line
- if @milestone.complete? && @milestone.active? - if @milestone.complete? && @milestone.active?
.alert.alert-success.prepend-top-default .alert.alert-success.prepend-top-default
......
...@@ -5,37 +5,47 @@ ...@@ -5,37 +5,47 @@
- if current_user - if current_user
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
.dashboard .cover-block
.header-with-avatar.clearfix .avatar-holder
= image_tag group_icon(@group), class: "avatar group-avatar s90" = link_to group_icon(@group), target: '_blank' do
%h3 = image_tag group_icon(@group), class: "avatar group-avatar s90"
= @group.name .cover-title
.username = @group.name
@#{@group.path}
- if @group.description.present? .cover-desc.username
.description @#{@group.path}
= markdown(@group.description, pipeline: :description)
%hr - if @group.description.present?
.cover-desc.description
= render 'shared/show_aside' = markdown(@group.description, pipeline: :description)
- if can?(current_user, :read_group, @group) - if can?(current_user, :read_group, @group)
.row %ul.center-top-menu.no-top
%section.activities.col-md-7 %li.active
.hidden-xs = link_to "#activity", 'data-toggle' => 'tab' do
- if current_user Activity
= render "events/event_last_push", event: @last_push - if @projects.present?
.pull-right %li
= link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' do = link_to "#projects", 'data-toggle' => 'tab' do
%i.fa.fa-rss Projects
= render 'shared/event_filter' .tab-content
%hr .tab-pane.active#activity
.gray-content-block.activity-filter-block
.content_list - if current_user
= spinner = render "events/event_last_push", event: @last_push
%aside.side.col-md-5 .pull-right
= render "projects", projects: @projects = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' do
- else %i.fa.fa-rss
%p
This group does not have public projects = render 'shared/event_filter'
.content_list
= spinner
.tab-pane#projects
= render "projects", projects: @projects
- else
%p
This group does not have public projects
%div
"#{link_to @note.author_name, user_url(@note.author)} wrote:"
%div %div
= markdown(@note.note, pipeline: :email) = markdown(@note.note, pipeline: :email)
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
%strong.monospace= link_to @build.commit.short_sha, ci_status_path(@build.commit) %strong.monospace= link_to @build.commit.short_sha, ci_status_path(@build.commit)
from from
= link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref) = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
- merge_request = @build.merge_request
- if merge_request
via
= link_to "merge request ##{merge_request.iid}", merge_request_path(merge_request)
#up-build-trace #up-build-trace
- if @commit.matrix_for_ref?(@build.ref) - if @commit.matrix_for_ref?(@build.ref)
......
...@@ -20,8 +20,8 @@ ...@@ -20,8 +20,8 @@
%p %p
%span.light Commit %span.light Commit
= link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace", data: { clipboard_text: @commit.id } = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
= clipboard_button = clipboard_button(clipboard_text: @commit.id)
.commit-info-row .commit-info-row
%span.light Authored by %span.light Authored by
%strong %strong
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
- @commit.parents.each do |parent| - @commit.parents.each do |parent|
= link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace" = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace"
- if @ci_commit && @ci_commit.show_build_status? - if @ci_commit
.pull-right .pull-right
= link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do = link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do
= ci_status_icon(@ci_commit) = ci_status_icon(@ci_commit)
......
...@@ -19,11 +19,11 @@ ...@@ -19,11 +19,11 @@
- if defined?(commit_sha) && commit_sha - if defined?(commit_sha) && commit_sha
%td %td
= link_to commit_status.short_sha, namespace_project_commit_path(@project.namespace, @project, commit_status.sha), class: "monospace" = link_to commit_status.short_sha, namespace_project_commit_path(commit_status.project.namespace, commit_status.project, commit_status.sha), class: "monospace"
%td %td
- if commit_status.ref - if commit_status.ref
= link_to commit_status.ref, namespace_project_commits_path(@project.namespace, @project, commit_status.ref) = link_to commit_status.ref, namespace_project_commits_path(commit_status.project.namespace, commit_status.project, commit_status.ref)
- else - else
.light none .light none
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
%td %td
.pull-right .pull-right
- if current_user && can?(current_user, :download_build_artifacts, @project) && commit_status.download_url - if current_user && can?(current_user, :download_build_artifacts, commit_status.project) && commit_status.download_url
= link_to commit_status.download_url, title: 'Download artifacts' do = link_to commit_status.download_url, title: 'Download artifacts' do
%i.fa.fa-download %i.fa.fa-download
- if current_user && can?(current_user, :manage_builds, commit_status.project) - if current_user && can?(current_user, :manage_builds, commit_status.project)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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