Commit 9f1c3bb7 authored by Alfredo Sumaran's avatar Alfredo Sumaran

Merge branch 'master' into improve-user-tabs

parents fefa6a6f 90ced487
...@@ -23,6 +23,7 @@ config/gitlab.yml ...@@ -23,6 +23,7 @@ config/gitlab.yml
config/gitlab_ci.yml config/gitlab_ci.yml
config/initializers/rack_attack.rb config/initializers/rack_attack.rb
config/initializers/smtp_settings.rb config/initializers/smtp_settings.rb
config/initializers/relative_url.rb
config/resque.yml config/resque.yml
config/unicorn.rb config/unicorn.rb
config/secrets.yml config/secrets.yml
......
...@@ -89,6 +89,7 @@ spec:other: ...@@ -89,6 +89,7 @@ spec:other:
spinach:project:half: spinach:project:half:
stage: test stage: test
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
tags: tags:
- ruby - ruby
...@@ -97,6 +98,7 @@ spinach:project:half: ...@@ -97,6 +98,7 @@ spinach:project:half:
spinach:project:rest: spinach:project:rest:
stage: test stage: test
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
tags: tags:
- ruby - ruby
...@@ -105,6 +107,7 @@ spinach:project:rest: ...@@ -105,6 +107,7 @@ spinach:project:rest:
spinach:other: spinach:other:
stage: test stage: test
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
tags: tags:
- ruby - ruby
...@@ -275,6 +278,7 @@ spinach:project:half:ruby22: ...@@ -275,6 +278,7 @@ spinach:project:half:ruby22:
only: only:
- master - master
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
cache: cache:
key: "ruby22" key: "ruby22"
...@@ -290,6 +294,7 @@ spinach:project:rest:ruby22: ...@@ -290,6 +294,7 @@ spinach:project:rest:ruby22:
only: only:
- master - master
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
cache: cache:
key: "ruby22" key: "ruby22"
...@@ -305,6 +310,7 @@ spinach:other:ruby22: ...@@ -305,6 +310,7 @@ spinach:other:ruby22:
only: only:
- master - master
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
cache: cache:
key: "ruby22" key: "ruby22"
...@@ -318,7 +324,7 @@ spinach:other:ruby22: ...@@ -318,7 +324,7 @@ spinach:other:ruby22:
notify:slack: notify:slack:
stage: notifications stage: notifications
script: script:
- ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Check <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>" - ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
when: on_failure when: on_failure
only: only:
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
......
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.6.0 (unreleased) v 8.6.0 (unreleased)
- Contributions to forked projects are included in calendar
- Improve the formatting for the user page bio (Connor Shea) - Improve the formatting for the user page bio (Connor Shea)
- Fix issue when pushing to projects ending in .wiki
- Fix avatar stretching by providing a cropping feature (Johann Pardanaud) - Fix avatar stretching by providing a cropping feature (Johann Pardanaud)
- Strip leading and trailing spaces in URL validator (evuez) - Strip leading and trailing spaces in URL validator (evuez)
- Return empty array instead of 404 when commit has no statuses in commit status API
- Update documentation to reflect Guest role not being enforced on internal projects - Update documentation to reflect Guest role not being enforced on internal projects
v 8.5.2 v 8.5.2
- Fix sidebar overlapping content when screen width was below 1200px - Fix sidebar overlapping content when screen width was below 1200px
- Fix error 500 when commenting on a commit - Fix error 500 when commenting on a commit
- Fix broken icons on installations with relative URL (Artem Sidorenko)
v 8.5.1 v 8.5.1
- Fix group projects styles - Fix group projects styles
...@@ -28,6 +32,7 @@ v 8.5.1 ...@@ -28,6 +32,7 @@ v 8.5.1
- Re-add the newrelic_rpm gem which was removed without any deprecation or warning (Stan Hu) - Re-add the newrelic_rpm gem which was removed without any deprecation or warning (Stan Hu)
- Update sentry-raven gem to 0.15.6 - Update sentry-raven gem to 0.15.6
- Add build coverage in project's builds page (Steffen Köhler) - Add build coverage in project's builds page (Steffen Köhler)
- Changed # to ! for merge requests in activity view
v 8.5.0 v 8.5.0
- Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu) - Fix duplicate "me" in tooltip of the "thumbsup" awards Emoji (Stan Hu)
......
source "https://rubygems.org" source "https://rubygems.org"
gem 'rails', '4.2.5.1' gem 'rails', '4.2.5.2'
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with # Responders respond_to and respond_with
...@@ -213,7 +213,6 @@ gem 'jquery-atwho-rails', '~> 1.3.2' ...@@ -213,7 +213,6 @@ gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 4.0.0' gem 'jquery-rails', '~> 4.0.0'
gem 'jquery-scrollto-rails', '~> 1.4.3' gem 'jquery-scrollto-rails', '~> 1.4.3'
gem 'jquery-ui-rails', '~> 5.0.0' gem 'jquery-ui-rails', '~> 5.0.0'
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'
gem 'select2-rails', '~> 3.5.9' gem 'select2-rails', '~> 3.5.9'
......
...@@ -4,41 +4,41 @@ GEM ...@@ -4,41 +4,41 @@ GEM
CFPropertyList (2.3.2) CFPropertyList (2.3.2)
RedCloth (4.2.9) RedCloth (4.2.9)
ace-rails-ap (2.0.1) ace-rails-ap (2.0.1)
actionmailer (4.2.5.1) actionmailer (4.2.5.2)
actionpack (= 4.2.5.1) actionpack (= 4.2.5.2)
actionview (= 4.2.5.1) actionview (= 4.2.5.2)
activejob (= 4.2.5.1) activejob (= 4.2.5.2)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.5.1) actionpack (4.2.5.2)
actionview (= 4.2.5.1) actionview (= 4.2.5.2)
activesupport (= 4.2.5.1) activesupport (= 4.2.5.2)
rack (~> 1.6) rack (~> 1.6)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.5.1) actionview (4.2.5.2)
activesupport (= 4.2.5.1) activesupport (= 4.2.5.2)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.5.1) activejob (4.2.5.2)
activesupport (= 4.2.5.1) activesupport (= 4.2.5.2)
globalid (>= 0.3.0) globalid (>= 0.3.0)
activemodel (4.2.5.1) activemodel (4.2.5.2)
activesupport (= 4.2.5.1) activesupport (= 4.2.5.2)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.2.5.1) activerecord (4.2.5.2)
activemodel (= 4.2.5.1) activemodel (= 4.2.5.2)
activesupport (= 4.2.5.1) activesupport (= 4.2.5.2)
arel (~> 6.0) arel (~> 6.0)
activerecord-deprecated_finders (1.0.4) activerecord-deprecated_finders (1.0.4)
activerecord-session_store (0.1.2) activerecord-session_store (0.1.2)
actionpack (>= 4.0.0, < 5) actionpack (>= 4.0.0, < 5)
activerecord (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5)
railties (>= 4.0.0, < 5) railties (>= 4.0.0, < 5)
activesupport (4.2.5.1) activesupport (4.2.5.2)
i18n (~> 0.7) i18n (~> 0.7)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
minitest (~> 5.1) minitest (~> 5.1)
...@@ -483,7 +483,6 @@ GEM ...@@ -483,7 +483,6 @@ GEM
newrelic_rpm (3.14.1.311) newrelic_rpm (3.14.1.311)
nokogiri (1.6.7.2) nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2) mini_portile2 (~> 2.0.0.rc2)
nprogress-rails (0.1.6.7)
oauth (0.4.7) oauth (0.4.7)
oauth2 (1.0.0) oauth2 (1.0.0)
faraday (>= 0.8, < 0.10) faraday (>= 0.8, < 0.10)
...@@ -587,16 +586,16 @@ GEM ...@@ -587,16 +586,16 @@ GEM
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rails (4.2.5.1) rails (4.2.5.2)
actionmailer (= 4.2.5.1) actionmailer (= 4.2.5.2)
actionpack (= 4.2.5.1) actionpack (= 4.2.5.2)
actionview (= 4.2.5.1) actionview (= 4.2.5.2)
activejob (= 4.2.5.1) activejob (= 4.2.5.2)
activemodel (= 4.2.5.1) activemodel (= 4.2.5.2)
activerecord (= 4.2.5.1) activerecord (= 4.2.5.2)
activesupport (= 4.2.5.1) activesupport (= 4.2.5.2)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.2.5.1) railties (= 4.2.5.2)
sprockets-rails sprockets-rails
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
...@@ -606,9 +605,9 @@ GEM ...@@ -606,9 +605,9 @@ GEM
rails-deprecated_sanitizer (>= 1.0.1) rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3) rails-html-sanitizer (1.0.3)
loofah (~> 2.0) loofah (~> 2.0)
railties (4.2.5.1) railties (4.2.5.2)
actionpack (= 4.2.5.1) actionpack (= 4.2.5.2)
activesupport (= 4.2.5.1) activesupport (= 4.2.5.2)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.0.0) rainbow (2.0.0)
...@@ -964,7 +963,6 @@ DEPENDENCIES ...@@ -964,7 +963,6 @@ DEPENDENCIES
net-ssh (~> 3.0.1) net-ssh (~> 3.0.1)
newrelic_rpm (~> 3.14) newrelic_rpm (~> 3.14)
nokogiri (~> 1.6.7, >= 1.6.7.2) nokogiri (~> 1.6.7, >= 1.6.7.2)
nprogress-rails (~> 0.1.6.7)
oauth2 (~> 1.0.0) oauth2 (~> 1.0.0)
octokit (~> 3.8.0) octokit (~> 3.8.0)
omniauth (~> 1.3.1) omniauth (~> 1.3.1)
...@@ -989,7 +987,7 @@ DEPENDENCIES ...@@ -989,7 +987,7 @@ DEPENDENCIES
rack-attack (~> 4.3.1) 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.5.1) rails (= 4.2.5.2)
rails-deprecated_sanitizer (~> 1.0.3) rails-deprecated_sanitizer (~> 1.0.3)
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
rblineprof rblineprof
......
...@@ -36,18 +36,15 @@ The most important thing is making sure valid issues receive feedback from the d ...@@ -36,18 +36,15 @@ The most important thing is making sure valid issues receive feedback from the d
Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue. Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue.
- *Awaiting feedback*: Feedback pending from the reporter - ~"Awaiting Feedback" Feedback pending from the reporter
- *Awaiting confirmation of fix*: The issue should already be solved in **master** (generally you can avoid this workflow item and just close the issue right away) - ~UX needs help from a UX designer
- *Attached MR*: There is a MR attached and the discussion should happen there - ~Frontend needs help from a Front-end engineer
- We need to let issues stay in sync with the MR's. We can do this with a "Closing #XXXX" or "Fixes #XXXX" comment in the MR. We can't close the issue when there is a merge request because sometimes a MR is not good and we just close the MR, then the issue must stay. - ~up-for-grabs is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels.
- *Developer*: needs help from a developer - ~"feature proposal" is a proposal for a new feature for GitLab. People are encouraged to vote
- *UX* needs needs help from a UX designer
- *Frontend* needs help from a Front-end engineer
- *Graphics* needs help from a Graphics designer
- *up-for-grabs* is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels.
- *feature proposal* is a proposal for a new feature for GitLab. People are encouraged to vote
in support or comment for further detail. Do not use `feature request`. in support or comment for further detail. Do not use `feature request`.
- ~bug is an issue reporting undesirable or incorrect behavior.
- ~customer is an issue reported by enterprise subscribers. This label should
be accompanied by *bug* or *feature proposal* labels.
Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label. Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label.
## Functional labels ## Functional labels
......
...@@ -4,4 +4,7 @@ ...@@ -4,4 +4,7 @@
require File.expand_path('../config/application', __FILE__) require File.expand_path('../config/application', __FILE__)
relative_url_conf = File.expand_path('../config/initializers/relative_url', __FILE__)
require relative_url_conf if File.exist?("#{relative_url_conf}.rb")
Gitlab::Application.load_tasks Gitlab::Application.load_tasks
...@@ -31,8 +31,6 @@ ...@@ -31,8 +31,6 @@
#= require ace/ace #= require ace/ace
#= require ace/ext-searchbox #= require ace/ext-searchbox
#= require underscore #= require underscore
#= require nprogress
#= require nprogress-turbolinks
#= require dropzone #= require dropzone
#= require mousetrap #= require mousetrap
#= require mousetrap/pause #= require mousetrap/pause
......
NProgress.configure(showSpinner: false) Turbolinks.enableProgressBar();
defaultClass = 'tanuki-shape' defaultClass = 'tanuki-shape'
pieces = [ pieces = [
......
...@@ -146,6 +146,7 @@ class @MergeRequestTabs ...@@ -146,6 +146,7 @@ class @MergeRequestTabs
url: "#{source}.json" + @_location.search url: "#{source}.json" + @_location.search
success: (data) => success: (data) =>
document.querySelector("div#diffs").innerHTML = data.html document.querySelector("div#diffs").innerHTML = data.html
$('.js-timeago').timeago()
$('div#diffs .js-syntax-highlight').syntaxHighlight() $('div#diffs .js-syntax-highlight').syntaxHighlight()
@expandViewContainer() if @diffViewType() is 'parallel' @expandViewContainer() if @diffViewType() is 'parallel'
@diffsLoaded = true @diffsLoaded = true
......
...@@ -25,12 +25,6 @@ ...@@ -25,12 +25,6 @@
*/ */
@import "framework"; @import "framework";
/*
* NProgress load bar css
*/
@import 'nprogress';
@import 'nprogress-bootstrap';
/* /*
* Font icons * Font icons
*/ */
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
@import "framework/mobile.scss"; @import "framework/mobile.scss";
@import "framework/nav.scss"; @import "framework/nav.scss";
@import "framework/pagination.scss"; @import "framework/pagination.scss";
@import "framework/progress.scss";
@import "framework/panels.scss"; @import "framework/panels.scss";
@import "framework/selects.scss"; @import "framework/selects.scss";
@import "framework/sidebar.scss"; @import "framework/sidebar.scss";
......
html.turbolinks-progress-bar::before {
background-color: $progress-color!important;
height: 2px!important;
box-shadow: 0 0 10px $progress-color, 0 0 5px $progress-color;
}
...@@ -13,6 +13,19 @@ ...@@ -13,6 +13,19 @@
transition-duration: .3s; transition-duration: .3s;
} }
.gitlab-text-container-link {
z-index: 1;
position: absolute;
left: 0px;
}
#logo {
z-index: 2;
position: absolute;
width: 58px;
cursor: pointer;
}
&.right-sidebar-expanded { &.right-sidebar-expanded {
padding-right: $gutter_width; padding-right: $gutter_width;
} }
...@@ -74,7 +87,7 @@ ...@@ -74,7 +87,7 @@
width: 158px; width: 158px;
float: left; float: left;
margin: 0; margin: 0;
margin-left: 14px; margin-left: 50px;
font-size: 19px; font-size: 19px;
line-height: 41px; line-height: 41px;
font-weight: normal; font-weight: normal;
......
...@@ -7,7 +7,7 @@ $gl-header-color: #323232; ...@@ -7,7 +7,7 @@ $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;
$nprogress-color: #c0392b; $progress-color: #c0392b;
$gl-font-size: 15px; $gl-font-size: 15px;
$list-font-size: 15px; $list-font-size: 15px;
$sidebar_collapsed_width: 62px; $sidebar_collapsed_width: 62px;
......
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
max-width: none; max-width: none;
} }
.flash-container {
margin-bottom: $gl-padding;
}
.brand-holder { .brand-holder {
font-size: 18px; font-size: 18px;
line-height: 1.5; line-height: 1.5;
......
...@@ -14,6 +14,18 @@ ul.notes { ...@@ -14,6 +14,18 @@ ul.notes {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
.timeline-icon {
float: left;
}
.timeline-content {
margin-left: 55px;
}
.note_created_ago, .note-updated-at {
white-space: nowrap;
}
.system-note { .system-note {
font-size: 14px; font-size: 14px;
padding-top: 10px; padding-top: 10px;
...@@ -151,6 +163,7 @@ ul.notes { ...@@ -151,6 +163,7 @@ ul.notes {
border-left: none; border-left: none;
&.notes_line { &.notes_line {
vertical-align: middle;
text-align: center; text-align: center;
padding: 10px 0; padding: 10px 0;
background: #FFF; background: #FFF;
......
...@@ -12,29 +12,10 @@ ...@@ -12,29 +12,10 @@
} }
} }
.todos {
.panel {
border-top: none;
margin-bottom: 0;
}
}
.todo-item { .todo-item {
font-size: $gl-font-size; font-size: $gl-font-size;
padding: $gl-padding-top 0 $gl-padding-top ($gl-avatar-size + $gl-padding-top); padding-left: $gl-avatar-size + $gl-padding-top;
border-bottom: 1px solid $table-border-color; color: $secondary-text;
color: #7f8fa4;
&.todo-inline {
.avatar {
position: relative;
top: -2px;
}
.todo-title {
line-height: 40px;
}
}
a { a {
color: #4c4e54; color: #4c4e54;
...@@ -48,7 +29,7 @@ ...@@ -48,7 +29,7 @@
@include str-truncated(calc(100% - 174px)); @include str-truncated(calc(100% - 174px));
font-weight: 600; font-weight: 600;
.author_name { .author-name {
color: #333; color: #333;
} }
} }
...@@ -88,17 +69,7 @@ ...@@ -88,17 +69,7 @@
margin-bottom: 0; margin-bottom: 0;
} }
} }
.todo-note-icon {
color: #777;
float: left;
font-size: $gl-font-size;
line-height: 16px;
margin-right: 5px;
}
} }
&:last-child { border:none }
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
......
...@@ -12,11 +12,13 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -12,11 +12,13 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
current_user.save! if current_user.changed? current_user.save! if current_user.changed?
if two_factor_grace_period_expired? if two_factor_authentication_required?
flash.now[:alert] = 'You must enable Two-factor Authentication for your account.' if two_factor_grace_period_expired?
else flash.now[:alert] = 'You must enable Two-factor Authentication for your account.'
grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours else
flash.now[:alert] = "You must enable Two-factor Authentication for your account before #{l(grace_period_deadline)}." grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
flash.now[:alert] = "You must enable Two-factor Authentication for your account before #{l(grace_period_deadline)}."
end
end end
@qr_code = build_qr_code @qr_code = build_qr_code
......
class Projects::AvatarsController < Projects::ApplicationController class Projects::AvatarsController < Projects::ApplicationController
include BlobHelper
before_action :project before_action :project
def show def show
...@@ -7,7 +9,7 @@ class Projects::AvatarsController < Projects::ApplicationController ...@@ -7,7 +9,7 @@ class Projects::AvatarsController < Projects::ApplicationController
headers['X-Content-Type-Options'] = 'nosniff' headers['X-Content-Type-Options'] = 'nosniff'
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
headers['Content-Disposition'] = 'inline' headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = @blob.content_type headers['Content-Type'] = safe_content_type(@blob)
head :ok # 'render nothing: true' messes up the Content-Type head :ok # 'render nothing: true' messes up the Content-Type
else else
render_404 render_404
......
...@@ -4,12 +4,22 @@ class Projects::ForksController < Projects::ApplicationController ...@@ -4,12 +4,22 @@ class Projects::ForksController < Projects::ApplicationController
before_action :authorize_download_code! before_action :authorize_download_code!
def index def index
@sort = params[:sort] || 'id_desc' base_query = project.forks.includes(:creator)
@all_forks = project.forks.includes(:creator).order_by(@sort)
@forks = if current_user
@public_forks, @protected_forks = @all_forks.partition do |project| base_query.where('projects.visibility_level IN (?) OR projects.id IN (?)',
can?(current_user, :read_project, project) Project.public_and_internal_levels,
end current_user.authorized_projects.pluck(:id))
else
base_query.where('projects.visibility_level = ?', Project::PUBLIC)
end
@total_forks_count = base_query.size
@private_forks_count = @total_forks_count - @forks.size
@public_forks_count = @total_forks_count - @private_forks_count
@sort = params[:sort] || 'id_desc'
@forks = @forks.order_by(@sort).page(params[:page]).per(PER_PAGE)
end end
def new def new
......
# Controller for viewing a file's raw # Controller for viewing a file's raw
class Projects::RawController < Projects::ApplicationController class Projects::RawController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include BlobHelper
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :assign_ref_vars before_action :assign_ref_vars
...@@ -17,7 +18,7 @@ class Projects::RawController < Projects::ApplicationController ...@@ -17,7 +18,7 @@ class Projects::RawController < Projects::ApplicationController
else else
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
headers['Content-Disposition'] = 'inline' headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = get_blob_type headers['Content-Type'] = safe_content_type(@blob)
head :ok # 'render nothing: true' messes up the Content-Type head :ok # 'render nothing: true' messes up the Content-Type
end end
else else
...@@ -27,16 +28,6 @@ class Projects::RawController < Projects::ApplicationController ...@@ -27,16 +28,6 @@ class Projects::RawController < Projects::ApplicationController
private private
def get_blob_type
if @blob.text?
'text/plain; charset=utf-8'
elsif @blob.image?
@blob.content_type
else
'application/octet-stream'
end
end
def send_lfs_object def send_lfs_object
lfs_object = find_lfs_object lfs_object = find_lfs_object
......
...@@ -89,7 +89,7 @@ class UsersController < ApplicationController ...@@ -89,7 +89,7 @@ class UsersController < ApplicationController
def contributions_calendar def contributions_calendar
@contributions_calendar ||= Gitlab::ContributionsCalendar. @contributions_calendar ||= Gitlab::ContributionsCalendar.
new(contributed_projects.reject(&:forked?), @user) new(contributed_projects, @user)
end end
def load_events def load_events
......
...@@ -134,4 +134,22 @@ module BlobHelper ...@@ -134,4 +134,22 @@ module BlobHelper
blob.data = Loofah.scrub_fragment(blob.data, :strip).to_xml blob.data = Loofah.scrub_fragment(blob.data, :strip).to_xml
blob blob
end end
# If we blindly set the 'real' content type when serving a Git blob we
# are enabling XSS attacks. An attacker could upload e.g. a Javascript
# file to a Git repository, trick the browser of a victim into
# downloading the blob, and then the 'application/javascript' content
# type would tell the browser to execute the attacker's Javascript. By
# overriding the content type and setting it to 'text/plain' (in the
# example of Javascript) we tell the browser of the victim not to
# execute untrusted data.
def safe_content_type(blob)
if blob.text?
'text/plain; charset=utf-8'
elsif blob.image?
blob.content_type
else
'application/octet-stream'
end
end
end end
...@@ -168,11 +168,11 @@ module EventsHelper ...@@ -168,11 +168,11 @@ module EventsHelper
link_to(namespace_project_snippet_path(event.project.namespace, link_to(namespace_project_snippet_path(event.project.namespace,
event.project, event.project,
event.note_target)) do event.note_target)) do
"#{event.note_target_type} ##{truncate event.note_target_id}" "#{event.note_target_type} #{truncate event.note_target.to_reference}"
end end
else else
link_to event_note_target_path(event) do link_to event_note_target_path(event) do
"#{event.note_target_type} ##{truncate event.note_target_iid}" "#{event.note_target_type} #{truncate event.note_target.to_reference}"
end end
end end
else else
......
...@@ -169,7 +169,7 @@ class User < ActiveRecord::Base ...@@ -169,7 +169,7 @@ class User < ActiveRecord::Base
validates :avatar_crop_x, :avatar_crop_y, :avatar_crop_size, validates :avatar_crop_x, :avatar_crop_y, :avatar_crop_size,
numericality: { only_integer: true }, numericality: { only_integer: true },
presence: true, presence: true,
if: ->(user) { user.avatar? } if: ->(user) { user.avatar? && user.avatar_changed? }
before_validation :generate_password, on: :create before_validation :generate_password, on: :create
before_validation :restricted_signup_domains, on: :create before_validation :restricted_signup_domains, on: :create
...@@ -362,17 +362,19 @@ class User < ActiveRecord::Base ...@@ -362,17 +362,19 @@ class User < ActiveRecord::Base
def disable_two_factor! def disable_two_factor!
update_attributes( update_attributes(
two_factor_enabled: false, two_factor_enabled: false,
encrypted_otp_secret: nil, encrypted_otp_secret: nil,
encrypted_otp_secret_iv: nil, encrypted_otp_secret_iv: nil,
encrypted_otp_secret_salt: nil, encrypted_otp_secret_salt: nil,
otp_backup_codes: nil otp_grace_period_started_at: nil,
otp_backup_codes: nil
) )
end end
def namespace_uniq def namespace_uniq
# Return early if username already failed the first uniqueness validation # Return early if username already failed the first uniqueness validation
return if self.errors[:username].include?('has already been taken') return if self.errors.key?(:username) &&
self.errors[:username].include?('has already been taken')
namespace_name = self.username namespace_name = self.username
existing_namespace = Namespace.by_path(namespace_name) existing_namespace = Namespace.by_path(namespace_name)
......
%li{class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo) } %li{class: "todo todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo) }
.todo-item{class: 'todo-block'} .todo-item.todo-block
= image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:'' = image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:''
.todo-title .todo-title
%span.author_name %span.author-name
= link_to_author todo = link_to_author todo
%span.todo_label %span.todo-label
= todo_action_name(todo) = todo_action_name(todo)
= todo_target_link(todo) = todo_target_link(todo)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
= event_action_name(event) = event_action_name(event)
- if event.target - if event.target
%strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] %strong= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target]
= event_preposition(event) = event_preposition(event)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.header-logo .header-logo
%a#logo %a#logo
= brand_header_logo = brand_header_logo
= link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do = link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do
.gitlab-text-container .gitlab-text-container
%h3 GitLab %h3 GitLab
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.header-logo .header-logo
%a#logo %a#logo
= brand_header_logo = brand_header_logo
= link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do = link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do
.gitlab-text-container .gitlab-text-container
%h3 GitLab %h3 GitLab
......
...@@ -3,17 +3,16 @@ ...@@ -3,17 +3,16 @@
Too many changes to show. Too many changes to show.
.pull-right .pull-right
- unless diff_hard_limit_enabled? - unless diff_hard_limit_enabled?
= link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: nil)), class: "btn btn-sm btn-warning" = link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: nil)), class: "btn btn-sm"
- if current_controller?(:commit) or current_controller?(:merge_requests) - if current_controller?(:commit) or current_controller?(:merge_requests)
- if current_controller?(:commit) - if current_controller?(:commit)
= link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-warning btn-sm" = link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-sm"
= link_to "Email patch", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch), class: "btn btn-warning btn-sm" = link_to "Email patch", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch), class: "btn btn-sm"
- elsif @merge_request && @merge_request.persisted? - elsif @merge_request && @merge_request.persisted?
= link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-warning btn-sm" = link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-sm"
= link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-warning btn-sm" = link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-sm"
%p %p
To preserve performance only To preserve performance only
%strong #{shown_files_count} of #{diffs.size} %strong #{shown_files_count} of #{diffs.size}
files are displayed. files are displayed.
.top-area .top-area
.nav-text .nav-text
- public_count = @public_forks.size - full_count_title = "#{@public_forks_count} public and #{@private_forks_count} private"
- protected_count = @protected_forks.size == #{pluralize(@total_forks_count, 'fork')}: #{full_count_title}
- full_count_title = "#{public_count} public and #{protected_count} private"
== #{pluralize(@all_forks.size, 'fork')}: #{full_count_title}
.nav-controls .nav-controls
= search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short', = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short',
...@@ -41,17 +39,17 @@ ...@@ -41,17 +39,17 @@
.projects-list-holder .projects-list-holder
- if @public_forks.blank? - if @forks.blank?
%ul.content-list %ul.content-list
%li %li
.nothing-here-block No forks to show .nothing-here-block No forks to show
- else - else
= render 'shared/projects/list', projects: @public_forks, use_creator_avatar: true, = render 'shared/projects/list', projects: @forks, use_creator_avatar: true,
forks: true, show_last_commit_as_description: true forks: true, show_last_commit_as_description: true
- if protected_count > 0 - if @private_forks_count > 0
%ul.projects-list.private-forks-notice %ul.projects-list.private-forks-notice
%li.project-row %li.project-row
= icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon') = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
%strong= pluralize(protected_count, 'private fork') %strong= pluralize(@private_forks_count, 'private fork')
%span you have no access to. %span you have no access to.
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
= note_count = note_count
.merge-request-info .merge-request-info
\##{merge_request.iid} &middot; #{merge_request.to_reference} &middot;
opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')}
by #{link_to_member(@project, merge_request.author, avatar: false)} by #{link_to_member(@project, merge_request.author, avatar: false)}
- if merge_request.target_project.default_branch != merge_request.target_branch - if merge_request.target_project.default_branch != merge_request.target_branch
......
- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests" - page_title "#{@merge_request.title} (#{@merge_request.to_reference})", "Merge Requests"
- page_description @merge_request.description - page_description @merge_request.description
- page_card_attributes @merge_request.card_attributes - page_card_attributes @merge_request.card_attributes
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.status-box{ class: status_box_class(@merge_request) } .status-box{ class: status_box_class(@merge_request) }
= @merge_request.state_human_name = @merge_request.state_human_name
%span.identifier %span.identifier
Merge Request ##{@merge_request.iid} Merge Request #{@merge_request.to_reference}
%span.creator %span.creator
&middot; &middot;
by #{link_to_member(@project, @merge_request.author, size: 24)} by #{link_to_member(@project, @merge_request.author, size: 24)}
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
%a{name: dom_id(note), href: "##{dom_id(note)}", title: 'Link here'} %a{name: dom_id(note), href: "##{dom_id(note)}", title: 'Link here'}
= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago') = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago')
- if note.updated_at != note.created_at - if note.updated_at != note.created_at
%span %span.note-updated-at
&middot; &middot;
= icon('edit', title: 'edited') = icon('edit', title: 'edited')
= time_ago_with_tooltip(note.updated_at, placement: 'bottom', html_class: 'note_edited_ago') = time_ago_with_tooltip(note.updated_at, placement: 'bottom', html_class: 'note_edited_ago')
......
...@@ -54,14 +54,6 @@ module Gitlab ...@@ -54,14 +54,6 @@ module Gitlab
config.action_view.sanitized_allowed_protocols = %w(smb) config.action_view.sanitized_allowed_protocols = %w(smb)
# Relative URL support
# WARNING: We recommend using an FQDN to host GitLab in a root path instead
# of using a relative URL.
# Documentation: http://doc.gitlab.com/ce/install/relative_url.html
# Uncomment and customize the following line to run in a non-root path
#
# config.relative_url_root = "/gitlab"
config.middleware.use Rack::Attack config.middleware.use Rack::Attack
# Allow access to GitLab API from other domains # Allow access to GitLab API from other domains
......
# Relative URL support
# WARNING: We recommend using an FQDN to host GitLab in a root path instead
# of using a relative URL.
# Documentation: http://doc.gitlab.com/ce/install/relative_url.html
# Copy this file to relative_url.rb and customize it to run in a non-root path
#
Rails.application.configure do
config.relative_url_root = "/gitlab"
end
class CreateAppearancesCE < ActiveRecord::Migration class CreateAppearancesCe < ActiveRecord::Migration
def change def change
unless table_exists?(:appearances) unless table_exists?(:appearances)
create_table :appearances do |t| create_table :appearances do |t|
......
...@@ -30,7 +30,7 @@ The API_TOKEN will take the Secure Variable value: `SECURE`. ...@@ -30,7 +30,7 @@ The API_TOKEN will take the Secure Variable value: `SECURE`.
| **CI_BUILD_REF_NAME** | all | The branch or tag name for which project is built | | **CI_BUILD_REF_NAME** | all | The branch or tag name for which project is built |
| **CI_BUILD_ID** | all | The unique id of the current build that GitLab CI uses internally | | **CI_BUILD_ID** | all | The unique id of the current build that GitLab CI uses internally |
| **CI_BUILD_REPO** | all | The URL to clone the Git repository | | **CI_BUILD_REPO** | all | The URL to clone the Git repository |
| **CI_BUILD_TRIGGERED** | 0.5 | The flag to indicate that build was triggered | | **CI_BUILD_TRIGGERED** | 0.5 | The flag to indicate that build was [triggered] |
| **CI_PROJECT_ID** | all | The unique id of the current project that GitLab CI uses internally | | **CI_PROJECT_ID** | all | The unique id of the current project that GitLab CI uses internally |
| **CI_PROJECT_DIR** | all | The full path where the repository is cloned and where the build is ran | | **CI_PROJECT_DIR** | all | The full path where the repository is cloned and where the build is ran |
...@@ -104,3 +104,5 @@ job_name: ...@@ -104,3 +104,5 @@ job_name:
script: script:
- export - export
``` ```
[triggered]: ../triggers/README.md
...@@ -25,7 +25,7 @@ that points to your GitLab instance. ...@@ -25,7 +25,7 @@ that points to your GitLab instance.
The TL;DR list of configuration files that you need to change in order to The TL;DR list of configuration files that you need to change in order to
serve GitLab under a relative URL is: serve GitLab under a relative URL is:
- `/home/git/gitlab/config/application.rb` - `/home/git/gitlab/config/initializers/relative_url.rb`
- `/home/git/gitlab/config/gitlab.yml` - `/home/git/gitlab/config/gitlab.yml`
- `/home/git/gitlab/config/unicorn.rb` - `/home/git/gitlab/config/unicorn.rb`
- `/home/git/gitlab-shell/config.yml` - `/home/git/gitlab-shell/config.yml`
...@@ -66,8 +66,14 @@ Make sure to follow all steps below: ...@@ -66,8 +66,14 @@ Make sure to follow all steps below:
sudo service gitlab stop sudo service gitlab stop
``` ```
1. Edit `/home/git/gitlab/config/application.rb` and uncomment/change the 1. Create `/home/git/gitlab/config/initializers/relative_url.rb`
following line:
```shell
cp /home/git/gitlab/config/initializers/relative_url.rb.sample \
/home/git/gitlab/config/initializers/relative_url.rb
```
and change the following line:
```ruby ```ruby
config.relative_url_root = "/gitlab" config.relative_url_root = "/gitlab"
...@@ -119,8 +125,12 @@ Make sure to follow all steps below: ...@@ -119,8 +125,12 @@ Make sure to follow all steps below:
### Disable relative URL in GitLab ### Disable relative URL in GitLab
To disable the relative URL, follow the same steps as above and set up the To disable the relative URL:
GitLab URL to one that doesn't contain a relative path.
1. Remove `/home/git/gitlab/config/initializers/relative_url.rb`
1. Follow the same as above starting from 2. and set up the
GitLab URL to one that doesn't contain a relative path.
[omnibus-rel]: http://doc.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to setup relative URL in Omnibus GitLab" [omnibus-rel]: http://doc.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to setup relative URL in Omnibus GitLab"
[restart gitlab]: ../administration/restart_gitlab.md#installations-from-source "How to restart GitLab" [restart gitlab]: ../administration/restart_gitlab.md#installations-from-source "How to restart GitLab"
...@@ -18,10 +18,12 @@ module API ...@@ -18,10 +18,12 @@ module API
# Examples: # Examples:
# GET /projects/:id/repository/commits/:sha/statuses # GET /projects/:id/repository/commits/:sha/statuses
get ':id/repository/commits/:sha/statuses' do get ':id/repository/commits/:sha/statuses' do
authorize! :read_commit_status, user_project authorize!(:read_commit_status, user_project)
sha = params[:sha]
ci_commit = user_project.ci_commit(sha) not_found!('Commit') unless user_project.commit(params[:sha])
not_found! 'Commit' unless ci_commit ci_commit = user_project.ci_commit(params[:sha])
return [] unless ci_commit
statuses = ci_commit.statuses statuses = ci_commit.statuses
statuses = statuses.latest unless parse_boolean(params[:all]) statuses = statuses.latest unless parse_boolean(params[:all])
statuses = statuses.where(ref: params[:ref]) if params[:ref].present? statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
......
...@@ -14,6 +14,14 @@ module API ...@@ -14,6 +14,14 @@ module API
# ref - branch name # ref - branch name
# forced_push - forced_push # forced_push - forced_push
# #
helpers do
def wiki?
@wiki ||= params[:project].end_with?('.wiki') &&
!Project.find_with_namespace(params[:project])
end
end
post "/allowed" do post "/allowed" do
status 200 status 200
...@@ -30,13 +38,12 @@ module API ...@@ -30,13 +38,12 @@ module API
# Strip out the .wiki from the pathname before finding the # Strip out the .wiki from the pathname before finding the
# project. This applies the correct project permissions to # project. This applies the correct project permissions to
# the wiki repository as well. # the wiki repository as well.
wiki = project_path.end_with?('.wiki') project_path.chomp!('.wiki') if wiki?
project_path.chomp!('.wiki') if wiki
project = Project.find_with_namespace(project_path) project = Project.find_with_namespace(project_path)
access = access =
if wiki if wiki?
Gitlab::GitAccessWiki.new(actor, project) Gitlab::GitAccessWiki.new(actor, project)
else else
Gitlab::GitAccess.new(actor, project) Gitlab::GitAccess.new(actor, project)
......
...@@ -50,21 +50,30 @@ module Banzai ...@@ -50,21 +50,30 @@ module Banzai
# See https://github.com/gollum/gollum/wiki # See https://github.com/gollum/gollum/wiki
# #
# Rubular: http://rubular.com/r/7dQnE5CUCH # Rubular: http://rubular.com/r/7dQnE5CUCH
TAGS_PATTERN = %r{\[\[(.+?)\]\]} TAGS_PATTERN = %r{\[\[(.+?)\]\]}.freeze
# Pattern to match allowed image extensions # Pattern to match allowed image extensions
ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i.freeze
def call def call
search_text_nodes(doc).each do |node| search_text_nodes(doc).each do |node|
content = node.content # A Gollum ToC tag is `[[_TOC_]]`, but due to MarkdownFilter running
# before this one, it will be converted into `[[<em>TOC</em>]]`, so it
# needs special-case handling
if toc_tag?(node)
next unless result[:toc].present?
next unless content.match(TAGS_PATTERN) process_toc_tag(node)
else
content = node.content
html = process_tag($1) next unless content =~ TAGS_PATTERN
if html && html != node.content html = process_tag($1)
node.replace(html)
if html && html != node.content
node.replace(html)
end
end end
end end
...@@ -73,6 +82,12 @@ module Banzai ...@@ -73,6 +82,12 @@ module Banzai
private private
# Replace an entire `[[<em>TOC</em>]]` node with the result generated by
# TableOfContentsFilter
def process_toc_tag(node)
node.parent.parent.replace(result[:toc])
end
# Process a single tag into its final HTML form. # Process a single tag into its final HTML form.
# #
# tag - The String tag contents (the stuff inside the double brackets). # tag - The String tag contents (the stuff inside the double brackets).
...@@ -108,6 +123,12 @@ module Banzai ...@@ -108,6 +123,12 @@ module Banzai
end end
end end
def toc_tag?(node)
node.content == 'TOC' &&
node.parent.name == 'em' &&
node.parent.parent.text == '[[TOC]]'
end
def image?(path) def image?(path)
path =~ ALLOWED_IMAGE_EXTENSIONS path =~ ALLOWED_IMAGE_EXTENSIONS
end end
......
...@@ -4,7 +4,11 @@ module Banzai ...@@ -4,7 +4,11 @@ module Banzai
module Pipeline module Pipeline
class WikiPipeline < FullPipeline class WikiPipeline < FullPipeline
def self.filters def self.filters
super.insert(1, Filter::GollumTagsFilter) @filters ||= begin
filters = super
toc = filters.index(Filter::TableOfContentsFilter)
filters.insert(toc + 1, Filter::GollumTagsFilter)
end
end end
end end
end end
......
namespace :cache do namespace :cache do
CLEAR_BATCH_SIZE = 1000 # The more the faster, but having too many can crash Ruby CLEAR_BATCH_SIZE = 1000 # There seems to be no speedup when pushing beyond 1,000
REDIS_SCAN_START_STOP = '0' # Magic value, see http://redis.io/commands/scan REDIS_SCAN_START_STOP = '0' # Magic value, see http://redis.io/commands/scan
desc "GitLab | Clear redis cache" desc "GitLab | Clear redis cache"
......
...@@ -41,6 +41,7 @@ describe UsersController do ...@@ -41,6 +41,7 @@ describe UsersController do
end end
describe 'GET #calendar' do describe 'GET #calendar' do
it 'renders calendar' do it 'renders calendar' do
sign_in(user) sign_in(user)
...@@ -48,6 +49,23 @@ describe UsersController do ...@@ -48,6 +49,23 @@ describe UsersController do
expect(response).to render_template('calendar') expect(response).to render_template('calendar')
end end
context 'forked project' do
let!(:project) { create(:project) }
let!(:forked_project) { Projects::ForkService.new(project, user).execute }
before do
sign_in(user)
project.team << [user, :developer]
EventCreateService.new.push(project, user, [])
EventCreateService.new.push(forked_project, user, [])
end
it 'includes forked projects' do
get :calendar, username: user.username
expect(assigns(:contributions_calendar).projects.count).to eq(2)
end
end
end end
describe 'GET #calendar_activities' do describe 'GET #calendar_activities' do
......
...@@ -32,6 +32,7 @@ FactoryGirl.define do ...@@ -32,6 +32,7 @@ FactoryGirl.define do
before(:create) do |user| before(:create) do |user|
user.two_factor_enabled = true user.two_factor_enabled = true
user.otp_secret = User.generate_otp_secret(32) user.otp_secret = User.generate_otp_secret(32)
user.otp_grace_period_started_at = Time.now
user.generate_otp_backup_codes! user.generate_otp_backup_codes!
end end
end end
......
...@@ -86,4 +86,56 @@ describe Banzai::Filter::GollumTagsFilter, lib: true do ...@@ -86,4 +86,56 @@ describe Banzai::Filter::GollumTagsFilter, lib: true do
expect(doc.at_css('a')['href']).to eq 'wiki-slug' expect(doc.at_css('a')['href']).to eq 'wiki-slug'
end end
end end
context 'table of contents' do
let(:pipeline) { Banzai::Pipeline[:wiki] }
it 'replaces the tag with the TableOfContentsFilter result' do
markdown = <<-MD.strip_heredoc
[[_TOC_]]
## Header
Foo
MD
result = pipeline.call(markdown, project_wiki: project_wiki, project: project)
aggregate_failures do
expect(result[:output].text).not_to include '[[_TOC_]]'
expect(result[:output].text).not_to include '[['
expect(result[:output].to_html).to include(result[:toc])
end
end
it 'is case-sensitive' do
markdown = <<-MD.strip_heredoc
[[_toc_]]
# Header 1
Foo
MD
output = pipeline.to_html(markdown, project_wiki: project_wiki, project: project)
expect(output).to include('[[<em>toc</em>]]')
end
it 'handles an empty pipeline result' do
# No Markdown headers in this doc, so `result[:toc]` will be empty
markdown = <<-MD.strip_heredoc
[[_TOC_]]
Foo
MD
output = pipeline.to_html(markdown, project_wiki: project_wiki, project: project)
aggregate_failures do
expect(output).not_to include('<ul>')
expect(output).to include('[[<em>TOC</em>]]')
end
end
end
end end
...@@ -176,7 +176,7 @@ describe User, models: true do ...@@ -176,7 +176,7 @@ describe User, models: true do
end end
describe 'avatar' do describe 'avatar' do
it 'only validates when avatar is present' do it 'only validates when avatar is present and changed' do
user = build(:user, :with_avatar) user = build(:user, :with_avatar)
user.avatar_crop_x = nil user.avatar_crop_x = nil
...@@ -184,6 +184,20 @@ describe User, models: true do ...@@ -184,6 +184,20 @@ describe User, models: true do
user.avatar_crop_size = nil user.avatar_crop_size = nil
expect(user).not_to be_valid expect(user).not_to be_valid
expect(user.errors.keys).
to match_array %i(avatar_crop_x avatar_crop_y avatar_crop_size)
end
it 'does not validate when avatar has not changed' do
user = create(:user, :with_avatar)
expect { user.avatar_crop_x = nil }.not_to change(user, :valid?)
end
it 'does not validate when avatar is not present' do
user = create(:user)
expect { user.avatar_crop_y = nil }.not_to change(user, :valid?)
end end
end end
end end
...@@ -268,6 +282,7 @@ describe User, models: true do ...@@ -268,6 +282,7 @@ describe User, models: true do
expect(user).to be_two_factor_enabled expect(user).to be_two_factor_enabled
expect(user.encrypted_otp_secret).not_to be_nil expect(user.encrypted_otp_secret).not_to be_nil
expect(user.otp_backup_codes).not_to be_nil expect(user.otp_backup_codes).not_to be_nil
expect(user.otp_grace_period_started_at).not_to be_nil
user.disable_two_factor! user.disable_two_factor!
...@@ -276,6 +291,7 @@ describe User, models: true do ...@@ -276,6 +291,7 @@ describe User, models: true do
expect(user.encrypted_otp_secret_iv).to be_nil expect(user.encrypted_otp_secret_iv).to be_nil
expect(user.encrypted_otp_secret_salt).to be_nil expect(user.encrypted_otp_secret_salt).to be_nil
expect(user.otp_backup_codes).to be_nil expect(user.otp_backup_codes).to be_nil
expect(user.otp_grace_period_started_at).to be_nil
end end
end end
......
...@@ -2,88 +2,125 @@ require 'spec_helper' ...@@ -2,88 +2,125 @@ require 'spec_helper'
describe API::CommitStatus, api: true do describe API::CommitStatus, api: true do
include ApiHelpers include ApiHelpers
let!(:project) { create(:project) } let!(:project) { create(:project) }
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
let!(:ci_commit) { project.ensure_ci_commit(commit.id) }
let(:commit_status) { create(:commit_status, commit: ci_commit) } let(:commit_status) { create(:commit_status, commit: ci_commit) }
let(:guest) { create_user(ProjectMember::GUEST) } let(:guest) { create_user(ProjectMember::GUEST) }
let(:reporter) { create_user(ProjectMember::REPORTER) } let(:reporter) { create_user(ProjectMember::REPORTER) }
let(:developer) { create_user(ProjectMember::DEVELOPER) } let(:developer) { create_user(ProjectMember::DEVELOPER) }
let(:sha) { commit.id }
describe "GET /projects/:id/repository/commits/:sha/statuses" do describe "GET /projects/:id/repository/commits/:sha/statuses" do
it_behaves_like 'a paginated resources' do let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" }
let(:request) { get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", reporter) }
end
context "reporter user" do context 'ci commit exists' do
let(:statuses_id) { json_response.map { |status| status['id'] } } let!(:ci_commit) { project.ensure_ci_commit(commit.id) }
before do it_behaves_like 'a paginated resources' do
@status1 = create(:commit_status, commit: ci_commit, status: 'running') let(:request) { get api(get_url, reporter) }
@status2 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'pending')
@status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running', allow_failure: true)
@status4 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'success')
@status5 = create(:commit_status, commit: ci_commit, ref: 'develop', status: 'success')
@status6 = create(:commit_status, commit: ci_commit, status: 'success')
end end
it "should return latest commit statuses" do context "reporter user" do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", reporter) let(:statuses_id) { json_response.map { |status| status['id'] } }
expect(response.status).to eq(200)
expect(json_response).to be_an Array def create_status(opts = {})
expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id) create(:commit_status, { commit: ci_commit }.merge(opts))
json_response.sort_by!{ |status| status['id'] } end
expect(json_response.map{ |status| status['allow_failure'] }).to eq([true, false, false, false])
end
it "should return all commit statuses" do let!(:status1) { create_status(status: 'running') }
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", reporter) let!(:status2) { create_status(name: 'coverage', status: 'pending') }
expect(response.status).to eq(200) let!(:status3) { create_status(ref: 'develop', status: 'running', allow_failure: true) }
let!(:status4) { create_status(name: 'coverage', status: 'success') }
let!(:status5) { create_status(name: 'coverage', ref: 'develop', status: 'success') }
let!(:status6) { create_status(status: 'success') }
expect(json_response).to be_an Array context 'latest commit statuses' do
expect(statuses_id).to contain_exactly(@status1.id, @status2.id, @status3.id, @status4.id, @status5.id, @status6.id) before { get api(get_url, reporter) }
end
it "should return latest commit statuses for specific ref" do it 'returns latest commit statuses' do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", reporter) expect(response.status).to eq(200)
expect(response.status).to eq(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(@status3.id, @status5.id) expect(statuses_id).to contain_exactly(status3.id, status4.id, status5.id, status6.id)
json_response.sort_by!{ |status| status['id'] }
expect(json_response.map{ |status| status['allow_failure'] }).to eq([true, false, false, false])
end
end
context 'all commit statuses' do
before { get api(get_url, reporter), all: 1 }
it 'returns all commit statuses' do
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(status1.id, status2.id,
status3.id, status4.id,
status5.id, status6.id)
end
end
context 'latest commit statuses for specific ref' do
before { get api(get_url, reporter), ref: 'develop' }
it 'returns latest commit statuses for specific ref' do
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(status3.id, status5.id)
end
end
context 'latest commit statues for specific name' do
before { get api(get_url, reporter), name: 'coverage' }
it 'return latest commit statuses for specific name' do
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(status4.id, status5.id)
end
end
end end
end
it "should return latest commit statuses for specific name" do context 'ci commit does not exist' do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", reporter) before { get api(get_url, reporter) }
expect(response.status).to eq(200)
it 'returns empty array' do
expect(response.status).to eq 200
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(@status3.id, @status4.id) expect(json_response).to be_empty
end end
end end
context "guest user" do context "guest user" do
before { get api(get_url, guest) }
it "should not return project commits" do it "should not return project commits" do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", guest)
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
end end
context "unauthorized user" do context "unauthorized user" do
before { get api(get_url) }
it "should not return project commits" do it "should not return project commits" do
get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses")
expect(response.status).to eq(401) expect(response.status).to eq(401)
end end
end end
end end
describe 'POST /projects/:id/statuses/:sha' do describe 'POST /projects/:id/statuses/:sha' do
let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" } let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" }
context 'developer user' do context 'developer user' do
context 'should create commit status' do context 'only required parameters' do
it 'with only required parameters' do before { post api(post_url, developer), state: 'success' }
post api(post_url, developer), state: 'success'
it 'creates commit status' do
expect(response.status).to eq(201) expect(response.status).to eq(201)
expect(json_response['sha']).to eq(commit.id) expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success') expect(json_response['status']).to eq('success')
...@@ -92,9 +129,17 @@ describe API::CommitStatus, api: true do ...@@ -92,9 +129,17 @@ describe API::CommitStatus, api: true do
expect(json_response['target_url']).to be_nil expect(json_response['target_url']).to be_nil
expect(json_response['description']).to be_nil expect(json_response['description']).to be_nil
end end
end
it 'with all optional parameters' do context 'with all optional parameters' do
post api(post_url, developer), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test' before do
optional_params = { state: 'success', context: 'coverage',
ref: 'develop', target_url: 'url', description: 'test' }
post api(post_url, developer), optional_params
end
it 'creates commit status' do
expect(response.status).to eq(201) expect(response.status).to eq(201)
expect(json_response['sha']).to eq(commit.id) expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success') expect(json_response['status']).to eq('success')
...@@ -105,41 +150,52 @@ describe API::CommitStatus, api: true do ...@@ -105,41 +150,52 @@ describe API::CommitStatus, api: true do
end end
end end
context 'should not create commit status' do context 'invalid status' do
it 'with invalid state' do before { post api(post_url, developer), state: 'invalid' }
post api(post_url, developer), state: 'invalid'
it 'does not create commit status' do
expect(response.status).to eq(400) expect(response.status).to eq(400)
end end
end
it 'without state' do context 'request without state' do
post api(post_url, developer) before { post api(post_url, developer) }
it 'does not create commit status' do
expect(response.status).to eq(400) expect(response.status).to eq(400)
end end
end
it 'invalid commit' do context 'invalid commit' do
post api("/projects/#{project.id}/statuses/invalid_sha", developer), state: 'running' let(:sha) { 'invalid_sha' }
before { post api(post_url, developer), state: 'running' }
it 'returns not found error' do
expect(response.status).to eq(404) expect(response.status).to eq(404)
end end
end end
end end
context 'reporter user' do context 'reporter user' do
before { post api(post_url, reporter) }
it 'should not create commit status' do it 'should not create commit status' do
post api(post_url, reporter)
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
end end
context 'guest user' do context 'guest user' do
before { post api(post_url, guest) }
it 'should not create commit status' do it 'should not create commit status' do
post api(post_url, guest)
expect(response.status).to eq(403) expect(response.status).to eq(403)
end end
end end
context 'unauthorized user' do context 'unauthorized user' do
before { post api(post_url) }
it 'should not create commit status' do it 'should not create commit status' do
post api(post_url)
expect(response.status).to eq(401) expect(response.status).to eq(401)
end end
end end
......
...@@ -54,6 +54,18 @@ describe API::API, api: true do ...@@ -54,6 +54,18 @@ describe API::API, api: true do
project.team << [user, :developer] project.team << [user, :developer]
end end
context "git push with project.wiki" do
it 'responds with success' do
project_wiki = create(:project, name: 'my.wiki', path: 'my.wiki')
project_wiki.team << [user, :developer]
push(key, project_wiki)
expect(response.status).to eq(200)
expect(json_response["status"]).to be_truthy
end
end
context "git pull" do context "git pull" do
it do it do
pull(key, project) pull(key, project)
......
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