diff --git a/CHANGELOG b/CHANGELOG index 0c4e0ac30a3..973425295c1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,10 @@ Please view this file on the master branch, on stable branches it's out of date. v 7.14.0 (unreleased) + - Fix coloring of diffs on MR Discussion-tab (Gert Goet) - Fix "Network" and "Graphs" pages for branches with encoded slashes (Stan Hu) - Fix errors deleting and creating branches with encoded slashes (Stan Hu) + - Always add current user to autocomplete controller to support filter by "Me" (Stan Hu) - Fix multi-line syntax highlighting (Stan Hu) - Fix network graph when branch name has single quotes (Stan Hu) - Add "Confirm user" button in user admin page (Stan Hu) @@ -39,6 +41,10 @@ v 7.14.0 (unreleased) - Remove satellites - Remove comments and email addresses when publicly exposing ssh keys (Zeger-Jan van de Weg) +v 7.13.3 + - Fix bug causing Bitbucket importer to crash when OAuth application had been removed. + - Allow users to send abuse reports + v 7.13.2 - Fix randomly failed spec - Create project services on Project creation diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index ec1cf33c3f6..2714f5313ae 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.6.3 +2.6.4 diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss index 24c828e0d97..bb7b9356c70 100644 --- a/app/assets/stylesheets/generic/mobile.scss +++ b/app/assets/stylesheets/generic/mobile.scss @@ -48,6 +48,10 @@ display: block; } + .project-home-desc { + font-size: 21px; + } + .project-repo-buttons, .git-clone-holder { display: none; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 029b167c38e..29d3dbc25eb 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -30,7 +30,13 @@ } } - .lead { + .project-home-desc { + h1 { + margin: 0; + margin-bottom: 10px; + font-size: 26px; + } + p { display: inline; } diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb new file mode 100644 index 00000000000..65dbd5ef551 --- /dev/null +++ b/app/controllers/abuse_reports_controller.rb @@ -0,0 +1,24 @@ +class AbuseReportsController < ApplicationController + def new + @abuse_report = AbuseReport.new + @abuse_report.user_id = params[:user_id] + end + + def create + @abuse_report = AbuseReport.new(report_params) + @abuse_report.reporter = current_user + + if @abuse_report.save + message = "Thank you for your report. A GitLab administrator will look into it shortly." + redirect_to root_path, notice: message + else + render :new + end + end + + private + + def report_params + params.require(:abuse_report).permit(:user_id, :message) + end +end diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb new file mode 100644 index 00000000000..34f37bca4ad --- /dev/null +++ b/app/controllers/admin/abuse_reports_controller.rb @@ -0,0 +1,11 @@ +class Admin::AbuseReportsController < Admin::ApplicationController + def index + @abuse_reports = AbuseReport.order(id: :desc).page(params[:page]) + end + + def destroy + AbuseReport.find(params[:id]).destroy + + redirect_to admin_abuse_reports_path, notice: 'Report was removed' + end +end diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 52e9c58b47c..5c3ca8e23c9 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -33,6 +33,8 @@ class AutocompleteController < ApplicationController @users = @users.search(params[:search]) if params[:search].present? @users = @users.active @users = @users.page(params[:page]).per(PER_PAGE) + # Always include current user if available to filter by "Me" + @users = User.find(@users.pluck(:id) + [current_user.id]).uniq if current_user render json: @users, only: [:name, :username, :id], methods: [:avatar_url] end diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb new file mode 100644 index 00000000000..c8c39db11bc --- /dev/null +++ b/app/models/abuse_report.rb @@ -0,0 +1,9 @@ +class AbuseReport < ActiveRecord::Base + belongs_to :reporter, class_name: "User" + belongs_to :user + + validates :reporter, presence: true + validates :user, presence: true + validates :message, presence: true + validates :user_id, uniqueness: { scope: :reporter_id } +end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 815672a1bf7..161a16ca61c 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -114,6 +114,9 @@ class Namespace < ActiveRecord::Base end def move_dir + # Ensure old directory exists before moving it + gitlab_shell.add_namespace(path_was) + if gitlab_shell.mv_namespace(path_was, path) # If repositories moved successfully we need to # send update instructions to users. diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml new file mode 100644 index 00000000000..a3b34345a3c --- /dev/null +++ b/app/views/abuse_reports/new.html.haml @@ -0,0 +1,24 @@ +- page_title "Report abuse" +%h3.page-title Report abuse +%p Please use this form to report users who create spam issues or comments or who otherwise behave inappropriately. +%hr += form_for @abuse_report, html: { class: 'form-horizontal'} do |f| + = f.hidden_field :user_id + - if @abuse_report.errors.any? + .alert.alert-danger + - @abuse_report.errors.full_messages.each do |msg| + %p= msg + .form-group + = f.label :user_id, class: 'control-label' + .col-sm-10 + - name = "#{@abuse_report.user.name} (@#{@abuse_report.user.username})" + = text_field_tag :user_name, name, class: "form-control", readonly: true + .form-group + = f.label :message, class: 'control-label' + .col-sm-10 + = f.text_area :message, class: "form-control", rows: 2, required: true + .help-block + Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment. + + .form-actions + = f.submit "Send report", class: "btn btn-create" diff --git a/app/views/admin/abuse_reports/_abuse_report.html.haml b/app/views/admin/abuse_reports/_abuse_report.html.haml new file mode 100644 index 00000000000..4449721ae38 --- /dev/null +++ b/app/views/admin/abuse_reports/_abuse_report.html.haml @@ -0,0 +1,23 @@ +- reporter = abuse_report.reporter +- user = abuse_report.user +%tr + %td + - if reporter + = link_to reporter.name, [:admin, reporter] + - else + (removed) + %td + = abuse_report.created_at.to_s(:short) + %td + = abuse_report.message + %td + - if user + = link_to user.name, [:admin, user] + - else + (removed) + %td + - if user + = link_to 'Block', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-xs btn-warning" + = link_to 'Remove user', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-xs btn-remove" + %td + = link_to 'Remove report', [:admin, abuse_report], method: :delete, class: "btn btn-xs btn-close" diff --git a/app/views/admin/abuse_reports/index.html.haml b/app/views/admin/abuse_reports/index.html.haml new file mode 100644 index 00000000000..4a25848f156 --- /dev/null +++ b/app/views/admin/abuse_reports/index.html.haml @@ -0,0 +1,17 @@ +- page_title "Abuse Reports" +%h3.page-title Abuse Reports +%hr +- if @abuse_reports.present? + %table.table + %thead + %tr + %th Reported by + %th Reported at + %th Message + %th User + %th + %th + = render @abuse_reports + = paginate @abuse_reports +- else + %h4 There are no abuse reports diff --git a/app/views/explore/projects/_project.html.haml b/app/views/explore/projects/_project.html.haml index d769c91545d..1e8a89e3661 100644 --- a/app/views/explore/projects/_project.html.haml +++ b/app/views/explore/projects/_project.html.haml @@ -9,8 +9,8 @@ .project-info - if project.description.present? - %p.project-description.str-truncated - = project.description + .project-description.str-truncated + = markdown(project.description, pipeline: :description) .repo-info - unless project.empty_repo? diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index a3191593dae..2065be3828a 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -57,6 +57,13 @@ %span Service Templates + = nav_link(controller: :abuse_reports) do + = link_to admin_abuse_reports_path, title: "Abuse reports" do + = icon('exclamation-circle fw') + %span + Abuse Reports + %span.count= AbuseReport.count(:all) + = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = link_to admin_application_settings_path, title: 'Settings', data: {placement: 'right'} do = icon('cogs fw') diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 159ce1ecf3f..71e92970305 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -3,6 +3,7 @@ .project-identicon-holder = project_icon(@project, alt: '', class: 'project-avatar avatar s90') .project-home-desc.lead + %h1= @project.name - if @project.description.present? = markdown(@project.description, pipeline: :description) diff --git a/app/views/projects/merge_requests/widget/open/_conflicts.html.haml b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml index d1db5fec43a..7dc3b4eb2cc 100644 --- a/app/views/projects/merge_requests/widget/open/_conflicts.html.haml +++ b/app/views/projects/merge_requests/widget/open/_conflicts.html.haml @@ -1,7 +1,8 @@ - if @merge_request.can_be_merged_by?(current_user) %h4 This merge request contains merge conflicts that must be resolved. - You can try it manually on the + %p + You can merge it manually using the %strong = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" - else diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml index 711aa39101b..0301445b5b2 100644 --- a/app/views/projects/notes/discussions/_diff.html.haml +++ b/app/views/projects/notes/discussions/_diff.html.haml @@ -12,18 +12,19 @@ .diff-content %table - note.truncated_diff_lines.each do |line| + - type = line.type - line_code = generate_line_code(note.file_path, line) - %tr.line_holder{ id: line_code } - - if line.type == "match" + %tr.line_holder{ id: line_code, class: "#{type}" } + - if type == "match" %td.old_line= "..." %td.new_line= "..." %td.line_content.matched= line.text - else - %td.old_line{class: line.type == "new" ? "new" : "old"} - = raw(line.type == "new" ? " " : line.old_pos) - %td.new_line{class: line.type == "new" ? "new" : "old"} - = raw(line.type == "old" ? " " : line.new_pos) - %td.line_content{class: "noteable_line #{line.type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line.text) + %td.old_line + = raw(type == "new" ? " " : line.old_pos) + %td.new_line + = raw(type == "old" ? " " : line.new_pos) + %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line.text) - if line_code == note.line_code = render "projects/notes/diff_notes_with_reply", notes: discussion_notes diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 43d847831d6..64b7f25ad37 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -18,6 +18,16 @@ = link_to profile_path, class: 'btn btn-sm' do %i.fa.fa-pencil-square-o Edit Profile settings + - elsif current_user + .pull-right + %span.dropdown + %a.light.dropdown-toggle.btn.btn-sm{href: '#', "data-toggle" => "dropdown"} + = icon('exclamation-circle') + %ul.dropdown-menu.dropdown-menu-right + %li + = link_to new_abuse_report_path(user_id: @user.id) do + Report abuse + .username @#{@user.username} .description diff --git a/config/routes.rb b/config/routes.rb index f252a6fcc91..d7307a61ede 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -65,6 +65,9 @@ Gitlab::Application.routes.draw do end end + # Spam reports + resources :abuse_reports, only: [:new, :create] + # # Import # @@ -165,6 +168,7 @@ Gitlab::Application.routes.draw do end end + resources :abuse_reports, only: [:index, :destroy] resources :applications resources :groups, constraints: { id: /[^\/]+/ } do diff --git a/db/migrate/20150806104937_create_abuse_reports.rb b/db/migrate/20150806104937_create_abuse_reports.rb new file mode 100644 index 00000000000..e97dc4cf04c --- /dev/null +++ b/db/migrate/20150806104937_create_abuse_reports.rb @@ -0,0 +1,11 @@ +class CreateAbuseReports < ActiveRecord::Migration + def change + create_table :abuse_reports do |t| + t.integer :reporter_id + t.integer :user_id + t.text :message + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 36b500c5420..6e919f2883b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,11 +11,19 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150730122406) do +ActiveRecord::Schema.define(version: 20150806104937) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "abuse_reports", force: true do |t| + t.integer "reporter_id" + t.integer "user_id" + t.text "message" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "application_settings", force: true do |t| t.integer "default_projects_limit" t.boolean "signup_enabled" diff --git a/features/abuse_report.feature b/features/abuse_report.feature new file mode 100644 index 00000000000..3e1cb455b77 --- /dev/null +++ b/features/abuse_report.feature @@ -0,0 +1,10 @@ +Feature: Abuse reports + Background: + Given I sign in as a user + And user "Mike" exists + + Scenario: Report abuse + Given I visit "Mike" user page + And I click "Report abuse" button + When I fill and submit abuse form + Then I should see success message diff --git a/features/admin/abuse_report.feature b/features/admin/abuse_report.feature new file mode 100644 index 00000000000..7d4ec2556e5 --- /dev/null +++ b/features/admin/abuse_report.feature @@ -0,0 +1,8 @@ +Feature: Admin Abuse reports + Background: + Given I sign in as an admin + And abuse reports exist + + Scenario: Browse abuse reports + When I visit abuse reports page + Then I should see list of abuse reports diff --git a/features/steps/abuse_reports.rb b/features/steps/abuse_reports.rb new file mode 100644 index 00000000000..8f9ddb2899f --- /dev/null +++ b/features/steps/abuse_reports.rb @@ -0,0 +1,28 @@ +class Spinach::Features::AbuseReports < Spinach::FeatureSteps + include SharedAuthentication + + step 'I visit "Mike" user page' do + visit user_path(user_mike) + end + + step 'I click "Report abuse" button' do + click_link 'Report abuse' + end + + step 'I fill and submit abuse form' do + fill_in 'abuse_report_message', with: 'This user send spam' + click_button 'Send report' + end + + step 'I should see success message' do + page.should have_content 'Thank you for your report' + end + + step 'user "Mike" exists' do + user_mike + end + + def user_mike + @user_mike ||= create(:user, name: 'Mike') + end +end diff --git a/features/steps/admin/abuse_reports.rb b/features/steps/admin/abuse_reports.rb new file mode 100644 index 00000000000..0149416c919 --- /dev/null +++ b/features/steps/admin/abuse_reports.rb @@ -0,0 +1,15 @@ +class Spinach::Features::AdminAbuseReports < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedAdmin + + step 'I should see list of abuse reports' do + page.should have_content("Abuse Reports") + page.should have_content AbuseReport.first.message + page.should have_link("Remove user") + end + + step 'abuse reports exist' do + create(:abuse_report) + end +end diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 3e6beb20e78..2a333222fb2 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -137,10 +137,11 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps end step 'I should see the users from the target project ID' do - expect(page).to have_selector('.user-result', visible: true, count: 2) + expect(page).to have_selector('.user-result', visible: true, count: 3) users = page.all('.user-name') expect(users[0].text).to eq 'Unassigned' - expect(users[1].text).to eq @project.users.first.name + expect(users[1].text).to eq current_user.name + expect(users[2].text).to eq @project.users.first.name end # Verify a link is generated against the correct project diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 88a98a37807..bb0cd9ac105 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -139,6 +139,10 @@ module SharedPaths visit admin_root_path end + step 'I visit abuse reports page' do + visit admin_abuse_reports_path + end + step 'I visit admin projects page' do visit admin_namespaces_projects_path end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 1230017c270..3521d690259 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -4,6 +4,7 @@ describe AutocompleteController do let!(:project) { create(:project) } let!(:user) { create(:user) } let!(:user2) { create(:user) } + let!(:non_member) { create(:user) } context 'project members' do before do @@ -61,6 +62,27 @@ describe AutocompleteController do end end + context 'non-member login for public project' do + let!(:project) { create(:project, :public) } + + before do + sign_in(non_member) + project.team << [user, :master] + end + + let(:body) { JSON.parse(response.body) } + + describe 'GET #users with project ID' do + before do + get(:users, project_id: project.id) + end + + it { expect(body).to be_kind_of(Array) } + it { expect(body.size).to eq 2 } + it { expect(body.map { |u| u['username'] }).to match_array([user.username, non_member.username]) } + end + end + context 'all users' do before do sign_in(user) diff --git a/spec/factories/abuse_reports.rb b/spec/factories/abuse_reports.rb new file mode 100644 index 00000000000..29fcbc5e197 --- /dev/null +++ b/spec/factories/abuse_reports.rb @@ -0,0 +1,9 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :abuse_report do + reporter factory: :user + user + message 'User sends spam' + end +end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index c8d44efdc8b..a362c54b9ad 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -22,9 +22,9 @@ feature 'Project', feature: true do end it 'sanitizes unwanted tags' do - project.update_attribute(:description, '# Project Description') + project.update_attribute(:description, "```\ncode\n```") visit path - expect(page).not_to have_css('.project-home-desc h1') + expect(page).not_to have_css('.project-home-desc code') end it 'permits `rel` attribute on links' do diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb new file mode 100644 index 00000000000..d83004a8388 --- /dev/null +++ b/spec/models/abuse_report_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe AbuseReport, type: :model do + subject { create(:abuse_report) } + + it { expect(subject).to be_valid } +end