Commit 811505c7 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' of dev.gitlab.org:gitlab/gitlabhq into ce-to-ee

Conflicts:
	app/controllers/projects/team_members_controller.rb
	app/views/groups/members.html.haml
parents fe51c666 8c47a72a
......@@ -877,7 +877,7 @@ Lint/ParenthesesAsGroupedExpression:
Checks for method calls with a space before the opening
parenthesis.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces'
Enabled: false
Enabled: true
Lint/RequireParentheses:
Description: >-
......
Please view this file on the master branch, on stable branches it's out of date.
v 7.9.0 (unreleased)
- Fix merge request URL passed to Webhooks. (Stan Hu)
- Move labels/milestones tabs to sidebar
- Upgrade Rails gem to version 4.1.9.
- Improve error messages for file edit failures
......@@ -15,6 +16,8 @@ v 7.9.0 (unreleased)
- Fix mass-unassignment of issues (Robert Speicher)
- Allow user confirmation to be skipped for new users via API
- Add a service to send updates to an Irker gateway (Romain Coltel)
- Add brakeman (security scanner for Ruby on Rails)
- Slack username and channel options
v 7.8.1
- Fix run of custom post receive hooks
......@@ -91,7 +94,6 @@ v 7.8.0
- Improve database performance for GitLab
- Add Asana service (Jeremy Benoist)
- Improve project web hooks with extra data
- Slack username and channel options
v 7.7.2
- Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch
......
......@@ -201,6 +201,7 @@ gem "virtus"
gem 'addressable'
group :development do
gem 'brakeman', require: false
gem "annotate", "~> 2.6.0.beta2"
gem "letter_opener"
gem 'quiet_assets', '~> 1.0.1'
......
......@@ -63,6 +63,16 @@ GEM
bootstrap-sass (3.3.3)
autoprefixer-rails (>= 5.0.0.1)
sass (>= 3.2.19)
brakeman (3.0.1)
erubis (~> 2.6)
fastercsv (~> 1.5)
haml (>= 3.0, < 5.0)
highline (~> 1.6.20)
multi_json (~> 1.2)
ruby2ruby (~> 2.1.1)
ruby_parser (~> 3.5.0)
sass (~> 3.0)
terminal-table (~> 1.4)
browser (0.7.2)
builder (3.2.2)
byebug (3.2.0)
......@@ -154,6 +164,7 @@ GEM
multipart-post (~> 1.2.0)
faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9)
fastercsv (1.5.5)
ffaker (1.22.1)
ffi (1.9.3)
fog (1.21.0)
......@@ -258,6 +269,7 @@ GEM
haml (>= 3.1, < 5.0)
railties (>= 4.0.1)
hashie (2.1.2)
highline (1.6.21)
hike (1.2.3)
hipchat (1.4.0)
httparty
......@@ -497,6 +509,11 @@ GEM
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4)
ruby-progressbar (1.7.1)
ruby2ruby (2.1.3)
ruby_parser (~> 3.1)
sexp_processor (~> 4.0)
ruby_parser (3.5.0)
sexp_processor (~> 4.1)
rubyntlm (0.4.0)
rubypants (0.2.0)
rugged (0.21.4)
......@@ -522,6 +539,7 @@ GEM
select2-rails (3.5.2)
thor (~> 0.14)
settingslogic (2.0.9)
sexp_processor (4.4.5)
shoulda-matchers (2.7.0)
activesupport (>= 3.0.0)
sidekiq (3.3.0)
......@@ -577,6 +595,7 @@ GEM
temple (0.6.7)
term-ansicolor (1.2.2)
tins (~> 0.8)
terminal-table (1.4.5)
test_after_commit (0.2.2)
therubyracer (0.12.0)
libv8 (~> 3.16.14.0)
......@@ -656,6 +675,7 @@ DEPENDENCIES
better_errors
binding_of_caller
bootstrap-sass (~> 3.0)
brakeman
browser
byebug
cal-heatmap-rails (~> 0.0.1)
......
......@@ -11,6 +11,7 @@ class @ProjectUsersSelect
Api.projectUsers project_id, query.term, (users) ->
data = { results: users }
if query.term.length == 0
nullUser = {
name: 'Unassigned',
avatar: null,
......
......@@ -3,7 +3,7 @@ class Import::BaseController < ApplicationController
private
def get_or_create_namespace
existing_namespace = Namespace.find_by("path = ? OR name = ?", @target_namespace, @target_namespace)
existing_namespace = Namespace.find_by_path_or_name(@target_namespace)
if existing_namespace
if existing_namespace.owner == current_user
......
......@@ -26,7 +26,7 @@ class Projects::ImportsController < Projects::ApplicationController
def show
unless @project.import_in_progress?
if @project.import_finished?
redirect_to(@project) and return
redirect_to(project_path(@project)) and return
else
redirect_to new_namespace_project_import_path(@project.namespace,
@project) && return
......
......@@ -30,12 +30,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
AuditEventService.new(current_user, @project, details).security_event
end
if params[:redirect_to]
redirect_to params[:redirect_to]
else
redirect_to namespace_project_team_index_path(@project.namespace,
@project)
end
redirect_to namespace_project_team_index_path(@project.namespace, @project)
end
def update
......
......@@ -97,7 +97,7 @@ class Projects::WikisController < Projects::ApplicationController
@project_wiki.wiki
rescue ProjectWiki::CouldNotCreateWikiError => ex
flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
redirect_to @project
redirect_to project_path(@project)
return false
end
......
class ProjectsController < ApplicationController
prepend_before_filter :render_go_import, only: [:show]
skip_before_filter :authenticate_user!, only: [:show]
before_filter :project, except: [:new, :create]
before_filter :repository, except: [:new, :create]
......@@ -185,4 +186,14 @@ class ProjectsController < ApplicationController
end
end
end
def render_go_import
return unless params["go-get"] == "1"
@namespace = params[:namespace_id]
@id = params[:project_id] || params[:id]
@id = @id.gsub(/\.git\Z/, "")
render "go_import", layout: false
end
end
......@@ -3,22 +3,53 @@ class UploadsController < ApplicationController
before_filter :authorize_access
def show
model = params[:model].camelize.constantize.find(params[:id])
uploader = model.send(params[:mounted_as])
unless upload_model && upload_mount
return not_found!
end
return not_found! if model.respond_to?(:project) && !can?(current_user, :read_project, model.project)
model = upload_model.find(params[:id])
uploader = model.send(upload_mount)
return redirect_to uploader.url unless uploader.file_storage?
if model.respond_to?(:project) && !can?(current_user, :read_project, model.project)
return not_found!
end
return not_found! unless uploader.file.exists?
unless uploader.file_storage?
return redirect_to uploader.url
end
unless uploader.file.exists?
return not_found!
end
disposition = uploader.image? ? 'inline' : 'attachment'
send_file uploader.file.path, disposition: disposition
end
private
def authorize_access
unless params[:mounted_as] == 'avatar'
authenticate_user! && reject_blocked!
end
end
def upload_model
upload_models = {
user: User,
project: Project,
note: Note,
group: Group
}
upload_models[params[:model].to_sym]
end
def upload_mount
upload_mounts = %w(avatar attachment file)
if upload_mounts.include?(params[:mounted_as])
params[:mounted_as]
end
end
end
......@@ -48,6 +48,11 @@ class Namespace < ActiveRecord::Base
where('lower(path) = :value', value: path.downcase).first
end
# Case insensetive search for namespace by path or name
def self.find_by_path_or_name(path)
find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase)
end
def self.search(query)
where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
end
......
......@@ -146,7 +146,7 @@ class Repository
end
def timestamps_by_user_log(user)
args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --pretty=format:%cd --date=short)
args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --branches --pretty=format:%cd --date=short)
dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
if dates.present?
......
......@@ -44,7 +44,7 @@
Projects (#{@projects.total_count})
.panel-head-actions
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
......
......@@ -9,7 +9,7 @@
.pull-right
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
......
......@@ -8,7 +8,7 @@
.pull-right
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
......
......@@ -14,7 +14,8 @@
%strong= member.human_access
- if show_controls
- if can?(current_user, :modify, member)
= link_to '#', class: "btn-tiny btn js-toggle-button", title: 'Edit access level' do
= button_tag class: "btn-tiny btn js-toggle-button",
title: 'Edit access level', type: 'button' do
%i.fa.fa-pencil-square-o
- if can?(current_user, :destroy, member)
- if current_user == member.user
......
......@@ -21,7 +21,7 @@
= link_to reset_access_group_ldap_path(@group), class: 'btn btn-grouped', data: { confirm: "Force GitLab to do LDAP permission checks for all group members? All members besides yourself will be reduced to 'Guest' access until their next interaction with GitLab." }, method: :put do
Clear LDAP permission cache
= link_to '#', class: 'btn btn-new js-toggle-button' do
= button_tag class: 'btn btn-new js-toggle-button', type: 'button' do
Add members
%i.fa.fa-chevron-down
......
......@@ -8,7 +8,7 @@
New branch
&nbsp;
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= @sort.humanize
......
!!! 5
%html
%head
- web_url = [Gitlab.config.gitlab.url, @namespace, @id].join('/')
%meta{name: "go-import", content: "#{web_url.split('://')[1]} git #{web_url}.git"}
.dropdown.inline
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn.btn-small{type: 'button', 'data-toggle' => 'dropdown'}
%i.fa.fa-tags
%span.light Group:
- if @group.present?
......@@ -17,7 +17,7 @@
= group.name
.dropdown.inline.prepend-left-10.project-filter
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn.btn-small{type: 'button', 'data-toggle' => 'dropdown'}
%i.fa.fa-tags
%span.light Project:
- if @project.present?
......
......@@ -22,7 +22,7 @@
disabled: !can?(current_user, :modify_issue, @project)
.issues-other-filters
.dropdown.inline.assignee-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-user
%span.light assignee:
- if @assignee.present?
......@@ -45,7 +45,7 @@
= user.name
.dropdown.inline.prepend-left-10.author-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-user
%span.light author:
- if @author.present?
......@@ -68,7 +68,7 @@
= user.name
.dropdown.inline.prepend-left-10.milestone-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-clock-o
%span.light milestone:
- if @milestone.present?
......@@ -92,7 +92,7 @@
- if @project
.dropdown.inline.prepend-left-10.labels-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"}
%i.fa.fa-tags
%span.light label:
- if params[:label_name].present?
......
.dropdown.inline.prepend-left-10
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
......
......@@ -37,7 +37,10 @@ bundle install --deployment --path vendor/bundle (Setup)
cp config/gitlab.yml.example config/gitlab.yml (Setup)
bundle exec rake db:create (Setup)
bundle exec rake spinach (Thread #1)
bundle exec rake spec (Thread #2)
bundle exec rake spec (thread #2)
bundle exec rake rubocop (thread #3)
bundle exec rake brakeman (thread #4)
bundle exec rake jasmine:ci (thread #5)
```
Use rubygems mirror.
# Installation
# Installation from source
## Consider the Omnibus package installation
......
......@@ -29,7 +29,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'I select user "Mary Jane" from list with role "Reporter"' do
user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane")
click_link 'Add members'
click_button 'Add members'
within ".users-group-form" do
select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "access_level"
......
......@@ -153,7 +153,7 @@ module API
class ProjectEntity < Grape::Entity
expose :id, :iid
expose (:project_id) { |entity| entity.project.id }
expose(:project_id) { |entity| entity.project.id }
expose :title, :description
expose :state, :created_at, :updated_at
end
......
......@@ -16,6 +16,17 @@ module API
#
post "/allowed" do
status 200
actor = if params[:key_id]
Key.find_by(id: params[:key_id])
elsif params[:user_id]
User.find_by(id: params[:user_id])
end
unless actor
return Gitlab::GitAccessStatus.new(false, 'No such user or key')
end
project_path = params[:project]
# Check for *.wiki repositories.
......@@ -32,21 +43,8 @@ module API
project = Project.find_with_namespace(project_path)
unless project
return Gitlab::GitAccessStatus.new(false, 'No such project')
end
actor = if params[:key_id]
Key.find_by(id: params[:key_id])
elsif params[:user_id]
User.find_by(id: params[:user_id])
end
unless actor
return Gitlab::GitAccessStatus.new(false, 'No such user or key')
end
access.check(
if project
status = access.check(
actor,
params[:action],
project,
......@@ -54,6 +52,13 @@ module API
)
end
if project && status && status.allowed?
status
else
Gitlab::GitAccessStatus.new(false, 'No such project')
end
end
#
# Discover user by ssh key
#
......
......@@ -10,8 +10,9 @@ module Grack
@request = Rack::Request.new(env)
@auth = Request.new(env)
# Need this patch due to the rails mount
@gitlab_ci = false
# Need this patch due to the rails mount
# Need this if under RELATIVE_URL_ROOT
unless Gitlab.config.gitlab.relative_url_root.empty?
# If website is mounted using relative_url_root need to remove it first
......@@ -22,8 +23,12 @@ module Grack
@env['SCRIPT_NAME'] = ""
if project
auth!
if project && authorized_request?
@app.call(env)
elsif @user.nil? && !@gitlab_ci
unauthorized
else
render_not_found
end
......@@ -32,7 +37,8 @@ module Grack
private
def auth!
if @auth.provided?
return unless @auth.provided?
return bad_request unless @auth.basic?
# Authentication with username and password
......@@ -41,7 +47,8 @@ module Grack
# Allow authentication for GitLab CI service
# if valid token passed
if gitlab_ci_request?(login, password)
return @app.call(env)
@gitlab_ci = true
return
end
@user = authenticate_user(login, password)
......@@ -52,15 +59,8 @@ module Grack
end
end
if authorized_request?
@app.call(env)
else
unauthorized
end
end
def gitlab_ci_request?(login, password)
if login == "gitlab-ci-token" && project.gitlab_ci?
if login == "gitlab-ci-token" && project && project.gitlab_ci?
token = project.gitlab_ci_service.token
if token.present? && token == password && git_cmd == 'git-upload-pack'
......@@ -107,6 +107,8 @@ module Grack
end
def authorized_request?
return true if @gitlab_ci
case git_cmd
when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
if user
......@@ -141,7 +143,9 @@ module Grack
end
def project
@project ||= project_by_path(@request.path_info)
return @project if defined?(@project)
@project = project_by_path(@request.path_info)
end
def project_by_path(path)
......
......@@ -10,6 +10,8 @@ module Gitlab
case @type
when :issue
issue_url(id)
when :merge_request
merge_request_url(id)
end
end
......@@ -22,5 +24,12 @@ module Gitlab
project_id: issue.project,
host: Gitlab.config.gitlab['url'])
end
def merge_request_url(id)
merge_request = MergeRequest.find(id)
project_merge_request_url(id: merge_request.id,
project_id: merge_request.project,
host: Gitlab.config.gitlab['url'])
end
end
end
......@@ -77,16 +77,6 @@ server {
proxy_pass http://gitlab;
}
## If ``go get`` detected, return go-import meta tag.
## This works for public and for private repositories.
## See also http://golang.org/cmd/go/#hdr-Remote_import_paths
if ($http_user_agent ~* "Go") {
return 200 "
<!DOCTYPE html>
<head><meta content='$host$uri git $scheme://$host$uri.git' name='go-import'></head>
</html>";
}
## If a file, which is not found in the root folder is requested,
## then the proxy passes the request to the upsteam (gitlab unicorn).
location @gitlab {
......
......@@ -123,16 +123,6 @@ server {
proxy_pass http://gitlab;
}
## If ``go get`` detected, return go-import meta tag.
## This works for public and for private repositories.
## See also http://golang.org/cmd/go/#hdr-Remote_import_paths
if ($http_user_agent ~* "Go") {
return 200 "
<!DOCTYPE html>
<head><meta content='$host$uri git $scheme://$host$uri.git' name='go-import'></head>
</html>";
}
## If a file, which is not found in the root folder is requested,
## then the proxy passes the request to the upsteam (gitlab unicorn).
location @gitlab {
......
desc 'Security check via brakeman'
task :brakeman do
if system("brakeman --skip-files lib/backup/repository.rb -w3 -z")
exit 0
else
puts 'Security check failed'
exit 1
end
end
......@@ -9,5 +9,5 @@ unless Rails.env.production?
require 'coveralls/rake/task'
Coveralls::RakeTask.new
desc "GITLAB | Run all tests on CI with simplecov"
task :test_ci => [:rubocop, :spinach, :spec, 'coveralls:push']
task :test_ci => [:rubocop, :brakeman, 'jasmine:ci', :spinach, :spec, 'coveralls:push']
end
......@@ -7,6 +7,22 @@ describe ProjectsController do
let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
describe "GET show" do
context "when requested by `go get`" do
render_views
it "renders the go-import meta tag" do
get :show, "go-get" => "1", namespace_id: "bogus_namespace", id: "bogus_project"
expect(response.body).to include("name='go-import'")
content = "localhost/bogus_namespace/bogus_project git http://localhost/bogus_namespace/bogus_project.git"
expect(response.body).to include("content='#{content}'")
end
end
end
describe "POST #toggle_star" do
it "toggles star if user is signed in" do
sign_in(user)
......
require "spec_helper"
describe Grack::Auth do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:app) { lambda { |env| [200, {}, "Success!"] } }
let!(:auth) { Grack::Auth.new(app) }
let(:env) {
{
"rack.input" => "",
"REQUEST_METHOD" => "GET",
"QUERY_STRING" => "service=git-upload-pack"
}
}
let(:status) { auth.call(env).first }
describe "#call" do
context "when the project doesn't exist" do
before do
env["PATH_INFO"] = "doesnt/exist.git"
end
context "when no authentication is provided" do
it "responds with status 401" do
expect(status).to eq(401)
end
end
context "when username and password are provided" do
context "when authentication fails" do
before do
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope")
end
it "responds with status 401" do
expect(status).to eq(401)
end
end
context "when authentication succeeds" do
before do
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
end
it "responds with status 404" do
expect(status).to eq(404)
end
end
end
end
context "when the project exists" do
before do
env["PATH_INFO"] = project.path_with_namespace + ".git"
end
context "when the project is public" do
before do
project.update_attribute(:visibility_level, Project::PUBLIC)
end
it "responds with status 200" do
expect(status).to eq(200)
end
end
context "when the project is private" do
before do
project.update_attribute(:visibility_level, Project::PRIVATE)
end
context "when no authentication is provided" do
it "responds with status 401" do
expect(status).to eq(401)
end
end
context "when username and password are provided" do
context "when authentication fails" do
before do
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, "nope")
end
it "responds with status 401" do
expect(status).to eq(401)
end
end
context "when authentication succeeds" do
before do
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
end
context "when the user has access to the project" do
before do
project.team << [user, :master]
end
context "when the user is blocked" do
before do
user.block
project.team << [user, :master]
end
it "responds with status 404" do
expect(status).to eq(404)
end
end
context "when the user isn't blocked" do
it "responds with status 200" do
expect(status).to eq(200)
end
end
end
context "when the user doesn't have access to the project" do
it "responds with status 404" do
expect(status).to eq(404)
end
end
end
end
context "when a gitlab ci token is provided" do
let(:token) { "123" }
before do
gitlab_ci_service = project.build_gitlab_ci_service
gitlab_ci_service.active = true
gitlab_ci_service.token = token
gitlab_ci_service.project_url = "http://google.com"
gitlab_ci_service.save
env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token)
end
it "responds with status 200" do
expect(status).to eq(200)
end
end
end
end
end
end
......@@ -8,4 +8,12 @@ describe Gitlab::UrlBuilder do
expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}"
end
end
describe 'When asking for an merge request' do
it 'returns the merge request url' do
merge_request = create(:merge_request)
url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id)
expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.to_param}/merge_requests/#{merge_request.id}"
end
end
end
......@@ -75,4 +75,14 @@ describe Namespace do
expect(namespace.rm_dir).to be_truthy
end
end
describe :find_by_path_or_name do
before do
@namespace = create(:namespace, name: 'WoW', path: 'woW')
end
it { expect(Namespace.find_by_path_or_name('wow')).to eq(@namespace) }
it { expect(Namespace.find_by_path_or_name('WOW')).to eq(@namespace) }
it { expect(Namespace.find_by_path_or_name('unknown')).to eq(nil) }
end
end
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