From 9bfa690b7d0a14bdd2791fe62f46dd38f57aa23e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 9 Feb 2018 15:58:28 +0100 Subject: add project import spec --- spec/requests/api/project_import_spec.rb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 spec/requests/api/project_import_spec.rb diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb new file mode 100644 index 00000000000..51980dc1872 --- /dev/null +++ b/spec/requests/api/project_import_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe API::ProjectImport do + let(:export_path) { "#{Dir.tmpdir}/project_export_spec" } + let(:user) { create(:user) } + let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } + let(:namespace){ create(:group) } + before do + allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + + group.add_owner(user) + end + + after do + FileUtils.rm_rf(export_path, secure: true) + end + + it 'schedules an import' do + expect_any_instance_of(Project).to receive(:import_schedule) + + post "/projects/import", file: file, namespace: namespace.full_path + + expect(project.status).to eq('started') + end +end -- cgit v1.2.3 From de3edb7178bd7df2f72ef403e57bfdf4e6f732df Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 12 Feb 2018 10:13:08 +0100 Subject: add more specs --- spec/requests/api/project_import_spec.rb | 34 +++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 51980dc1872..bf7bde0325a 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -4,7 +4,7 @@ describe API::ProjectImport do let(:export_path) { "#{Dir.tmpdir}/project_export_spec" } let(:user) { create(:user) } let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } - let(:namespace){ create(:group) } + let(:namespace) { create(:group) } before do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) @@ -15,11 +15,35 @@ describe API::ProjectImport do FileUtils.rm_rf(export_path, secure: true) end - it 'schedules an import' do - expect_any_instance_of(Project).to receive(:import_schedule) + describe 'POST /projects/import' do - post "/projects/import", file: file, namespace: namespace.full_path + it 'schedules an import' do + expect_any_instance_of(Project).to receive(:import_schedule) - expect(project.status).to eq('started') + post api('/projects/import', user), file: file, namespace: namespace.full_path + + expect(project.status).to eq('started') + end + end + + describe 'GET /projects/:id/import' do + it 'returns the import status' do + project = create(:project, import_status: 'started') + + get api("/projects/#{project.id}/import", user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to eq('import_status' => 'started') + end + + it 'returns the import status and the error if failed' do + project = create(:project, import_status: 'failed', import_error: 'error') + + get api("/projects/#{project.id}/import", user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to eq('import_status' => 'failed', + 'import_error' => 'error') + end end end -- cgit v1.2.3 From 848f49801d2c45227c525fb5ecdf8b71e7711ded Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 12 Feb 2018 12:40:55 +0100 Subject: add entity and update spec --- lib/api/api.rb | 1 + lib/api/entities.rb | 5 +++++ spec/requests/api/project_import_spec.rb | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/api/api.rb b/lib/api/api.rb index e953f3d2eca..754549f72f0 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -138,6 +138,7 @@ module API mount ::API::PagesDomains mount ::API::Pipelines mount ::API::PipelineSchedules + mount ::API::ProjectImport mount ::API::ProjectHooks mount ::API::Projects mount ::API::ProjectMilestones diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 7838de13c56..8fbad2b6959 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -90,6 +90,11 @@ module API expose :created_at end + class ProjectImportStatus < ProjectIdentity + expose :import_status + expose :import_error, if: :import_error + end + class BasicProjectDetails < ProjectIdentity include ::API::ProjectsRelationBuilder diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index bf7bde0325a..55df2d34419 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -33,7 +33,7 @@ describe API::ProjectImport do get api("/projects/#{project.id}/import", user) expect(response).to have_gitlab_http_status(200) - expect(json_response).to eq('import_status' => 'started') + expect(json_response).to include('import_status' => 'started') end it 'returns the import status and the error if failed' do @@ -42,7 +42,7 @@ describe API::ProjectImport do get api("/projects/#{project.id}/import", user) expect(response).to have_gitlab_http_status(200) - expect(json_response).to eq('import_status' => 'failed', + expect(json_response).to include('import_status' => 'failed', 'import_error' => 'error') end end -- cgit v1.2.3 From 82ff66ef31d6ff8ba2332f51b38f79b3bd7d64a5 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 12 Feb 2018 12:42:33 +0100 Subject: add post import API endpoint --- lib/api/project_import.rb | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 lib/api/project_import.rb diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb new file mode 100644 index 00000000000..4bbb78a62f9 --- /dev/null +++ b/lib/api/project_import.rb @@ -0,0 +1,48 @@ +module API + class ProjectImport < Grape::API + include PaginationParams + + helpers do + def import_params + declared_params(include_missing: false) + end + + def file_is_valid? + import_params[:file] && import_params[:file].respond_to?(:read) + end + end + + before do + not_found! unless Gitlab::CurrentSettings.import_sources.include?('gitlab_project') + end + + params do + optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' + requires :file, type: File, desc: 'The project export file to be imported' + end + resource :projects do + desc 'Get export status' do + success Entities::ProjectImportStatus + end + post 'import' do + render_api_error!('The branch refname is invalid', 400) unless file_is_valid? + + namespace = import_params[:namespace] + + namespace = if namespace && namespace =~ /^\d+$/ + Namespace.find_by(id: namespace) + elsif namespace.blank? + current_user.namespace + else + Namespace.find_by_path_or_name(namespace) + end + + project = ::Projects::GitlabProjectsImportService.new(current_user, import_params).execute + + render_api_error!(link.project.full_messages.first, 400) unless project.saved? + + present project, with: Entities::ProjectImportStatus + end + end + end +end -- cgit v1.2.3 From d3b3f5d1b4def5e87f90a2347acc0b0ee8edc80a Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 12 Feb 2018 14:46:47 +0100 Subject: update import API and spec --- lib/api/project_import.rb | 7 +++++-- spec/requests/api/project_import_spec.rb | 5 ++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index 4bbb78a62f9..396c316af22 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -17,6 +17,7 @@ module API end params do + requires :name, type: String, desc: 'The new project name' optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' requires :file, type: File, desc: 'The project export file to be imported' end @@ -37,9 +38,11 @@ module API Namespace.find_by_path_or_name(namespace) end - project = ::Projects::GitlabProjectsImportService.new(current_user, import_params).execute + project_params = import_params.merge(namespace: namespace.id) - render_api_error!(link.project.full_messages.first, 400) unless project.saved? + project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute + + render_api_error!(project.full_messages.first, 400) unless project.saved? present project, with: Entities::ProjectImportStatus end diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 55df2d34419..b810c81108c 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -16,11 +16,10 @@ describe API::ProjectImport do end describe 'POST /projects/import' do - it 'schedules an import' do expect_any_instance_of(Project).to receive(:import_schedule) - post api('/projects/import', user), file: file, namespace: namespace.full_path + post api('/projects/import', user), name: 'test', file: file, namespace: namespace.full_path expect(project.status).to eq('started') end @@ -43,7 +42,7 @@ describe API::ProjectImport do expect(response).to have_gitlab_http_status(200) expect(json_response).to include('import_status' => 'failed', - 'import_error' => 'error') + 'import_error' => 'error') end end end -- cgit v1.2.3 From 516d33f5ac8f65d8d69d1e5e88efbf0faabbe0eb Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 12 Feb 2018 15:26:59 +0100 Subject: update import API and spec --- lib/api/project_import.rb | 4 ++-- spec/requests/api/project_import_spec.rb | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index 396c316af22..1b63f4d4d9f 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -17,7 +17,7 @@ module API end params do - requires :name, type: String, desc: 'The new project name' + requires :path, type: String, desc: 'The new project path and name' optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' requires :file, type: File, desc: 'The project export file to be imported' end @@ -38,7 +38,7 @@ module API Namespace.find_by_path_or_name(namespace) end - project_params = import_params.merge(namespace: namespace.id) + project_params = import_params.merge(namespace_id: namespace.id) project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index b810c81108c..0875c1cc149 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -8,7 +8,7 @@ describe API::ProjectImport do before do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) - group.add_owner(user) + namespace.add_owner(user) end after do @@ -19,9 +19,11 @@ describe API::ProjectImport do it 'schedules an import' do expect_any_instance_of(Project).to receive(:import_schedule) - post api('/projects/import', user), name: 'test', file: file, namespace: namespace.full_path + post api('/projects/import', user), path: 'test-import', file: file, namespace: namespace.full_path - expect(project.status).to eq('started') + expect(response).to have_gitlab_http_status(200) + + expect(Project.find_by_name('test-import').first.status).to eq('started') end end -- cgit v1.2.3 From 7ec1a022b79c68dd3232c0abf07d119f0dad808f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 12 Feb 2018 16:02:15 +0100 Subject: fix file upload --- lib/api/project_import.rb | 7 ++++--- spec/requests/api/project_import_spec.rb | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index 1b63f4d4d9f..5a4e4189a58 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -8,7 +8,7 @@ module API end def file_is_valid? - import_params[:file] && import_params[:file].respond_to?(:read) + import_params[:file] && import_params[:file]['tempfile'].respond_to?(:read) end end @@ -26,7 +26,7 @@ module API success Entities::ProjectImportStatus end post 'import' do - render_api_error!('The branch refname is invalid', 400) unless file_is_valid? + render_api_error!('The file is invalid', 400) unless file_is_valid? namespace = import_params[:namespace] @@ -38,7 +38,8 @@ module API Namespace.find_by_path_or_name(namespace) end - project_params = import_params.merge(namespace_id: namespace.id) + project_params = import_params.merge(namespace_id: namespace.id, + file: import_params[:file]['tempfile']) project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 0875c1cc149..3c9a05f93ea 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -19,9 +19,9 @@ describe API::ProjectImport do it 'schedules an import' do expect_any_instance_of(Project).to receive(:import_schedule) - post api('/projects/import', user), path: 'test-import', file: file, namespace: namespace.full_path + post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path - expect(response).to have_gitlab_http_status(200) + expect(response).to have_gitlab_http_status(201) expect(Project.find_by_name('test-import').first.status).to eq('started') end -- cgit v1.2.3 From 583ed0eb94ff938b4986491e27af5f3c97ea6baf Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 13 Feb 2018 09:24:10 +0100 Subject: add import status endpoint --- lib/api/project_import.rb | 26 ++++++++++++++++++-------- spec/requests/api/project_import_spec.rb | 4 ++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index 5a4e4189a58..d554d6d12bd 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -16,12 +16,13 @@ module API not_found! unless Gitlab::CurrentSettings.import_sources.include?('gitlab_project') end - params do - requires :path, type: String, desc: 'The new project path and name' - optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' - requires :file, type: File, desc: 'The project export file to be imported' - end - resource :projects do + resource :projects, requirements: { id: %r{[^/]+} } do + + params do + requires :path, type: String, desc: 'The new project path and name' + optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' + requires :file, type: File, desc: 'The project export file to be imported' + end desc 'Get export status' do success Entities::ProjectImportStatus end @@ -40,13 +41,22 @@ module API project_params = import_params.merge(namespace_id: namespace.id, file: import_params[:file]['tempfile']) - project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute - render_api_error!(project.full_messages.first, 400) unless project.saved? + render_api_error!(project&.full_messages&.first, 400) unless project&.saved? present project, with: Entities::ProjectImportStatus end + + params do + requires :id, type: String, desc: 'The ID of a project' + end + desc 'Get export status' do + success Entities::ProjectImportStatus + end + get ':id/import' do + present user_project, with: Entities::ProjectImportStatus + end end end end diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 3c9a05f93ea..cbea0229b09 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -22,14 +22,13 @@ describe API::ProjectImport do post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path expect(response).to have_gitlab_http_status(201) - - expect(Project.find_by_name('test-import').first.status).to eq('started') end end describe 'GET /projects/:id/import' do it 'returns the import status' do project = create(:project, import_status: 'started') + project.add_master(user) get api("/projects/#{project.id}/import", user) @@ -39,6 +38,7 @@ describe API::ProjectImport do it 'returns the import status and the error if failed' do project = create(:project, import_status: 'failed', import_error: 'error') + project.add_master(user) get api("/projects/#{project.id}/import", user) -- cgit v1.2.3 From 4a0d56daacc3f1825c5f9644a0ea63842fa9da7d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 13 Feb 2018 09:47:47 +0100 Subject: fix entity --- lib/api/entities.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 8fbad2b6959..ffdb06b8094 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -92,7 +92,9 @@ module API class ProjectImportStatus < ProjectIdentity expose :import_status - expose :import_error, if: :import_error + + # TODO: Use `expose_nil` once we upgrade the grape-entity gem + expose :import_error, if: lambda { |status, _ops| status.import_error } end class BasicProjectDetails < ProjectIdentity -- cgit v1.2.3 From 79879145e5cfb3837b3a2f77fb7c35bd1234068c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 13 Feb 2018 10:54:04 +0100 Subject: add more specs --- lib/api/project_import.rb | 10 ++++------ spec/requests/api/project_import_spec.rb | 33 +++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index d554d6d12bd..1e51c92cbf1 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -17,7 +17,6 @@ module API end resource :projects, requirements: { id: %r{[^/]+} } do - params do requires :path, type: String, desc: 'The new project path and name' optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' @@ -30,11 +29,10 @@ module API render_api_error!('The file is invalid', 400) unless file_is_valid? namespace = import_params[:namespace] - - namespace = if namespace && namespace =~ /^\d+$/ - Namespace.find_by(id: namespace) - elsif namespace.blank? + namespace = if namespace.blank? current_user.namespace + elsif namespace =~ /^\d+$/ + Namespace.find_by(id: namespace) else Namespace.find_by_path_or_name(namespace) end @@ -43,7 +41,7 @@ module API file: import_params[:file]['tempfile']) project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute - render_api_error!(project&.full_messages&.first, 400) unless project&.saved? + render_api_error!(project.errors.full_messages&.first, 400) unless project.saved? present project, with: Entities::ProjectImportStatus end diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index cbea0229b09..b7b22e91bbf 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -16,13 +16,44 @@ describe API::ProjectImport do end describe 'POST /projects/import' do - it 'schedules an import' do + it 'schedules an import using a namespace' do expect_any_instance_of(Project).to receive(:import_schedule) + expect(Gitlab::ImportExport::ProjectCreator).to receive(:new).with(namespace.id, any_args).and_call_original post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path expect(response).to have_gitlab_http_status(201) end + + it 'schedules an import at the user namespace level' do + expect_any_instance_of(Project).to receive(:import_schedule) + expect(Gitlab::ImportExport::ProjectCreator).to receive(:new).with(user.namespace.id, any_args).and_call_original + + post api('/projects/import', user), path: 'test-import2', file: fixture_file_upload(file) + + expect(response).to have_gitlab_http_status(201) + end + + it 'does not schedule an import if the user has no permission to the namespace' do + expect_any_instance_of(Project).not_to receive(:import_schedule) + + post(api('/projects/import', create(:user)), + path: 'test-import3', + file: fixture_file_upload(file), + namespace: namespace.full_path) + + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']).to eq('Namespace is not valid') + end + + it 'does not schedule an import if the user uploads no valid file' do + expect_any_instance_of(Project).not_to receive(:import_schedule) + + post api('/projects/import', user), path: 'test-import3', file: './random/test' + + expect(response).to have_gitlab_http_status(400) + expect(json_response['error']).to eq('file is invalid') + end end describe 'GET /projects/:id/import' do -- cgit v1.2.3 From de83f29a3506af01af31c6668640fbb2a0dd54a9 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 13 Feb 2018 11:24:14 +0100 Subject: add more specs --- spec/requests/api/project_import_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index b7b22e91bbf..47c1edc7919 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -20,6 +20,15 @@ describe API::ProjectImport do expect_any_instance_of(Project).to receive(:import_schedule) expect(Gitlab::ImportExport::ProjectCreator).to receive(:new).with(namespace.id, any_args).and_call_original + post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.id + + expect(response).to have_gitlab_http_status(201) + end + + it 'schedules an import using the namespace path' do + expect_any_instance_of(Project).to receive(:import_schedule) + expect(Gitlab::ImportExport::ProjectCreator).to receive(:new).with(namespace.id, any_args).and_call_original + post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path expect(response).to have_gitlab_http_status(201) -- cgit v1.2.3 From 17e5ef4b269f4c13d22bd5c10086c6226dc43543 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 13 Feb 2018 15:04:19 +0100 Subject: add docs and changelog --- ...api-endpoint-for-importing-a-project-export.yml | 5 ++ doc/api/project_import_export.md | 72 ++++++++++++++++++++++ lib/api/project_import.rb | 2 +- 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml create mode 100644 doc/api/project_import_export.md diff --git a/changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml b/changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml new file mode 100644 index 00000000000..29ab7cc7cab --- /dev/null +++ b/changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml @@ -0,0 +1,5 @@ +--- +title: API endpoint for importing a project export +merge_request: 17025 +author: +type: added diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md new file mode 100644 index 00000000000..c594db7d7f2 --- /dev/null +++ b/doc/api/project_import_export.md @@ -0,0 +1,72 @@ +# Project import API + +[Introduced][ce-41899] in GitLab 10.6 + +[Project import/export](../user/project/settings/import_export.md) + +## Import a file + +```http +POST /projects/import +``` + +| Attribute | Type | Required | Description | +| --------- | -------------- | -------- | ---------------------------------------- | +| `namespace` | integer/string | no | The ID or path of the namespace that the project will be imported to. Defaults to the user's namespace | +| `file` | string | yes | The file to be uploaded | +| `path` | string | yes | Name and path for new project | + +To upload a file from your filesystem, use the `--form` argument. This causes +cURL to post data using the header `Content-Type: multipart/form-data`. +The `file=` parameter must point to a file on your filesystem and be preceded +by `@`. For example: + +```console +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -F "path=api-project" -F "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import +``` + +```json +{ + "id": 1, + "description": null, + "name": "api-project", + "name_with_namespace": "Administrator / api-project", + "path": "api-project", + "path_with_namespace": "root/api-project", + "created_at": "2018-02-13T09:05:58.023Z", + "import_status": "scheduled" +} +``` + +## Import status + +Get the status of export. + +```http +GET /projects/:id/import +``` + +| Attribute | Type | Required | Description | +| --------- | -------------- | -------- | ---------------------------------------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | + +```console +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/import +``` + +Status can be one of `none`, `started`, or `finished`. + +```json +{ + "id": 1, + "description": "Itaque perspiciatis minima aspernatur corporis consequatur.", + "name": "Gitlab Test", + "name_with_namespace": "Gitlab Org / Gitlab Test", + "path": "gitlab-test", + "path_with_namespace": "gitlab-org/gitlab-test", + "created_at": "2017-08-29T04:36:44.383Z", + "import_status": "started" +} +``` + +[ce-41899]: https://gitlab.com/gitlab-org/gitlab-ce/issues/41899 diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index 1e51c92cbf1..e93c14ddcf5 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -19,8 +19,8 @@ module API resource :projects, requirements: { id: %r{[^/]+} } do params do requires :path, type: String, desc: 'The new project path and name' - optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' requires :file, type: File, desc: 'The project export file to be imported' + optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' end desc 'Get export status' do success Entities::ProjectImportStatus -- cgit v1.2.3 From 39122ea40dda9a1c5b353fc671219b51c625f6cb Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 13 Feb 2018 15:12:18 +0100 Subject: update docs --- doc/api/project_import_export.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md index c594db7d7f2..17b9b530be7 100644 --- a/doc/api/project_import_export.md +++ b/doc/api/project_import_export.md @@ -40,7 +40,7 @@ curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -F "path=api- ## Import status -Get the status of export. +Get the status of an import. ```http GET /projects/:id/import @@ -54,7 +54,9 @@ GET /projects/:id/import curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/import ``` -Status can be one of `none`, `started`, or `finished`. +Status can be one of `none`, `scheduled`, `failed`, `started`, or `finished`. + +If the status is `failed`, it will include the import error message. ```json { -- cgit v1.2.3 From e8813520029f0f07b0cf2d8463337ae09f15b7fe Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 13 Feb 2018 15:18:52 +0100 Subject: refactor api class --- lib/api/project_import.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index e93c14ddcf5..b0511342b63 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -13,7 +13,7 @@ module API end before do - not_found! unless Gitlab::CurrentSettings.import_sources.include?('gitlab_project') + forbidden! unless Gitlab::CurrentSettings.import_sources.include?('gitlab_project') end resource :projects, requirements: { id: %r{[^/]+} } do @@ -22,12 +22,14 @@ module API requires :file, type: File, desc: 'The project export file to be imported' optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' end - desc 'Get export status' do + desc 'Create a new project import' do success Entities::ProjectImportStatus end post 'import' do render_api_error!('The file is invalid', 400) unless file_is_valid? + Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42437') + namespace = import_params[:namespace] namespace = if namespace.blank? current_user.namespace @@ -49,7 +51,7 @@ module API params do requires :id, type: String, desc: 'The ID of a project' end - desc 'Get export status' do + desc 'Get a project export status' do success Entities::ProjectImportStatus end get ':id/import' do -- cgit v1.2.3 From 083194908437f129adcb6e36be40af72ad6d308c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 13 Feb 2018 15:35:08 +0100 Subject: update missing doc links --- doc/api/projects.md | 4 ++++ doc/user/project/settings/import_export.md | 1 + 2 files changed, 5 insertions(+) diff --git a/doc/api/projects.md b/doc/api/projects.md index 46f5de5aa0e..08df65b2f66 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1326,6 +1326,10 @@ POST /projects/:id/housekeeping Read more in the [Branches](branches.md) documentation. +## Project Import/Export + +Read more in the [Project export](project_import_export.md) documentation. + ## Project members Read more in the [Project members](members.md) documentation. diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index b8f865679a2..dedf102fc37 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -22,6 +22,7 @@ > in the import side is required to map the users, based on email or username. > Otherwise, a supplementary comment is left to mention the original author and > the MRs, notes or issues will be owned by the importer. +> - Control project Import/Export with the [API](../../../api/project_import_export.md). Existing projects running on any GitLab instance or GitLab.com can be exported with all their related data and be moved into a new GitLab instance. -- cgit v1.2.3 From e613d777b258a4f7070d2b7aaee093901e4b7ed7 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 14 Feb 2018 14:46:40 +0100 Subject: refactor code based on feedback --- lib/api/project_import.rb | 17 ++++++++++------- spec/requests/api/project_import_spec.rb | 28 ++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index b0511342b63..88fe1d2b5f5 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -10,19 +10,24 @@ module API def file_is_valid? import_params[:file] && import_params[:file]['tempfile'].respond_to?(:read) end + + def validate_file! + render_api_error!('The file is invalid', 400) unless file_is_valid? + end end before do forbidden! unless Gitlab::CurrentSettings.import_sources.include?('gitlab_project') end - resource :projects, requirements: { id: %r{[^/]+} } do + resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do params do requires :path, type: String, desc: 'The new project path and name' requires :file, type: File, desc: 'The project export file to be imported' optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' end desc 'Create a new project import' do + detail 'This feature was introduced in GitLab 10.6.' success Entities::ProjectImportStatus end post 'import' do @@ -30,13 +35,10 @@ module API Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42437') - namespace = import_params[:namespace] - namespace = if namespace.blank? - current_user.namespace - elsif namespace =~ /^\d+$/ - Namespace.find_by(id: namespace) + namespace = if import_params[:namespace] + find_namespace!(import_params[:namespace]) else - Namespace.find_by_path_or_name(namespace) + current_user.namespace end project_params = import_params.merge(namespace_id: namespace.id, @@ -52,6 +54,7 @@ module API requires :id, type: String, desc: 'The ID of a project' end desc 'Get a project export status' do + detail 'This feature was introduced in GitLab 10.6.' success Entities::ProjectImportStatus end get ':id/import' do diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 47c1edc7919..2fb103c6510 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -17,8 +17,7 @@ describe API::ProjectImport do describe 'POST /projects/import' do it 'schedules an import using a namespace' do - expect_any_instance_of(Project).to receive(:import_schedule) - expect(Gitlab::ImportExport::ProjectCreator).to receive(:new).with(namespace.id, any_args).and_call_original + stub_import(user.namespace) post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.id @@ -26,8 +25,7 @@ describe API::ProjectImport do end it 'schedules an import using the namespace path' do - expect_any_instance_of(Project).to receive(:import_schedule) - expect(Gitlab::ImportExport::ProjectCreator).to receive(:new).with(namespace.id, any_args).and_call_original + stub_import(unamespace) post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path @@ -35,14 +33,23 @@ describe API::ProjectImport do end it 'schedules an import at the user namespace level' do - expect_any_instance_of(Project).to receive(:import_schedule) - expect(Gitlab::ImportExport::ProjectCreator).to receive(:new).with(user.namespace.id, any_args).and_call_original + stub_import(user.namespace) post api('/projects/import', user), path: 'test-import2', file: fixture_file_upload(file) expect(response).to have_gitlab_http_status(201) end + it 'schedules an import at the user namespace level' do + expect_any_instance_of(Project).not_to receive(:import_schedule) + expect(Gitlab::ImportExport::ProjectCreator).not_to receive(:new) + + post api('/projects/import', user), namespace: 'nonexistent', path: 'test-import2', file: fixture_file_upload(file) + + expect(response).to have_gitlab_http_status(404) + expect(json_response['message']).to eq('404 Namespace Not Found') + end + it 'does not schedule an import if the user has no permission to the namespace' do expect_any_instance_of(Project).not_to receive(:import_schedule) @@ -51,8 +58,8 @@ describe API::ProjectImport do file: fixture_file_upload(file), namespace: namespace.full_path) - expect(response).to have_gitlab_http_status(400) - expect(json_response['message']).to eq('Namespace is not valid') + expect(response).to have_gitlab_http_status(404) + expect(json_response['message']).to eq('404 Namespace Not Found') end it 'does not schedule an import if the user uploads no valid file' do @@ -63,6 +70,11 @@ describe API::ProjectImport do expect(response).to have_gitlab_http_status(400) expect(json_response['error']).to eq('file is invalid') end + + def stub_import(namespace) + expect_any_instance_of(Project).to receive(:import_schedule) + expect(Gitlab::ImportExport::ProjectCreator).to receive(:new).with(namespace.id, any_args).and_call_original + end end describe 'GET /projects/:id/import' do -- cgit v1.2.3 From 2dc2bad6233e1e74c74c49af4be838e0a39dcd53 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 14 Feb 2018 14:55:11 +0100 Subject: refactor code based on feedback --- doc/api/project_import_export.md | 2 +- spec/requests/api/project_import_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md index 17b9b530be7..c473b94aafe 100644 --- a/doc/api/project_import_export.md +++ b/doc/api/project_import_export.md @@ -22,7 +22,7 @@ The `file=` parameter must point to a file on your filesystem and be preceded by `@`. For example: ```console -curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -F "path=api-project" -F "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "path=api-project" --form "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import ``` ```json diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 2fb103c6510..eabf9095c13 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -17,7 +17,7 @@ describe API::ProjectImport do describe 'POST /projects/import' do it 'schedules an import using a namespace' do - stub_import(user.namespace) + stub_import(namespace) post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.id @@ -25,7 +25,7 @@ describe API::ProjectImport do end it 'schedules an import using the namespace path' do - stub_import(unamespace) + stub_import(namespace) post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path -- cgit v1.2.3 From 0abd85f919053efa8a03add9ae43ce9ea2d02ae5 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 14 Feb 2018 14:59:11 +0100 Subject: refactor code based on feedback --- lib/api/project_import.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index 88fe1d2b5f5..a6da9b90fe3 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -31,7 +31,7 @@ module API success Entities::ProjectImportStatus end post 'import' do - render_api_error!('The file is invalid', 400) unless file_is_valid? + validate_file! Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42437') -- cgit v1.2.3 From e4990b66df64f2e23502d161f411335c9a771a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 26 Jan 2018 15:23:46 +0100 Subject: Combine all rake tasks in the static-analysis job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/tasks/lint.rake | 28 ++++++++++++++++++++++++++++ scripts/static-analysis | 7 +------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/tasks/lint.rake b/lib/tasks/lint.rake index 3ab406eff2c..e7812ff3568 100644 --- a/lib/tasks/lint.rake +++ b/lib/tasks/lint.rake @@ -16,5 +16,33 @@ unless Rails.env.production? task :javascript do Rake::Task['eslint'].invoke end + + desc "GitLab | lint | Run several lint checks" + task :all do + status = 0 + original_stdout = $stdout + + %w[ + config_lint + haml_lint + scss_lint + flay + gettext:lint + lint:static_verification + ].each do |task| + begin + $stdout = StringIO.new + Rake::Task[task].invoke + rescue RuntimeError, SystemExit => ex + raise ex if ex.is_a?(RuntimeError) && task != 'haml_lint' + original_stdout << $stdout.string + status = 1 + ensure + $stdout = original_stdout + end + end + + exit status + end end end diff --git a/scripts/static-analysis b/scripts/static-analysis index bdb88f3cb57..db4df4ee6cb 100755 --- a/scripts/static-analysis +++ b/scripts/static-analysis @@ -26,15 +26,10 @@ def emit_errors(static_analysis) end tasks = [ - %w[bundle exec rake config_lint], - %w[bundle exec rake flay], - %w[bundle exec rake haml_lint], - %w[bundle exec rake scss_lint], + %w[bin/rake lint:all], %w[bundle exec license_finder], %w[yarn run eslint], %w[bundle exec rubocop --parallel], - %w[bundle exec rake gettext:lint], - %w[bundle exec rake lint:static_verification], %w[scripts/lint-conflicts.sh], %w[scripts/lint-rugged] ] -- cgit v1.2.3 From 2f0d2ab55b6deac79f81834f6724a676ceae94ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 12 Feb 2018 18:34:07 +0100 Subject: Run lint:all tasks in forks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/tasks/lint.rake | 43 ++++++++++++++++++++++++++++++++----------- scripts/static-analysis | 4 ++-- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/lib/tasks/lint.rake b/lib/tasks/lint.rake index e7812ff3568..fe5032cae18 100644 --- a/lib/tasks/lint.rake +++ b/lib/tasks/lint.rake @@ -20,7 +20,6 @@ unless Rails.env.production? desc "GitLab | lint | Run several lint checks" task :all do status = 0 - original_stdout = $stdout %w[ config_lint @@ -30,19 +29,41 @@ unless Rails.env.production? gettext:lint lint:static_verification ].each do |task| - begin - $stdout = StringIO.new - Rake::Task[task].invoke - rescue RuntimeError, SystemExit => ex - raise ex if ex.is_a?(RuntimeError) && task != 'haml_lint' - original_stdout << $stdout.string - status = 1 - ensure - $stdout = original_stdout + pid = Process.fork do + rd, wr = IO.pipe + stdout = $stdout.dup + stderr = $stderr.dup + $stdout.reopen(wr) + $stderr.reopen(wr) + + begin + begin + Rake::Task[task].invoke + rescue RuntimeError # The haml_lint tasks raise a RuntimeError + exit(1) + end + rescue SystemExit => ex + msg = "*** Rake task #{task} failed with the following error(s):" + raise ex + ensure + $stdout.reopen(stdout) + $stderr.reopen(stderr) + wr.close + + if msg + warn "\n#{msg}\n\n" + IO.copy_stream(rd, $stderr) + else + IO.copy_stream(rd, $stdout) + end + end end + + Process.waitpid(pid) + status += $?.exitstatus end - exit status + exit(status) end end end diff --git a/scripts/static-analysis b/scripts/static-analysis index db4df4ee6cb..0e67eabfec1 100755 --- a/scripts/static-analysis +++ b/scripts/static-analysis @@ -7,7 +7,7 @@ require_relative '../lib/gitlab/popen/runner' def emit_warnings(static_analysis) static_analysis.warned_results.each do |result| puts - puts "**** #{result.cmd.join(' ')} had the following warnings:" + puts "**** #{result.cmd.join(' ')} had the following warning(s):" puts puts result.stderr puts @@ -17,7 +17,7 @@ end def emit_errors(static_analysis) static_analysis.failed_results.each do |result| puts - puts "**** #{result.cmd.join(' ')} failed with the following error:" + puts "**** #{result.cmd.join(' ')} failed with the following error(s):" puts puts result.stdout puts result.stderr -- cgit v1.2.3 From 05f66d1342db228c1a659b4c58e15c890b522261 Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Wed, 14 Feb 2018 10:16:04 +0100 Subject: Add new modal Vue component --- .../jobs/index/components/stop_jobs_modal.vue | 26 +-- .../javascripts/pages/admin/jobs/index/index.js | 37 ++-- .../javascripts/vue_shared/components/gl_modal.vue | 106 ++++++++++++ app/views/admin/jobs/index.html.haml | 7 +- changelogs/unreleased/winh-new-modal-component.yml | 5 + doc/development/fe_guide/components.md | 61 +++++++ doc/development/fe_guide/dropdowns.md | 33 +--- doc/development/fe_guide/img/gl-modal.png | Bin 0 -> 25893 bytes doc/development/fe_guide/index.md | 6 +- .../vue_shared/components/gl_modal_spec.js | 192 +++++++++++++++++++++ 10 files changed, 405 insertions(+), 68 deletions(-) create mode 100644 app/assets/javascripts/vue_shared/components/gl_modal.vue create mode 100644 changelogs/unreleased/winh-new-modal-component.yml create mode 100644 doc/development/fe_guide/components.md create mode 100644 doc/development/fe_guide/img/gl-modal.png create mode 100644 spec/javascripts/vue_shared/components/gl_modal_spec.js diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue index 555725cbe12..ba1d8e4d8db 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue +++ b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue @@ -1,13 +1,13 @@ diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js index 0e004bd9174..31d58eabaaf 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/index.js +++ b/app/assets/javascripts/pages/admin/jobs/index/index.js @@ -8,22 +8,23 @@ Vue.use(Translate); export default () => { const stopJobsButton = document.getElementById('stop-jobs-button'); - - // eslint-disable-next-line no-new - new Vue({ - el: '#stop-jobs-modal', - components: { - stopJobsModal, - }, - mounted() { - stopJobsButton.classList.remove('disabled'); - }, - render(createElement) { - return createElement('stop-jobs-modal', { - props: { - url: stopJobsButton.dataset.url, - }, - }); - }, - }); + if (stopJobsButton) { + // eslint-disable-next-line no-new + new Vue({ + el: '#stop-jobs-modal', + components: { + stopJobsModal, + }, + mounted() { + stopJobsButton.classList.remove('disabled'); + }, + render(createElement) { + return createElement('stop-jobs-modal', { + props: { + url: stopJobsButton.dataset.url, + }, + }); + }, + }); + } }; diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue new file mode 100644 index 00000000000..67c9181c7b1 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue @@ -0,0 +1,106 @@ + + + diff --git a/app/views/admin/jobs/index.html.haml b/app/views/admin/jobs/index.html.haml index a01676d82a8..4e3e2f7a475 100644 --- a/app/views/admin/jobs/index.html.haml +++ b/app/views/admin/jobs/index.html.haml @@ -7,10 +7,9 @@ - build_path_proc = ->(scope) { admin_jobs_path(scope: scope) } = render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope - .nav-controls - - if @all_builds.running_or_pending.any? - #stop-jobs-modal - + - if @all_builds.running_or_pending.any? + #stop-jobs-modal + .nav-controls %button#stop-jobs-button.btn.btn-danger{ data: { toggle: 'modal', target: '#stop-jobs-modal', url: cancel_all_admin_jobs_path } } diff --git a/changelogs/unreleased/winh-new-modal-component.yml b/changelogs/unreleased/winh-new-modal-component.yml new file mode 100644 index 00000000000..bcc0d489c88 --- /dev/null +++ b/changelogs/unreleased/winh-new-modal-component.yml @@ -0,0 +1,5 @@ +--- +title: Add new modal Vue component +merge_request: 17108 +author: +type: changed diff --git a/doc/development/fe_guide/components.md b/doc/development/fe_guide/components.md new file mode 100644 index 00000000000..66a8abe42f7 --- /dev/null +++ b/doc/development/fe_guide/components.md @@ -0,0 +1,61 @@ +# Components + +## Contents +* [Dropdowns](#dropdowns) +* [Modals](#modals) + +## Dropdowns + +See also the [corresponding UX guide](../ux_guide/components.md#dropdowns). + +### How to style a bootstrap dropdown +1. Use the HTML structure provided by the [docs][bootstrap-dropdowns] +1. Add a specific class to the top level `.dropdown` element + + + ```Haml + .dropdown.my-dropdown + %button{ type: 'button', data: { toggle: 'dropdown' }, 'aria-haspopup': true, 'aria-expanded': false } + %span.dropdown-toggle-text + Toggle Dropdown + = icon('chevron-down') + + %ul.dropdown-menu + %li + %a + item! + ``` + + Or use the helpers + ```Haml + .dropdown.my-dropdown + = dropdown_toggle('Toogle!', { toggle: 'dropdown' }) + = dropdown_content + %li + %a + item! + ``` + +[bootstrap-dropdowns]: https://getbootstrap.com/docs/3.3/javascript/#dropdowns + +## Modals + +See also the [corresponding UX guide](../ux_guide/components.md#modals). + +We have a reusable Vue component for modals: [vue_shared/components/gl-modal.vue](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/vue_shared/components/gl-modal.vue) + +Here is an example of how to use it: + +```html + + {{ s__('ModalExample|You’re about to let the dogs out.') }} + +``` + +![example modal](img/gl-modal.png) diff --git a/doc/development/fe_guide/dropdowns.md b/doc/development/fe_guide/dropdowns.md index 6314f8f38d2..e9d6244355c 100644 --- a/doc/development/fe_guide/dropdowns.md +++ b/doc/development/fe_guide/dropdowns.md @@ -1,32 +1 @@ -# Dropdowns - - -## How to style a bootstrap dropdown -1. Use the HTML structure provided by the [docs][bootstrap-dropdowns] -1. Add a specific class to the top level `.dropdown` element - - - ```Haml - .dropdown.my-dropdown - %button{ type: 'button', data: { toggle: 'dropdown' }, 'aria-haspopup': true, 'aria-expanded': false } - %span.dropdown-toggle-text - Toggle Dropdown - = icon('chevron-down') - - %ul.dropdown-menu - %li - %a - item! - ``` - - Or use the helpers - ```Haml - .dropdown.my-dropdown - = dropdown_toggle('Toogle!', { toggle: 'dropdown' }) - = dropdown_content - %li - %a - item! - ``` - -[bootstrap-dropdowns]: https://getbootstrap.com/docs/3.3/javascript/#dropdowns +This page has moved [here](components.md#dropdowns). diff --git a/doc/development/fe_guide/img/gl-modal.png b/doc/development/fe_guide/img/gl-modal.png new file mode 100644 index 00000000000..47302e857bc Binary files /dev/null and b/doc/development/fe_guide/img/gl-modal.png differ diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md index 72cb557d054..5c2e50e8c8e 100644 --- a/doc/development/fe_guide/index.md +++ b/doc/development/fe_guide/index.md @@ -77,8 +77,10 @@ Axios specific practices and gotchas. ## [Icons](icons.md) How we use SVG for our Icons. -## [Dropdowns](dropdowns.md) -How we use dropdowns. +## [Components](components.md) + +How we use UI components. + --- ## Style Guides diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js new file mode 100644 index 00000000000..d6148cb785b --- /dev/null +++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js @@ -0,0 +1,192 @@ +import Vue from 'vue'; +import GlModal from '~/vue_shared/components/gl_modal.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; + +const modalComponent = Vue.extend(GlModal); + +describe('GlModal', () => { + let vm; + + afterEach(() => { + vm.$destroy(); + }); + + describe('props', () => { + describe('with id', () => { + const props = { + id: 'my-modal', + }; + + beforeEach(() => { + vm = mountComponent(modalComponent, props); + }); + + it('assigns the id to the modal', () => { + expect(vm.$el.id).toBe(props.id); + }); + }); + + describe('without id', () => { + beforeEach(() => { + vm = mountComponent(modalComponent, { }); + }); + + it('does not add an id attribute to the modal', () => { + expect(vm.$el.hasAttribute('id')).toBe(false); + }); + }); + + describe('with headerTitleText', () => { + const props = { + headerTitleText: 'my title text', + }; + + beforeEach(() => { + vm = mountComponent(modalComponent, props); + }); + + it('sets the modal title', () => { + const modalTitle = vm.$el.querySelector('.modal-title'); + expect(modalTitle.innerHTML.trim()).toBe(props.headerTitleText); + }); + }); + + describe('with footerPrimaryButtonVariant', () => { + const props = { + footerPrimaryButtonVariant: 'danger', + }; + + beforeEach(() => { + vm = mountComponent(modalComponent, props); + }); + + it('sets the primary button class', () => { + const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type'); + expect(primaryButton).toHaveClass(`btn-${props.footerPrimaryButtonVariant}`); + }); + }); + + describe('with footerPrimaryButtonText', () => { + const props = { + footerPrimaryButtonText: 'my button text', + }; + + beforeEach(() => { + vm = mountComponent(modalComponent, props); + }); + + it('sets the primary button text', () => { + const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type'); + expect(primaryButton.innerHTML.trim()).toBe(props.footerPrimaryButtonText); + }); + }); + }); + + it('works with data-toggle="modal"', (done) => { + setFixtures(` + + + `); + + const modalContainer = document.getElementById('modal-container'); + const modalButton = document.getElementById('modal-button'); + vm = mountComponent(modalComponent, { + id: 'my-modal', + }, modalContainer); + $(vm.$el).on('shown.bs.modal', () => done()); + + modalButton.click(); + }); + + describe('methods', () => { + const dummyEvent = 'not really an event'; + + beforeEach(() => { + vm = mountComponent(modalComponent, { }); + spyOn(vm, '$emit'); + }); + + describe('emitCancel', () => { + it('emits a cancel event', () => { + vm.emitCancel(dummyEvent); + + expect(vm.$emit).toHaveBeenCalledWith('cancel', dummyEvent); + }); + }); + + describe('emitSubmit', () => { + it('emits a submit event', () => { + vm.emitSubmit(dummyEvent); + + expect(vm.$emit).toHaveBeenCalledWith('submit', dummyEvent); + }); + }); + }); + + describe('slots', () => { + const slotContent = 'this should go into the slot'; + const modalWithSlot = (slotName) => { + let template; + if (slotName) { + template = ` + + + + `; + } else { + template = `${slotContent}`; + } + + return Vue.extend({ + components: { + GlModal, + }, + template, + }); + }; + + describe('default slot', () => { + beforeEach(() => { + vm = mountComponent(modalWithSlot()); + }); + + it('sets the modal body', () => { + const modalBody = vm.$el.querySelector('.modal-body'); + expect(modalBody.innerHTML).toBe(slotContent); + }); + }); + + describe('header slot', () => { + beforeEach(() => { + vm = mountComponent(modalWithSlot('header')); + }); + + it('sets the modal header', () => { + const modalHeader = vm.$el.querySelector('.modal-header'); + expect(modalHeader.innerHTML).toBe(slotContent); + }); + }); + + describe('title slot', () => { + beforeEach(() => { + vm = mountComponent(modalWithSlot('title')); + }); + + it('sets the modal title', () => { + const modalTitle = vm.$el.querySelector('.modal-title'); + expect(modalTitle.innerHTML).toBe(slotContent); + }); + }); + + describe('footer slot', () => { + beforeEach(() => { + vm = mountComponent(modalWithSlot('footer')); + }); + + it('sets the modal footer', () => { + const modalFooter = vm.$el.querySelector('.modal-footer'); + expect(modalFooter.innerHTML).toBe(slotContent); + }); + }); + }); +}); -- cgit v1.2.3 From 9a450aedc2c81f8ce17e4c9b4238c58edbef8269 Mon Sep 17 00:00:00 2001 From: Turo Soisenniemi Date: Wed, 14 Feb 2018 19:41:13 +0000 Subject: Set Asciidoctor outfilesuffix to default .adoc Without setting outfilesuffix Asciidoctor defaults to .html. When .html is used, inter-document cross references do not work in Gitlab's repository / file renderer. See more information from http://asciidoctor.org/docs/user-manual/#navigating-between-source-files and http://asciidoctor.org/docs/user-manual/#inter-document-cross-references --- .../unreleased/asciidoc_inter_document_cross_references.yml | 5 +++++ lib/gitlab/asciidoc.rb | 3 ++- spec/lib/gitlab/asciidoc_spec.rb | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/asciidoc_inter_document_cross_references.yml diff --git a/changelogs/unreleased/asciidoc_inter_document_cross_references.yml b/changelogs/unreleased/asciidoc_inter_document_cross_references.yml new file mode 100644 index 00000000000..34b26753312 --- /dev/null +++ b/changelogs/unreleased/asciidoc_inter_document_cross_references.yml @@ -0,0 +1,5 @@ +--- +title: Asciidoc now support inter-document cross references between files in repository +merge_request: 17125 +author: Turo Soisenniemi +type: changed diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb index ee7f4be6b9f..62c41801d75 100644 --- a/lib/gitlab/asciidoc.rb +++ b/lib/gitlab/asciidoc.rb @@ -8,7 +8,8 @@ module Gitlab module Asciidoc DEFAULT_ADOC_ATTRS = [ 'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab', - 'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font' + 'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font', + 'outfilesuffix=.adoc' ].freeze # Public: Converts the provided Asciidoc markup into HTML. diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index f668f78c2b8..2a0e19ae796 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -95,6 +95,14 @@ module Gitlab expect(render(input, context)).to include('

2+2 is 4

') end end + + context 'outfilesuffix' do + it 'defaults to adoc' do + output = render("Inter-document reference <>", context) + + expect(output).to include("a href=\"README.adoc\"") + end + end end def render(*args) -- cgit v1.2.3 From c6b2ff8a21470cb59e3512b2f27f1c926e17ae6f Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Fri, 9 Feb 2018 13:09:33 -0600 Subject: Hide CI secret variable values on save See https://gitlab.com/gitlab-org/gitlab-ce/issues/42928 --- .../ci_variable_list/ajax_variable_list.js | 1 + .../ci_variable_list/ci_variable_list.js | 4 ++ .../unreleased/42929-hide-new-variable-values.yml | 5 +++ .../ci_variable_list/ajax_variable_list_spec.js | 46 ++++++++++++++++------ .../ci_variable_list/ci_variable_list_spec.js | 41 +++++++++++++++++-- 5 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 changelogs/unreleased/42929-hide-new-variable-values.yml diff --git a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js index 76f93e5c6bd..b33adff609f 100644 --- a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js +++ b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js @@ -75,6 +75,7 @@ export default class AjaxVariableList { if (res.status === statusCodes.OK && res.data) { this.updateRowsWithPersistedVariables(res.data.variables); + this.variableList.hideValues(); } else if (res.status === statusCodes.BAD_REQUEST) { // Validation failed this.errorBox.innerHTML = generateErrorBoxContent(res.data); diff --git a/app/assets/javascripts/ci_variable_list/ci_variable_list.js b/app/assets/javascripts/ci_variable_list/ci_variable_list.js index d91789c2192..3f2ff4002c2 100644 --- a/app/assets/javascripts/ci_variable_list/ci_variable_list.js +++ b/app/assets/javascripts/ci_variable_list/ci_variable_list.js @@ -178,6 +178,10 @@ export default class VariableList { this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled); } + hideValues() { + this.secretValues.updateDom(false); + } + getAllData() { // Ignore the last empty row because we don't want to try persist // a blank variable and run into validation problems. diff --git a/changelogs/unreleased/42929-hide-new-variable-values.yml b/changelogs/unreleased/42929-hide-new-variable-values.yml new file mode 100644 index 00000000000..68decd25b5a --- /dev/null +++ b/changelogs/unreleased/42929-hide-new-variable-values.yml @@ -0,0 +1,5 @@ +--- +title: Hide CI secret variable values after saving +merge_request: 17044 +author: +type: changed diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js index 5b9cdceee71..ee457a9c48c 100644 --- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js +++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js @@ -1,8 +1,10 @@ +import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import AjaxFormVariableList from '~/ci_variable_list/ajax_variable_list'; const VARIABLE_PATCH_ENDPOINT = 'http://test.host/frontend-fixtures/builds-project/variables'; +const HIDE_CLASS = 'hide'; describe('AjaxFormVariableList', () => { preloadFixtures('projects/ci_cd_settings.html.raw'); @@ -45,16 +47,16 @@ describe('AjaxFormVariableList', () => { const loadingIcon = saveButton.querySelector('.js-secret-variables-save-loading-icon'); mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => { - expect(loadingIcon.classList.contains('hide')).toEqual(false); + expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(false); return [200, {}]; }); - expect(loadingIcon.classList.contains('hide')).toEqual(true); + expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true); ajaxVariableList.onSaveClicked() .then(() => { - expect(loadingIcon.classList.contains('hide')).toEqual(true); + expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true); }) .then(done) .catch(done.fail); @@ -78,11 +80,11 @@ describe('AjaxFormVariableList', () => { it('hides any previous error box', (done) => { mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200); - expect(errorBox.classList.contains('hide')).toEqual(true); + expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); ajaxVariableList.onSaveClicked() .then(() => { - expect(errorBox.classList.contains('hide')).toEqual(true); + expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); }) .then(done) .catch(done.fail); @@ -103,17 +105,39 @@ describe('AjaxFormVariableList', () => { .catch(done.fail); }); + it('hides secret values', (done) => { + mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, {}); + + const row = container.querySelector('.js-row:first-child'); + const valueInput = row.querySelector('.js-ci-variable-input-value'); + const valuePlaceholder = row.querySelector('.js-secret-value-placeholder'); + + valueInput.value = 'bar'; + $(valueInput).trigger('input'); + + expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(true); + expect(valueInput.classList.contains(HIDE_CLASS)).toBe(false); + + ajaxVariableList.onSaveClicked() + .then(() => { + expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(false); + expect(valueInput.classList.contains(HIDE_CLASS)).toBe(true); + }) + .then(done) + .catch(done.fail); + }); + it('shows error box with validation errors', (done) => { const validationError = 'some validation error'; mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(400, [ validationError, ]); - expect(errorBox.classList.contains('hide')).toEqual(true); + expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); ajaxVariableList.onSaveClicked() .then(() => { - expect(errorBox.classList.contains('hide')).toEqual(false); + expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(false); expect(errorBox.textContent.trim().replace(/\n+\s+/m, ' ')).toEqual(`Validation failed ${validationError}`); }) .then(done) @@ -123,11 +147,11 @@ describe('AjaxFormVariableList', () => { it('shows flash message when request fails', (done) => { mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(500); - expect(errorBox.classList.contains('hide')).toEqual(true); + expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); ajaxVariableList.onSaveClicked() .then(() => { - expect(errorBox.classList.contains('hide')).toEqual(true); + expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); }) .then(done) .catch(done.fail); @@ -170,9 +194,9 @@ describe('AjaxFormVariableList', () => { const valueInput = row.querySelector('.js-ci-variable-input-value'); keyInput.value = 'foo'; - keyInput.dispatchEvent(new Event('input')); + $(keyInput).trigger('input'); valueInput.value = 'bar'; - valueInput.dispatchEvent(new Event('input')); + $(valueInput).trigger('input'); expect(idInput.value).toEqual(''); diff --git a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js index 6ab7b50e035..5d41f7ca44f 100644 --- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js +++ b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js @@ -1,6 +1,8 @@ import VariableList from '~/ci_variable_list/ci_variable_list'; import getSetTimeoutPromise from '../helpers/set_timeout_promise_helper'; +const HIDE_CLASS = 'hide'; + describe('VariableList', () => { preloadFixtures('pipeline_schedules/edit.html.raw'); preloadFixtures('pipeline_schedules/edit_with_variables.html.raw'); @@ -92,14 +94,14 @@ describe('VariableList', () => { const $inputValue = $row.find('.js-ci-variable-input-value'); const $placeholder = $row.find('.js-secret-value-placeholder'); - expect($placeholder.hasClass('hide')).toBe(false); - expect($inputValue.hasClass('hide')).toBe(true); + expect($placeholder.hasClass(HIDE_CLASS)).toBe(false); + expect($inputValue.hasClass(HIDE_CLASS)).toBe(true); // Reveal values $wrapper.find('.js-secret-value-reveal-button').click(); - expect($placeholder.hasClass('hide')).toBe(true); - expect($inputValue.hasClass('hide')).toBe(false); + expect($placeholder.hasClass(HIDE_CLASS)).toBe(true); + expect($inputValue.hasClass(HIDE_CLASS)).toBe(false); }); }); }); @@ -179,4 +181,35 @@ describe('VariableList', () => { expect($wrapper.find('.js-ci-variable-input-key:not([disabled])').length).toBe(3); }); }); + + describe('hideValues', () => { + beforeEach(() => { + loadFixtures('projects/ci_cd_settings.html.raw'); + $wrapper = $('.js-ci-variable-list-section'); + + variableList = new VariableList({ + container: $wrapper, + formField: 'variables', + }); + variableList.init(); + }); + + it('should hide value input and show placeholder stars', () => { + const $row = $wrapper.find('.js-row'); + const $inputValue = $row.find('.js-ci-variable-input-value'); + const $placeholder = $row.find('.js-secret-value-placeholder'); + + $row.find('.js-ci-variable-input-value') + .val('foo') + .trigger('input'); + + expect($placeholder.hasClass(HIDE_CLASS)).toBe(true); + expect($inputValue.hasClass(HIDE_CLASS)).toBe(false); + + variableList.hideValues(); + + expect($placeholder.hasClass(HIDE_CLASS)).toBe(false); + expect($inputValue.hasClass(HIDE_CLASS)).toBe(true); + }); + }); }); -- cgit v1.2.3 From 4567fcfe22e6142772d62423d8333550f2fbc462 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Thu, 15 Feb 2018 11:23:54 +0100 Subject: Re-organize issue move method calls Move method calls related to execution of moving an issue into separate private mothods. The reason is that then these methods can be easily extended with EE specific code (https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4550#note_59158691). Related gitlab-ce#41949 --- app/services/issues/move_service.rb | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index 2f511ab44b7..299b9c6215f 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -19,19 +19,10 @@ module Issues # on rewriting notes (unfolding references) # ActiveRecord::Base.transaction do - # New issue tasks - # @new_issue = create_new_issue - rewrite_notes - rewrite_issue_award_emoji - add_note_moved_from - - # Old issue tasks - # - add_note_moved_to - close_issue - mark_as_moved + update_new_issue + update_old_issue end notify_participants @@ -41,6 +32,18 @@ module Issues private + def update_new_issue + rewrite_notes + rewrite_issue_award_emoji + add_note_moved_from + end + + def update_old_issue + add_note_moved_to + close_issue + mark_as_moved + end + def create_new_issue new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids, milestone_id: cloneable_milestone_id, -- cgit v1.2.3 From bed60b8c47acd11569da7cf5dc5bdb545ac97784 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 15 Feb 2018 12:06:57 +0100 Subject: Escape HTML entities in commit messages --- changelogs/unreleased/dm-escape-commit-message.yml | 5 +++++ lib/banzai/filter/html_entity_filter.rb | 2 +- spec/helpers/events_helper_spec.rb | 4 ++++ spec/lib/banzai/filter/html_entity_filter_spec.rb | 9 ++------- 4 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/dm-escape-commit-message.yml diff --git a/changelogs/unreleased/dm-escape-commit-message.yml b/changelogs/unreleased/dm-escape-commit-message.yml new file mode 100644 index 00000000000..89af2da3484 --- /dev/null +++ b/changelogs/unreleased/dm-escape-commit-message.yml @@ -0,0 +1,5 @@ +--- +title: Escape HTML entities in commit messages +merge_request: +author: +type: fixed diff --git a/lib/banzai/filter/html_entity_filter.rb b/lib/banzai/filter/html_entity_filter.rb index f3bd587c28b..e008fd428b0 100644 --- a/lib/banzai/filter/html_entity_filter.rb +++ b/lib/banzai/filter/html_entity_filter.rb @@ -5,7 +5,7 @@ module Banzai # Text filter that escapes these HTML entities: & " < > class HtmlEntityFilter < HTML::Pipeline::TextFilter def call - ERB::Util.html_escape_once(text) + ERB::Util.html_escape(text) end end end diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb index 8a80b88da5d..fccde8b7eba 100644 --- a/spec/helpers/events_helper_spec.rb +++ b/spec/helpers/events_helper_spec.rb @@ -20,5 +20,9 @@ describe EventsHelper do it 'handles nil values' do expect(helper.event_commit_title(nil)).to eq('') end + + it 'does not escape HTML entities' do + expect(helper.event_commit_title("foo & bar")).to eq("foo & bar") + end end end diff --git a/spec/lib/banzai/filter/html_entity_filter_spec.rb b/spec/lib/banzai/filter/html_entity_filter_spec.rb index 91e18d876d5..43e85cbcb24 100644 --- a/spec/lib/banzai/filter/html_entity_filter_spec.rb +++ b/spec/lib/banzai/filter/html_entity_filter_spec.rb @@ -3,17 +3,12 @@ require 'spec_helper' describe Banzai::Filter::HtmlEntityFilter do include FilterSpecHelper - let(:unescaped) { 'foo &&&' } - let(:escaped) { 'foo <strike attr="foo">&&&</strike>' } + let(:unescaped) { 'foo &&' } + let(:escaped) { 'foo <strike attr="foo">&&amp;&</strike>' } it 'converts common entities to their HTML-escaped equivalents' do output = filter(unescaped) expect(output).to eq(escaped) end - - it 'does not double-escape' do - escaped = ERB::Util.html_escape("Merge branch 'blabla' into 'master'") - expect(filter(escaped)).to eq(escaped) - end end -- cgit v1.2.3 From e090366f78add95ecda4032f2043b6037d082a01 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 15 Feb 2018 13:41:55 +0000 Subject: Fix spec --- spec/lib/banzai/filter/html_entity_filter_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/banzai/filter/html_entity_filter_spec.rb b/spec/lib/banzai/filter/html_entity_filter_spec.rb index 43e85cbcb24..1d98fc0d5db 100644 --- a/spec/lib/banzai/filter/html_entity_filter_spec.rb +++ b/spec/lib/banzai/filter/html_entity_filter_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Banzai::Filter::HtmlEntityFilter do include FilterSpecHelper - let(:unescaped) { 'foo &&' } + let(:unescaped) { 'foo &&&' } let(:escaped) { 'foo <strike attr="foo">&&amp;&</strike>' } it 'converts common entities to their HTML-escaped equivalents' do -- cgit v1.2.3 From c312d2b9ffeaed9c30a9776d2ef5a7c126d5a5db Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 15 Feb 2018 16:26:44 +0000 Subject: Updated pipelines charts to use dynamic webpack bundle #41341 --- .../pages/projects/pipelines/charts/index.js | 56 ++++++++++++++++++++++ .../javascripts/pipelines/pipelines_times.js | 27 ----------- app/views/projects/pipelines/charts.html.haml | 3 -- .../projects/pipelines/charts/_pipeline_times.haml | 3 -- .../projects/pipelines/charts/_pipelines.haml | 3 -- config/webpack.config.js | 4 +- 6 files changed, 57 insertions(+), 39 deletions(-) create mode 100644 app/assets/javascripts/pages/projects/pipelines/charts/index.js delete mode 100644 app/assets/javascripts/pipelines/pipelines_times.js diff --git a/app/assets/javascripts/pages/projects/pipelines/charts/index.js b/app/assets/javascripts/pages/projects/pipelines/charts/index.js new file mode 100644 index 00000000000..c1dafda0e24 --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipelines/charts/index.js @@ -0,0 +1,56 @@ +import Chart from 'vendor/Chart'; + +const options = { + scaleOverlay: true, + responsive: true, + maintainAspectRatio: false, +}; + +const buildChart = (chartScope) => { + const data = { + labels: chartScope.labels, + datasets: [{ + fillColor: '#707070', + strokeColor: '#707070', + pointColor: '#707070', + pointStrokeColor: '#EEE', + data: chartScope.totalValues, + }, + { + fillColor: '#1aaa55', + strokeColor: '#1aaa55', + pointColor: '#1aaa55', + pointStrokeColor: '#fff', + data: chartScope.successValues, + }, + ], + }; + const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d'); + + new Chart(ctx).Line(data, options); +}; + +document.addEventListener('DOMContentLoaded', () => { + const chartTimesData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML); + const chartsData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML); + const data = { + labels: chartTimesData.labels, + datasets: [{ + fillColor: 'rgba(220,220,220,0.5)', + strokeColor: 'rgba(220,220,220,1)', + barStrokeWidth: 1, + barValueSpacing: 1, + barDatasetSpacing: 1, + data: chartTimesData.values, + }], + }; + + if (window.innerWidth < 768) { + // Scale fonts if window width lower than 768px (iPad portrait) + options.scaleFontSize = 8; + } + + new Chart($('#build_timesChart').get(0).getContext('2d')).Bar(data, options); + + chartsData.forEach(scope => buildChart(scope)); +}); diff --git a/app/assets/javascripts/pipelines/pipelines_times.js b/app/assets/javascripts/pipelines/pipelines_times.js deleted file mode 100644 index b5e7a0e53d9..00000000000 --- a/app/assets/javascripts/pipelines/pipelines_times.js +++ /dev/null @@ -1,27 +0,0 @@ -import Chart from 'vendor/Chart'; - -document.addEventListener('DOMContentLoaded', () => { - const chartData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML); - const data = { - labels: chartData.labels, - datasets: [{ - fillColor: 'rgba(220,220,220,0.5)', - strokeColor: 'rgba(220,220,220,1)', - barStrokeWidth: 1, - barValueSpacing: 1, - barDatasetSpacing: 1, - data: chartData.values, - }], - }; - const ctx = $('#build_timesChart').get(0).getContext('2d'); - const options = { - scaleOverlay: true, - responsive: true, - maintainAspectRatio: false, - }; - if (window.innerWidth < 768) { - // Scale fonts if window width lower than 768px (iPad portrait) - options.scaleFontSize = 8; - } - new Chart(ctx).Bar(data, options); -}); diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml index ba55bc23add..a86cb14960a 100644 --- a/app/views/projects/pipelines/charts.html.haml +++ b/app/views/projects/pipelines/charts.html.haml @@ -1,9 +1,6 @@ - @no_container = true - breadcrumb_title "CI / CD Charts" - page_title _("Charts"), _("Pipelines") -- content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_d3') - = page_specific_javascript_bundle_tag('graphs') %div{ class: container_class } .sub-header-block diff --git a/app/views/projects/pipelines/charts/_pipeline_times.haml b/app/views/projects/pipelines/charts/_pipeline_times.haml index a5dbd1b1532..510697c2ae9 100644 --- a/app/views/projects/pipelines/charts/_pipeline_times.haml +++ b/app/views/projects/pipelines/charts/_pipeline_times.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('pipelines_times') - %div %p.light = _("Commit duration in minutes for last 30 commits") diff --git a/app/views/projects/pipelines/charts/_pipelines.haml b/app/views/projects/pipelines/charts/_pipelines.haml index 41dc2f6cf9d..2f4b6def155 100644 --- a/app/views/projects/pipelines/charts/_pipelines.haml +++ b/app/views/projects/pipelines/charts/_pipelines.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('pipelines_charts') - %h4= _("Pipelines charts") %p   diff --git a/config/webpack.config.js b/config/webpack.config.js index 49653b61181..4d7115b263b 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -79,9 +79,7 @@ var config = { notes: './notes/index.js', pdf_viewer: './blob/pdf_viewer.js', pipelines: './pipelines/pipelines_bundle.js', - pipelines_charts: './pipelines/pipelines_charts.js', pipelines_details: './pipelines/pipeline_details_bundle.js', - pipelines_times: './pipelines/pipelines_times.js', profile: './profile/profile_bundle.js', project_import_gl: './projects/project_import_gitlab_project.js', prometheus_metrics: './prometheus_metrics', @@ -156,7 +154,7 @@ var config = { include: /node_modules\/katex\/dist/, use: [ { loader: 'style-loader' }, - { + { loader: 'css-loader', options: { name: '[name].[hash].[ext]' -- cgit v1.2.3 From 7d79b5ccc4bb5f51bf04f80083c31127f70c06ca Mon Sep 17 00:00:00 2001 From: Jacopo Date: Thu, 15 Feb 2018 15:09:57 +0100 Subject: Fixes Renaming repository (project[name]) fails on 2nd try whend the validation has failed --- app/views/projects/edit.html.haml | 2 +- app/views/projects/update.js.haml | 2 +- .../unreleased/43201-rename-repository-submit-button-disabled.yml | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/43201-rename-repository-submit-button-disabled.yml diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index e16d132f869..0931ceb1512 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -58,7 +58,7 @@ - if @project.avatar? %hr = link_to _('Remove avatar'), project_avatar_path(@project), data: { confirm: _("Avatar will be removed. Are you sure?") }, method: :delete, class: "btn btn-danger btn-inverted" - = f.submit 'Save changes', class: "btn btn-success" + = f.submit 'Save changes', class: "btn btn-success js-btn-save-general-project-settings" %section.settings.sharing-permissions.no-animate{ class: ('expanded' if expanded) } .settings-header diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml index 2c05ebe52ae..1a353953838 100644 --- a/app/views/projects/update.js.haml +++ b/app/views/projects/update.js.haml @@ -6,4 +6,4 @@ $(".project-edit-errors").html("#{escape_javascript(render('errors'))}"); $('.save-project-loader').hide(); $('.project-edit-container').show(); - $('.edit-project .btn-save').enable(); + $('.edit-project .js-btn-save-general-project-settings').enable(); diff --git a/changelogs/unreleased/43201-rename-repository-submit-button-disabled.yml b/changelogs/unreleased/43201-rename-repository-submit-button-disabled.yml new file mode 100644 index 00000000000..b527000332e --- /dev/null +++ b/changelogs/unreleased/43201-rename-repository-submit-button-disabled.yml @@ -0,0 +1,5 @@ +--- +title: Allows project rename after validation error +merge_request: 17150 +author: +type: fixed -- cgit v1.2.3 From efbe0768c35399ef58b6abac230adff6a1851088 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 15 Feb 2018 11:26:01 -0800 Subject: Filter secret variable values from logs Right now Project::VariablesController users the `value` parameter to send the secret variable value. `value` is a pretty generic term and could be used in other controllers, but for now it's better to err on the side of caution and filter this out. Closes #43313 --- config/application.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/application.rb b/config/application.rb index c914e34b9c3..918bd4d57cf 100644 --- a/config/application.rb +++ b/config/application.rb @@ -69,6 +69,7 @@ module Gitlab # - Webhook URLs (:hook) # - Sentry DSN (:sentry_dsn) # - Deploy keys (:key) + # - Secret variable values (:value) config.filter_parameters += [/token$/, /password/, /secret/] config.filter_parameters += %i( certificate @@ -80,6 +81,7 @@ module Gitlab sentry_dsn trace variables + value ) # Enable escaping HTML in JSON. -- cgit v1.2.3 From e5c8a47cbb39dddb0855c1ddfa505f083fff0c78 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 8 Feb 2018 15:36:11 -0600 Subject: fix monaco editor config to ensure service workers are not loaded from cross-domain origins --- app/assets/javascripts/ide/monaco_loader.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/assets/javascripts/ide/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js index af83a1ec0b4..142a220097b 100644 --- a/app/assets/javascripts/ide/monaco_loader.js +++ b/app/assets/javascripts/ide/monaco_loader.js @@ -6,6 +6,11 @@ monacoContext.require.config({ }, }); +// ignore CDN config and use local assets path for service worker which cannot be cross-domain +const relativeRootPath = (gon && gon.relative_url_root) || ''; +const monacoPath = `${relativeRootPath}/assets/webpack/monaco-editor/vs`; +window.MonacoEnvironment = { getWorkerUrl: () => `${monacoPath}/base/worker/workerMain.js` }; + // eslint-disable-next-line no-underscore-dangle window.__monaco_context__ = monacoContext; export default monacoContext.require; -- cgit v1.2.3 From b49d5dd11eda9e1213e0f3c87aab863924fe16cc Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 8 Feb 2018 15:39:51 -0600 Subject: add CHANGELOG.md entry for !17021 --- .../42641-monaco-service-workers-do-not-work-with-cdn-enabled.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/42641-monaco-service-workers-do-not-work-with-cdn-enabled.yml diff --git a/changelogs/unreleased/42641-monaco-service-workers-do-not-work-with-cdn-enabled.yml b/changelogs/unreleased/42641-monaco-service-workers-do-not-work-with-cdn-enabled.yml new file mode 100644 index 00000000000..955a5a27e21 --- /dev/null +++ b/changelogs/unreleased/42641-monaco-service-workers-do-not-work-with-cdn-enabled.yml @@ -0,0 +1,5 @@ +--- +title: Fix monaco editor features which were incompatable with GitLab CDN settings +merge_request: 17021 +author: +type: fixed -- cgit v1.2.3 From f5caabad73e9a2045e76f50df8107dddef39d80d Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 15 Feb 2018 21:51:41 +0000 Subject: Replace $.get with axios in importerStatus --- app/assets/javascripts/importer_status.js | 49 +++++++++++--------- spec/javascripts/importer_status_spec.js | 74 ++++++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 28 deletions(-) diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js index 134a503864e..35094f8e73b 100644 --- a/app/assets/javascripts/importer_status.js +++ b/app/assets/javascripts/importer_status.js @@ -59,29 +59,36 @@ class ImporterStatus { .catch(() => flash(__('An error occurred while importing project'))); } - setAutoUpdate() { - return setInterval(() => $.get(this.jobsUrl, data => $.each(data, (i, job) => { - const jobItem = $(`#project_${job.id}`); - const statusField = jobItem.find('.job-status'); + autoUpdate() { + return axios.get(this.jobsUrl) + .then(({ data = [] }) => { + data.forEach((job) => { + const jobItem = $(`#project_${job.id}`); + const statusField = jobItem.find('.job-status'); + + const spinner = ''; - const spinner = ''; + switch (job.import_status) { + case 'finished': + jobItem.removeClass('active').addClass('success'); + statusField.html(' done'); + break; + case 'scheduled': + statusField.html(`${spinner} scheduled`); + break; + case 'started': + statusField.html(`${spinner} started`); + break; + default: + statusField.html(job.import_status); + break; + } + }); + }); + } - switch (job.import_status) { - case 'finished': - jobItem.removeClass('active').addClass('success'); - statusField.html(' done'); - break; - case 'scheduled': - statusField.html(`${spinner} scheduled`); - break; - case 'started': - statusField.html(`${spinner} started`); - break; - default: - statusField.html(job.import_status); - break; - } - })), 4000); + setAutoUpdate() { + setInterval(this.autoUpdate.bind(this), 4000); } } diff --git a/spec/javascripts/importer_status_spec.js b/spec/javascripts/importer_status_spec.js index bb49c576e91..71a2cd51f63 100644 --- a/spec/javascripts/importer_status_spec.js +++ b/spec/javascripts/importer_status_spec.js @@ -3,9 +3,18 @@ import axios from '~/lib/utils/axios_utils'; import MockAdapter from 'axios-mock-adapter'; describe('Importer Status', () => { + let instance; + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + describe('addToImport', () => { - let instance; - let mock; const importUrl = '/import_url'; beforeEach(() => { @@ -21,11 +30,6 @@ describe('Importer Status', () => { spyOn(ImporterStatus.prototype, 'initStatusPage').and.callFake(() => {}); spyOn(ImporterStatus.prototype, 'setAutoUpdate').and.callFake(() => {}); instance = new ImporterStatus('', importUrl); - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); }); it('sets table row to active after post request', (done) => { @@ -44,4 +48,60 @@ describe('Importer Status', () => { .catch(done.fail); }); }); + + describe('autoUpdate', () => { + const jobsUrl = '/jobs_url'; + + beforeEach(() => { + const div = document.createElement('div'); + div.innerHTML = ` +
+
+
+
+ `; + + document.body.appendChild(div); + + spyOn(ImporterStatus.prototype, 'initStatusPage').and.callFake(() => {}); + spyOn(ImporterStatus.prototype, 'setAutoUpdate').and.callFake(() => {}); + instance = new ImporterStatus(jobsUrl); + }); + + function setupMock(importStatus) { + mock.onGet(jobsUrl).reply(200, [{ + id: 1, + import_status: importStatus, + }]); + } + + function expectJobStatus(done, status) { + instance.autoUpdate() + .then(() => { + expect(document.querySelector('#project_1').innerText.trim()).toEqual(status); + done(); + }) + .catch(done.fail); + } + + it('sets the job status to done', (done) => { + setupMock('finished'); + expectJobStatus(done, 'done'); + }); + + it('sets the job status to scheduled', (done) => { + setupMock('scheduled'); + expectJobStatus(done, 'scheduled'); + }); + + it('sets the job status to started', (done) => { + setupMock('started'); + expectJobStatus(done, 'started'); + }); + + it('sets the job status to custom status', (done) => { + setupMock('custom status'); + expectJobStatus(done, 'custom status'); + }); + }); }); -- cgit v1.2.3 From a68a2306e47f59865f134ef3f2bda6d54c5ee972 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 15 Feb 2018 23:07:04 +0000 Subject: Remove Webpack bundle tag for users:show --- app/assets/javascripts/dispatcher.js | 5 - .../javascripts/pages/users/activity_calendar.js | 242 +++++++++++++++++++++ app/assets/javascripts/pages/users/index.js | 27 +++ app/assets/javascripts/pages/users/show/index.js | 3 - app/assets/javascripts/pages/users/user_tabs.js | 199 +++++++++++++++++ app/assets/javascripts/users/activity_calendar.js | 242 --------------------- app/assets/javascripts/users/index.js | 25 --- app/assets/javascripts/users/user_tabs.js | 199 ----------------- app/views/users/show.html.haml | 3 - config/webpack.config.js | 1 - 10 files changed, 468 insertions(+), 478 deletions(-) create mode 100644 app/assets/javascripts/pages/users/activity_calendar.js create mode 100644 app/assets/javascripts/pages/users/index.js delete mode 100644 app/assets/javascripts/pages/users/show/index.js create mode 100644 app/assets/javascripts/pages/users/user_tabs.js delete mode 100644 app/assets/javascripts/users/activity_calendar.js delete mode 100644 app/assets/javascripts/users/index.js delete mode 100644 app/assets/javascripts/users/user_tabs.js diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index e4288dc1317..8f708dde063 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -467,11 +467,6 @@ var Dispatcher; .then(callDefault) .catch(fail); break; - case 'users:show': - import('./pages/users/show') - .then(callDefault) - .catch(fail); - break; case 'admin:conversational_development_index:show': import('./pages/admin/conversational_development_index/show') .then(callDefault) diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js new file mode 100644 index 00000000000..57306322aa4 --- /dev/null +++ b/app/assets/javascripts/pages/users/activity_calendar.js @@ -0,0 +1,242 @@ +import _ from 'underscore'; +import { scaleLinear, scaleThreshold } from 'd3-scale'; +import { select } from 'd3-selection'; +import { getDayName, getDayDifference } from '~/lib/utils/datetime_utility'; +import axios from '~/lib/utils/axios_utils'; +import flash from '~/flash'; +import { __ } from '~/locale'; + +const d3 = { select, scaleLinear, scaleThreshold }; + +const LOADING_HTML = ` +
+ +
+`; + +function getSystemDate(systemUtcOffsetSeconds) { + const date = new Date(); + const localUtcOffsetMinutes = 0 - date.getTimezoneOffset(); + const systemUtcOffsetMinutes = systemUtcOffsetSeconds / 60; + date.setMinutes((date.getMinutes() - localUtcOffsetMinutes) + systemUtcOffsetMinutes); + return date; +} + +function formatTooltipText({ date, count }) { + const dateObject = new Date(date); + const dateDayName = getDayName(dateObject); + const dateText = dateObject.format('mmm d, yyyy'); + + let contribText = 'No contributions'; + if (count > 0) { + contribText = `${count} contribution${count > 1 ? 's' : ''}`; + } + return `${contribText}
${dateDayName} ${dateText}`; +} + +const initColorKey = () => d3.scaleLinear().range(['#acd5f2', '#254e77']).domain([0, 3]); + +export default class ActivityCalendar { + constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0) { + this.calendarActivitiesPath = calendarActivitiesPath; + this.clickDay = this.clickDay.bind(this); + this.currentSelectedDate = ''; + this.daySpace = 1; + this.daySize = 15; + this.daySizeWithSpace = this.daySize + (this.daySpace * 2); + this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + this.months = []; + + // Loop through the timestamps to create a group of objects + // The group of objects will be grouped based on the day of the week they are + this.timestampsTmp = []; + let group = 0; + + const today = getSystemDate(utcOffset); + today.setHours(0, 0, 0, 0, 0); + + const oneYearAgo = new Date(today); + oneYearAgo.setFullYear(today.getFullYear() - 1); + + const days = getDayDifference(oneYearAgo, today); + + for (let i = 0; i <= days; i += 1) { + const date = new Date(oneYearAgo); + date.setDate(date.getDate() + i); + + const day = date.getDay(); + const count = timestamps[date.format('yyyy-mm-dd')] || 0; + + // Create a new group array if this is the first day of the week + // or if is first object + if ((day === 0 && i !== 0) || i === 0) { + this.timestampsTmp.push([]); + group += 1; + } + + // Push to the inner array the values that will be used to render map + const innerArray = this.timestampsTmp[group - 1]; + innerArray.push({ count, date, day }); + } + + // Init color functions + this.colorKey = initColorKey(); + this.color = this.initColor(); + + // Init the svg element + this.svg = this.renderSvg(container, group); + this.renderDays(); + this.renderMonths(); + this.renderDayTitles(); + this.renderKey(); + + // Init tooltips + $(`${container} .js-tooltip`).tooltip({ html: true }); + } + + // Add extra padding for the last month label if it is also the last column + getExtraWidthPadding(group) { + let extraWidthPadding = 0; + const lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); + const secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); + + if (lastColMonth !== secondLastColMonth) { + extraWidthPadding = 6; + } + + return extraWidthPadding; + } + + renderSvg(container, group) { + const width = ((group + 1) * this.daySizeWithSpace) + this.getExtraWidthPadding(group); + return d3.select(container) + .append('svg') + .attr('width', width) + .attr('height', 167) + .attr('class', 'contrib-calendar'); + } + + renderDays() { + this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g') + .attr('transform', (group, i) => { + _.each(group, (stamp, a) => { + if (a === 0 && stamp.day === 0) { + const month = stamp.date.getMonth(); + const x = (this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace; + const lastMonth = _.last(this.months); + if ( + lastMonth == null || + (month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonth.x) + ) { + this.months.push({ month, x }); + } + } + }); + return `translate(${(this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace}, 18)`; + }) + .selectAll('rect') + .data(stamp => stamp) + .enter() + .append('rect') + .attr('x', '0') + .attr('y', stamp => this.daySizeWithSpace * stamp.day) + .attr('width', this.daySize) + .attr('height', this.daySize) + .attr('fill', stamp => ( + stamp.count !== 0 ? this.color(Math.min(stamp.count, 40)) : '#ededed' + )) + .attr('title', stamp => formatTooltipText(stamp)) + .attr('class', 'user-contrib-cell js-tooltip') + .attr('data-container', 'body') + .on('click', this.clickDay); + } + + renderDayTitles() { + const days = [ + { + text: 'M', + y: 29 + (this.daySizeWithSpace * 1), + }, { + text: 'W', + y: 29 + (this.daySizeWithSpace * 3), + }, { + text: 'F', + y: 29 + (this.daySizeWithSpace * 5), + }, + ]; + this.svg.append('g') + .selectAll('text') + .data(days) + .enter() + .append('text') + .attr('text-anchor', 'middle') + .attr('x', 8) + .attr('y', day => day.y) + .text(day => day.text) + .attr('class', 'user-contrib-text'); + } + + renderMonths() { + this.svg.append('g') + .attr('direction', 'ltr') + .selectAll('text') + .data(this.months) + .enter() + .append('text') + .attr('x', date => date.x) + .attr('y', 10) + .attr('class', 'user-contrib-text') + .text(date => this.monthNames[date.month]); + } + + renderKey() { + const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions']; + const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; + + this.svg.append('g') + .attr('transform', `translate(18, ${(this.daySizeWithSpace * 8) + 16})`) + .selectAll('rect') + .data(keyColors) + .enter() + .append('rect') + .attr('width', this.daySize) + .attr('height', this.daySize) + .attr('x', (color, i) => this.daySizeWithSpace * i) + .attr('y', 0) + .attr('fill', color => color) + .attr('class', 'js-tooltip') + .attr('title', (color, i) => keyValues[i]) + .attr('data-container', 'body'); + } + + initColor() { + const colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; + return d3.scaleThreshold().domain([0, 10, 20, 30]).range(colorRange); + } + + clickDay(stamp) { + if (this.currentSelectedDate !== stamp.date) { + this.currentSelectedDate = stamp.date; + + const date = [ + this.currentSelectedDate.getFullYear(), + this.currentSelectedDate.getMonth() + 1, + this.currentSelectedDate.getDate(), + ].join('-'); + + $('.user-calendar-activities').html(LOADING_HTML); + + axios.get(this.calendarActivitiesPath, { + params: { + date, + }, + responseType: 'text', + }) + .then(({ data }) => $('.user-calendar-activities').html(data)) + .catch(() => flash(__('An error occurred while retrieving calendar activity'))); + } else { + this.currentSelectedDate = ''; + $('.user-calendar-activities').html(''); + } + } +} diff --git a/app/assets/javascripts/pages/users/index.js b/app/assets/javascripts/pages/users/index.js new file mode 100644 index 00000000000..899dcd42e37 --- /dev/null +++ b/app/assets/javascripts/pages/users/index.js @@ -0,0 +1,27 @@ +import UserCallout from '~/user_callout'; +import Cookies from 'js-cookie'; +import UserTabs from './user_tabs'; + +function initUserProfile(action) { + // place profile avatars to top + $('.profile-groups-avatars').tooltip({ + placement: 'top', + }); + + // eslint-disable-next-line no-new + new UserTabs({ parentEl: '.user-profile', action }); + + // hide project limit message + $('.hide-project-limit-message').on('click', (e) => { + e.preventDefault(); + Cookies.set('hide_project_limit_message', 'false'); + $(this).parents('.project-limit-message').remove(); + }); +} + +document.addEventListener('DOMContentLoaded', () => { + const page = $('body').attr('data-page'); + const action = page.split(':')[1]; + initUserProfile(action); + new UserCallout(); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/pages/users/show/index.js b/app/assets/javascripts/pages/users/show/index.js deleted file mode 100644 index f18f98b4e9a..00000000000 --- a/app/assets/javascripts/pages/users/show/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import UserCallout from '~/user_callout'; - -export default () => new UserCallout(); diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js new file mode 100644 index 00000000000..c1217623467 --- /dev/null +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -0,0 +1,199 @@ +import axios from '~/lib/utils/axios_utils'; +import Activities from '~/activities'; +import { localTimeAgo } from '~/lib/utils/datetime_utility'; +import { __ } from '~/locale'; +import flash from '~/flash'; +import ActivityCalendar from './activity_calendar'; + +/** + * UserTabs + * + * Handles persisting and restoring the current tab selection and lazily-loading + * content on the Users#show page. + * + * ### Example Markup + * + * + * + *
+ *
+ * Activity Content + *
+ *
+ * Groups Content + *
+ *
+ * Contributed projects content + *
+ *
+ * Projects content + *
+ *
+ * Snippets content + *
+ *
+ * + *
+ *
+ * Loading Animation + *
+ *
+ */ + +const CALENDAR_TEMPLATE = ` +
+
+
+ Summary of issues, merge requests, push events, and comments +
+
+`; + +export default class UserTabs { + constructor({ defaultAction, action, parentEl }) { + this.loaded = {}; + this.defaultAction = defaultAction || 'activity'; + this.action = action || this.defaultAction; + this.$parentEl = $(parentEl) || $(document); + this.windowLocation = window.location; + this.$parentEl.find('.nav-links a') + .each((i, navLink) => { + this.loaded[$(navLink).attr('data-action')] = false; + }); + this.actions = Object.keys(this.loaded); + this.bindEvents(); + + if (this.action === 'show') { + this.action = this.defaultAction; + } + + this.activateTab(this.action); + } + + bindEvents() { + this.$parentEl + .off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') + .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)) + .on('click', '.gl-pagination a', event => this.changeProjectsPage(event)); + } + + changeProjectsPage(e) { + e.preventDefault(); + + $('.tab-pane.active').empty(); + const endpoint = $(e.target).attr('href'); + this.loadTab(this.getCurrentAction(), endpoint); + } + + tabShown(event) { + const $target = $(event.target); + const action = $target.data('action'); + const source = $target.attr('href'); + const endpoint = $target.data('endpoint'); + this.setTab(action, endpoint); + return this.setCurrentAction(source); + } + + activateTab(action) { + return this.$parentEl.find(`.nav-links .js-${action}-tab a`) + .tab('show'); + } + + setTab(action, endpoint) { + if (this.loaded[action]) { + return; + } + if (action === 'activity') { + this.loadActivities(); + } + + const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; + if (loadableActions.indexOf(action) > -1) { + this.loadTab(action, endpoint); + } + } + + loadTab(action, endpoint) { + this.toggleLoading(true); + + return axios.get(endpoint) + .then(({ data }) => { + const tabSelector = `div#${action}`; + this.$parentEl.find(tabSelector).html(data.html); + this.loaded[action] = true; + localTimeAgo($('.js-timeago', tabSelector)); + + this.toggleLoading(false); + }) + .catch(() => { + this.toggleLoading(false); + }); + } + + loadActivities() { + if (this.loaded.activity) { + return; + } + const $calendarWrap = this.$parentEl.find('.user-calendar'); + const calendarPath = $calendarWrap.data('calendarPath'); + const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); + const utcOffset = $calendarWrap.data('utcOffset'); + let utcFormatted = 'UTC'; + if (utcOffset !== 0) { + utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${(utcOffset / 3600)}`; + } + + axios.get(calendarPath) + .then(({ data }) => { + $calendarWrap.html(CALENDAR_TEMPLATE); + $calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`); + + // eslint-disable-next-line no-new + new ActivityCalendar('.js-contrib-calendar', data, calendarActivitiesPath, utcOffset); + }) + .catch(() => flash(__('There was an error loading users activity calendar.'))); + + // eslint-disable-next-line no-new + new Activities(); + this.loaded.activity = true; + } + + toggleLoading(status) { + return this.$parentEl.find('.loading-status .loading') + .toggle(status); + } + + setCurrentAction(source) { + let newState = source; + newState = newState.replace(/\/+$/, ''); + newState += this.windowLocation.search + this.windowLocation.hash; + history.replaceState({ + url: newState, + }, document.title, newState); + return newState; + } + + getCurrentAction() { + return this.$parentEl.find('.nav-links .active a').data('action'); + } +} diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js deleted file mode 100644 index 57306322aa4..00000000000 --- a/app/assets/javascripts/users/activity_calendar.js +++ /dev/null @@ -1,242 +0,0 @@ -import _ from 'underscore'; -import { scaleLinear, scaleThreshold } from 'd3-scale'; -import { select } from 'd3-selection'; -import { getDayName, getDayDifference } from '~/lib/utils/datetime_utility'; -import axios from '~/lib/utils/axios_utils'; -import flash from '~/flash'; -import { __ } from '~/locale'; - -const d3 = { select, scaleLinear, scaleThreshold }; - -const LOADING_HTML = ` -
- -
-`; - -function getSystemDate(systemUtcOffsetSeconds) { - const date = new Date(); - const localUtcOffsetMinutes = 0 - date.getTimezoneOffset(); - const systemUtcOffsetMinutes = systemUtcOffsetSeconds / 60; - date.setMinutes((date.getMinutes() - localUtcOffsetMinutes) + systemUtcOffsetMinutes); - return date; -} - -function formatTooltipText({ date, count }) { - const dateObject = new Date(date); - const dateDayName = getDayName(dateObject); - const dateText = dateObject.format('mmm d, yyyy'); - - let contribText = 'No contributions'; - if (count > 0) { - contribText = `${count} contribution${count > 1 ? 's' : ''}`; - } - return `${contribText}
${dateDayName} ${dateText}`; -} - -const initColorKey = () => d3.scaleLinear().range(['#acd5f2', '#254e77']).domain([0, 3]); - -export default class ActivityCalendar { - constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0) { - this.calendarActivitiesPath = calendarActivitiesPath; - this.clickDay = this.clickDay.bind(this); - this.currentSelectedDate = ''; - this.daySpace = 1; - this.daySize = 15; - this.daySizeWithSpace = this.daySize + (this.daySpace * 2); - this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - this.months = []; - - // Loop through the timestamps to create a group of objects - // The group of objects will be grouped based on the day of the week they are - this.timestampsTmp = []; - let group = 0; - - const today = getSystemDate(utcOffset); - today.setHours(0, 0, 0, 0, 0); - - const oneYearAgo = new Date(today); - oneYearAgo.setFullYear(today.getFullYear() - 1); - - const days = getDayDifference(oneYearAgo, today); - - for (let i = 0; i <= days; i += 1) { - const date = new Date(oneYearAgo); - date.setDate(date.getDate() + i); - - const day = date.getDay(); - const count = timestamps[date.format('yyyy-mm-dd')] || 0; - - // Create a new group array if this is the first day of the week - // or if is first object - if ((day === 0 && i !== 0) || i === 0) { - this.timestampsTmp.push([]); - group += 1; - } - - // Push to the inner array the values that will be used to render map - const innerArray = this.timestampsTmp[group - 1]; - innerArray.push({ count, date, day }); - } - - // Init color functions - this.colorKey = initColorKey(); - this.color = this.initColor(); - - // Init the svg element - this.svg = this.renderSvg(container, group); - this.renderDays(); - this.renderMonths(); - this.renderDayTitles(); - this.renderKey(); - - // Init tooltips - $(`${container} .js-tooltip`).tooltip({ html: true }); - } - - // Add extra padding for the last month label if it is also the last column - getExtraWidthPadding(group) { - let extraWidthPadding = 0; - const lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); - const secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); - - if (lastColMonth !== secondLastColMonth) { - extraWidthPadding = 6; - } - - return extraWidthPadding; - } - - renderSvg(container, group) { - const width = ((group + 1) * this.daySizeWithSpace) + this.getExtraWidthPadding(group); - return d3.select(container) - .append('svg') - .attr('width', width) - .attr('height', 167) - .attr('class', 'contrib-calendar'); - } - - renderDays() { - this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g') - .attr('transform', (group, i) => { - _.each(group, (stamp, a) => { - if (a === 0 && stamp.day === 0) { - const month = stamp.date.getMonth(); - const x = (this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace; - const lastMonth = _.last(this.months); - if ( - lastMonth == null || - (month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonth.x) - ) { - this.months.push({ month, x }); - } - } - }); - return `translate(${(this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace}, 18)`; - }) - .selectAll('rect') - .data(stamp => stamp) - .enter() - .append('rect') - .attr('x', '0') - .attr('y', stamp => this.daySizeWithSpace * stamp.day) - .attr('width', this.daySize) - .attr('height', this.daySize) - .attr('fill', stamp => ( - stamp.count !== 0 ? this.color(Math.min(stamp.count, 40)) : '#ededed' - )) - .attr('title', stamp => formatTooltipText(stamp)) - .attr('class', 'user-contrib-cell js-tooltip') - .attr('data-container', 'body') - .on('click', this.clickDay); - } - - renderDayTitles() { - const days = [ - { - text: 'M', - y: 29 + (this.daySizeWithSpace * 1), - }, { - text: 'W', - y: 29 + (this.daySizeWithSpace * 3), - }, { - text: 'F', - y: 29 + (this.daySizeWithSpace * 5), - }, - ]; - this.svg.append('g') - .selectAll('text') - .data(days) - .enter() - .append('text') - .attr('text-anchor', 'middle') - .attr('x', 8) - .attr('y', day => day.y) - .text(day => day.text) - .attr('class', 'user-contrib-text'); - } - - renderMonths() { - this.svg.append('g') - .attr('direction', 'ltr') - .selectAll('text') - .data(this.months) - .enter() - .append('text') - .attr('x', date => date.x) - .attr('y', 10) - .attr('class', 'user-contrib-text') - .text(date => this.monthNames[date.month]); - } - - renderKey() { - const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions']; - const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; - - this.svg.append('g') - .attr('transform', `translate(18, ${(this.daySizeWithSpace * 8) + 16})`) - .selectAll('rect') - .data(keyColors) - .enter() - .append('rect') - .attr('width', this.daySize) - .attr('height', this.daySize) - .attr('x', (color, i) => this.daySizeWithSpace * i) - .attr('y', 0) - .attr('fill', color => color) - .attr('class', 'js-tooltip') - .attr('title', (color, i) => keyValues[i]) - .attr('data-container', 'body'); - } - - initColor() { - const colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; - return d3.scaleThreshold().domain([0, 10, 20, 30]).range(colorRange); - } - - clickDay(stamp) { - if (this.currentSelectedDate !== stamp.date) { - this.currentSelectedDate = stamp.date; - - const date = [ - this.currentSelectedDate.getFullYear(), - this.currentSelectedDate.getMonth() + 1, - this.currentSelectedDate.getDate(), - ].join('-'); - - $('.user-calendar-activities').html(LOADING_HTML); - - axios.get(this.calendarActivitiesPath, { - params: { - date, - }, - responseType: 'text', - }) - .then(({ data }) => $('.user-calendar-activities').html(data)) - .catch(() => flash(__('An error occurred while retrieving calendar activity'))); - } else { - this.currentSelectedDate = ''; - $('.user-calendar-activities').html(''); - } - } -} diff --git a/app/assets/javascripts/users/index.js b/app/assets/javascripts/users/index.js deleted file mode 100644 index 9fd8452a2b6..00000000000 --- a/app/assets/javascripts/users/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import Cookies from 'js-cookie'; -import UserTabs from './user_tabs'; - -function initUserProfile(action) { - // place profile avatars to top - $('.profile-groups-avatars').tooltip({ - placement: 'top', - }); - - // eslint-disable-next-line no-new - new UserTabs({ parentEl: '.user-profile', action }); - - // hide project limit message - $('.hide-project-limit-message').on('click', (e) => { - e.preventDefault(); - Cookies.set('hide_project_limit_message', 'false'); - $(this).parents('.project-limit-message').remove(); - }); -} - -document.addEventListener('DOMContentLoaded', () => { - const page = $('body').attr('data-page'); - const action = page.split(':')[1]; - initUserProfile(action); -}); diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js deleted file mode 100644 index e13b9839a20..00000000000 --- a/app/assets/javascripts/users/user_tabs.js +++ /dev/null @@ -1,199 +0,0 @@ -import axios from '../lib/utils/axios_utils'; -import Activities from '../activities'; -import ActivityCalendar from './activity_calendar'; -import { localTimeAgo } from '../lib/utils/datetime_utility'; -import { __ } from '../locale'; -import flash from '../flash'; - -/** - * UserTabs - * - * Handles persisting and restoring the current tab selection and lazily-loading - * content on the Users#show page. - * - * ### Example Markup - * - * - * - *
- *
- * Activity Content - *
- *
- * Groups Content - *
- *
- * Contributed projects content - *
- *
- * Projects content - *
- *
- * Snippets content - *
- *
- * - *
- *
- * Loading Animation - *
- *
- */ - -const CALENDAR_TEMPLATE = ` -
-
-
- Summary of issues, merge requests, push events, and comments -
-
-`; - -export default class UserTabs { - constructor({ defaultAction, action, parentEl }) { - this.loaded = {}; - this.defaultAction = defaultAction || 'activity'; - this.action = action || this.defaultAction; - this.$parentEl = $(parentEl) || $(document); - this.windowLocation = window.location; - this.$parentEl.find('.nav-links a') - .each((i, navLink) => { - this.loaded[$(navLink).attr('data-action')] = false; - }); - this.actions = Object.keys(this.loaded); - this.bindEvents(); - - if (this.action === 'show') { - this.action = this.defaultAction; - } - - this.activateTab(this.action); - } - - bindEvents() { - this.$parentEl - .off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') - .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)) - .on('click', '.gl-pagination a', event => this.changeProjectsPage(event)); - } - - changeProjectsPage(e) { - e.preventDefault(); - - $('.tab-pane.active').empty(); - const endpoint = $(e.target).attr('href'); - this.loadTab(this.getCurrentAction(), endpoint); - } - - tabShown(event) { - const $target = $(event.target); - const action = $target.data('action'); - const source = $target.attr('href'); - const endpoint = $target.data('endpoint'); - this.setTab(action, endpoint); - return this.setCurrentAction(source); - } - - activateTab(action) { - return this.$parentEl.find(`.nav-links .js-${action}-tab a`) - .tab('show'); - } - - setTab(action, endpoint) { - if (this.loaded[action]) { - return; - } - if (action === 'activity') { - this.loadActivities(); - } - - const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; - if (loadableActions.indexOf(action) > -1) { - this.loadTab(action, endpoint); - } - } - - loadTab(action, endpoint) { - this.toggleLoading(true); - - return axios.get(endpoint) - .then(({ data }) => { - const tabSelector = `div#${action}`; - this.$parentEl.find(tabSelector).html(data.html); - this.loaded[action] = true; - localTimeAgo($('.js-timeago', tabSelector)); - - this.toggleLoading(false); - }) - .catch(() => { - this.toggleLoading(false); - }); - } - - loadActivities() { - if (this.loaded.activity) { - return; - } - const $calendarWrap = this.$parentEl.find('.user-calendar'); - const calendarPath = $calendarWrap.data('calendarPath'); - const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); - const utcOffset = $calendarWrap.data('utcOffset'); - let utcFormatted = 'UTC'; - if (utcOffset !== 0) { - utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${(utcOffset / 3600)}`; - } - - axios.get(calendarPath) - .then(({ data }) => { - $calendarWrap.html(CALENDAR_TEMPLATE); - $calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`); - - // eslint-disable-next-line no-new - new ActivityCalendar('.js-contrib-calendar', data, calendarActivitiesPath, utcOffset); - }) - .catch(() => flash(__('There was an error loading users activity calendar.'))); - - // eslint-disable-next-line no-new - new Activities(); - this.loaded.activity = true; - } - - toggleLoading(status) { - return this.$parentEl.find('.loading-status .loading') - .toggle(status); - } - - setCurrentAction(source) { - let newState = source; - newState = newState.replace(/\/+$/, ''); - newState += this.windowLocation.search + this.windowLocation.hash; - history.replaceState({ - url: newState, - }, document.title, newState); - return newState; - } - - getCurrentAction() { - return this.$parentEl.find('.nav-links .active a').data('action'); - } -} diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index c9a77d668a2..a396d1007a7 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -4,9 +4,6 @@ - page_description @user.bio - header_title @user.name, user_path(@user) - @no_container = true -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_d3' - = webpack_bundle_tag 'users' = content_for :meta_tags do = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity") diff --git a/config/webpack.config.js b/config/webpack.config.js index 3fff808f166..3c581a156dd 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -99,7 +99,6 @@ var config = { vue_merge_request_widget: './vue_merge_request_widget/index.js', test: './test.js', two_factor_auth: './two_factor_auth.js', - users: './users/index.js', webpack_runtime: './webpack.js', }, -- cgit v1.2.3 From 4634e3a109f2b3a761bf959128812226b425fc7d Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 15 Feb 2018 17:19:54 -0600 Subject: Add axios to frontend docs as the default network request library --- doc/development/fe_guide/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md index 5c2e50e8c8e..12dfc10812b 100644 --- a/doc/development/fe_guide/index.md +++ b/doc/development/fe_guide/index.md @@ -21,6 +21,8 @@ Working with our frontend assets requires Node (v4.3 or greater) and Yarn [jQuery][jquery] is used throughout the application's JavaScript, with [Vue.js][vue] for particularly advanced, dynamic elements. +We also use [Axios][axios] to handle all of our network requests. + ### Browser Support For our currently-supported browsers, see our [requirements][requirements]. @@ -124,6 +126,7 @@ The [externalization part of the guide](../i18n/externalization.md) explains the [webpack]: https://webpack.js.org/ [jquery]: https://jquery.com/ [vue]: http://vuejs.org/ +[axios]: https://github.com/axios/axios [airbnb-js-style-guide]: https://github.com/airbnb/javascript [scss-lint]: https://github.com/brigade/scss-lint [install]: ../../install/installation.md#4-node -- cgit v1.2.3 From bda4f0811e3d7f3530d1d6c338e2de6ada5bf1f2 Mon Sep 17 00:00:00 2001 From: Dylan Griffith Date: Fri, 16 Feb 2018 17:38:45 +1100 Subject: Improve error handling for Gitlab::Profiler and improve doc about providing a user --- doc/development/profiling.md | 11 +++++++++++ lib/gitlab/profiler.rb | 1 + spec/lib/gitlab/profiler_spec.rb | 9 +++++++++ 3 files changed, 21 insertions(+) diff --git a/doc/development/profiling.md b/doc/development/profiling.md index 97c997e0568..11878b4009b 100644 --- a/doc/development/profiling.md +++ b/doc/development/profiling.md @@ -27,6 +27,17 @@ Gitlab::Profiler.profile('/my-user') # Returns a RubyProf::Profile where 100 seconds is spent in UsersController#show ``` +For routes that require authorization you will need to provide a user to +`Gitlab::Profiler`. You can do this like so: + +```ruby +Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first) +``` + +The user you provide will need to have a [personal access +token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html) in +the GitLab instance. + Passing a `logger:` keyword argument to `Gitlab::Profiler.profile` will send ActiveRecord and ActionController log output to that logger. Further options are documented with the method source. diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb index 95d94b3cc68..98a168b43bb 100644 --- a/lib/gitlab/profiler.rb +++ b/lib/gitlab/profiler.rb @@ -45,6 +45,7 @@ module Gitlab if user private_token ||= user.personal_access_tokens.active.pluck(:token).first + raise 'Your user must have a personal_access_token' unless private_token end headers['Private-Token'] = private_token if private_token diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb index 4a43dbb2371..f02b1cf55fb 100644 --- a/spec/lib/gitlab/profiler_spec.rb +++ b/spec/lib/gitlab/profiler_spec.rb @@ -53,6 +53,15 @@ describe Gitlab::Profiler do described_class.profile('/', user: user) end + context 'when providing a user without a personal access token' do + it 'raises an error' do + user = double(:user) + allow(user).to receive_message_chain(:personal_access_tokens, :active, :pluck).and_return([]) + + expect { described_class.profile('/', user: user) }.to raise_error('Your user must have a personal_access_token') + end + end + it 'uses the private_token for auth if both it and user are set' do user = double(:user) user_token = 'user' -- cgit v1.2.3 From f80c5730bec095bbcf45c395c94f3c44452a6442 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Fri, 16 Feb 2018 15:13:34 +0530 Subject: Ignore `_links` property for no-underscore-dangle rule --- .eslintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index ad5eaebccae..8f9cdfb14ac 100644 --- a/.eslintrc +++ b/.eslintrc @@ -36,7 +36,7 @@ "import/no-commonjs": "error", "no-multiple-empty-lines": ["error", { "max": 1 }], "promise/catch-or-return": "error", - "no-underscore-dangle": ["error", { "allow": ["__"]}], + "no-underscore-dangle": ["error", { "allow": ["__", "_links"]}], "vue/html-self-closing": ["error", { "html": { "void": "always", -- cgit v1.2.3 From d74c63e61b2e4e610f6880acb7cafcc4f5f42060 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 16 Feb 2018 10:26:05 +0000 Subject: deleted pipelines_charts file it is no longer used now that all the pipeline chart stuff is in 1 file --- .../javascripts/pipelines/pipelines_charts.js | 38 ---------------------- 1 file changed, 38 deletions(-) delete mode 100644 app/assets/javascripts/pipelines/pipelines_charts.js diff --git a/app/assets/javascripts/pipelines/pipelines_charts.js b/app/assets/javascripts/pipelines/pipelines_charts.js deleted file mode 100644 index 821aa7e229f..00000000000 --- a/app/assets/javascripts/pipelines/pipelines_charts.js +++ /dev/null @@ -1,38 +0,0 @@ -import Chart from 'vendor/Chart'; - -document.addEventListener('DOMContentLoaded', () => { - const chartData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML); - const buildChart = (chartScope) => { - const data = { - labels: chartScope.labels, - datasets: [{ - fillColor: '#707070', - strokeColor: '#707070', - pointColor: '#707070', - pointStrokeColor: '#EEE', - data: chartScope.totalValues, - }, - { - fillColor: '#1aaa55', - strokeColor: '#1aaa55', - pointColor: '#1aaa55', - pointStrokeColor: '#fff', - data: chartScope.successValues, - }, - ], - }; - const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d'); - const options = { - scaleOverlay: true, - responsive: true, - maintainAspectRatio: false, - }; - if (window.innerWidth < 768) { - // Scale fonts if window width lower than 768px (iPad portrait) - options.scaleFontSize = 8; - } - new Chart(ctx).Line(data, options); - }; - - chartData.forEach(scope => buildChart(scope)); -}); -- cgit v1.2.3 From 8469725ec4a43b24f825017c5447529f9b4a2cc8 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 16 Feb 2018 11:10:34 +0000 Subject: Show loading button inline in MR widget --- .../components/states/mr_widget_auto_merge_failed.vue | 5 ++++- changelogs/unreleased/fl-refresh-btn.yml | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/fl-refresh-btn.yml diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue index 40c3cb500bb..ebaf2b972eb 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue @@ -44,7 +44,10 @@ type="button" class="btn btn-xs btn-default" > - + {{ s__("mrWidget|Refresh") }} diff --git a/changelogs/unreleased/fl-refresh-btn.yml b/changelogs/unreleased/fl-refresh-btn.yml new file mode 100644 index 00000000000..640fdda9ce7 --- /dev/null +++ b/changelogs/unreleased/fl-refresh-btn.yml @@ -0,0 +1,5 @@ +--- +title: Show loading button inline in refresh button in MR widget +merge_request: +author: +type: fixed -- cgit v1.2.3 From 523f1f7cf484bea334c365ca21ab3dd492613b55 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Fri, 16 Feb 2018 12:38:50 +0100 Subject: Removed prometheus_metrics webpack bundle tag --- app/assets/javascripts/pages/projects/services/edit/index.js | 7 +++++++ app/assets/javascripts/prometheus_metrics/index.js | 6 ------ app/views/projects/services/prometheus/_show.html.haml | 3 --- config/webpack.config.js | 1 - 4 files changed, 7 insertions(+), 10 deletions(-) delete mode 100644 app/assets/javascripts/prometheus_metrics/index.js diff --git a/app/assets/javascripts/pages/projects/services/edit/index.js b/app/assets/javascripts/pages/projects/services/edit/index.js index 434a7e44277..5c73171e62e 100644 --- a/app/assets/javascripts/pages/projects/services/edit/index.js +++ b/app/assets/javascripts/pages/projects/services/edit/index.js @@ -1,6 +1,13 @@ import IntegrationSettingsForm from '~/integrations/integration_settings_form'; +import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics'; export default () => { + const prometheusSettingsWrapper = document.querySelector('.js-prometheus-metrics-monitoring'); const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); integrationSettingsForm.init(); + + if (prometheusSettingsWrapper) { + const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring'); + prometheusMetrics.loadActiveMetrics(); + } }; diff --git a/app/assets/javascripts/prometheus_metrics/index.js b/app/assets/javascripts/prometheus_metrics/index.js deleted file mode 100644 index a0c43c5abe1..00000000000 --- a/app/assets/javascripts/prometheus_metrics/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import PrometheusMetrics from './prometheus_metrics'; - -$(() => { - const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring'); - prometheusMetrics.loadActiveMetrics(); -}); diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml index b0cb5ce5e8f..5f38ecd6820 100644 --- a/app/views/projects/services/prometheus/_show.html.haml +++ b/app/views/projects/services/prometheus/_show.html.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('prometheus_metrics') - .row.prepend-top-default.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring .col-lg-3 %h4.prepend-top-0 diff --git a/config/webpack.config.js b/config/webpack.config.js index 3c581a156dd..4e0ae90f8ef 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -83,7 +83,6 @@ var config = { pipelines_times: './pipelines/pipelines_times.js', profile: './profile/profile_bundle.js', project_import_gl: './projects/project_import_gitlab_project.js', - prometheus_metrics: './prometheus_metrics', protected_branches: './protected_branches', protected_tags: './protected_tags', registry_list: './registry/index.js', -- cgit v1.2.3 From 2f817f6528db0b835bd59f3241e2cf72b83cdb3a Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 13 Feb 2018 14:32:34 +0100 Subject: Replace "Kubernetes cluster" where appropriate in docs Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/42939 --- doc/ci/README.md | 2 + doc/user/project/clusters/index.md | 117 +++++++++++++++++-------------------- 2 files changed, 55 insertions(+), 64 deletions(-) diff --git a/doc/ci/README.md b/doc/ci/README.md index eabeb4510db..532ae52a184 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -70,6 +70,8 @@ learn how to leverage its potential even more. - [Use SSH keys in your build environment](ssh_keys/README.md) - [Trigger pipelines through the GitLab API](triggers/README.md) - [Trigger pipelines on a schedule](../user/project/pipelines/schedules.md) +- [Kubernetes clusters](../user/project/clusters/index.md) - Integrate one or + more Kubernetes clusters to your project ## GitLab CI/CD for Docker diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 50a8e0d5ec5..bbe25c2d911 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -5,20 +5,23 @@ Connect your project to Google Kubernetes Engine (GKE) or an existing Kubernetes cluster in a few steps. -With a cluster associated to your project, you can use Review Apps, deploy your -applications, run your pipelines, and much more, in an easy way. +## Overview + +With a Kubernetes cluster associated to your project, you can use +[Review Apps](../../../ci/review_apps/index.md), deploy your applications, run +your pipelines, and much more, in an easy way. There are two options when adding a new cluster to your project; either associate your account with Google Kubernetes Engine (GKE) so that you can [create new clusters](#adding-and-creating-a-new-gke-cluster-via-gitlab) from within GitLab, or provide the credentials to an [existing Kubernetes cluster](#adding-an-existing-kubernetes-cluster). -## Prerequisites +## Adding and creating a new GKE cluster via GitLab -In order to be able to manage your Kubernetes cluster through GitLab, the -following prerequisites must be met. +NOTE: **Note:** +You need Master [permissions] and above to access the Kubernetes page. -**For a cluster hosted on GKE:** +Before proceeding, make sure the following requirements are met: - The [Google authentication integration](../../../integration/google.md) must be enabled in GitLab at the instance level. If that's not the case, ask your @@ -28,30 +31,16 @@ following prerequisites must be met. account](https://cloud.google.com/billing/docs/how-to/manage-billing-account) must be set up and that you have to have permissions to access it. - You must have Master [permissions] in order to be able to access the - **Cluster** page. + **Kubernetes** page. - You must have [Cloud Billing API](https://cloud.google.com/billing/) enabled - You must have [Resource Manager API](https://cloud.google.com/resource-manager/) -**For an existing Kubernetes cluster:** - -- Since the cluster is already created, there are no prerequisites. - ---- - -If all of the above requirements are met, you can proceed to add a new Kubernetes -cluster. - -## Adding and creating a new GKE cluster via GitLab - -NOTE: **Note:** -You need Master [permissions] and above to access the Clusters page. - -Before proceeding, make sure all [prerequisites](#prerequisites) are met. -To add a new cluster hosted on GKE to your project: +If all of the above requirements are met, you can proceed to create and add a +new Kubernetes cluster that will be hosted on GKE to your project: -1. Navigate to your project's **CI/CD > Clusters** page. -1. Click on **Add cluster**. +1. Navigate to your project's **CI/CD > Kubernetes** page. +1. Click on **Add Kubernetes cluster**. 1. Click on **Create with GKE**. 1. Connect your Google account if you haven't done already by clicking the **Sign in with Google** button. @@ -66,7 +55,7 @@ To add a new cluster hosted on GKE to your project: - **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types) of the Virtual Machine instance that the cluster will be based on. - **Environment scope** - The [associated environment](#setting-the-environment-scope) to this cluster. -1. Finally, click the **Create cluster** button. +1. Finally, click the **Create Kubernetes cluster** button. After a few moments, your cluster should be created. If something goes wrong, you will be notified. @@ -77,14 +66,14 @@ enable the Cluster integration. ## Adding an existing Kubernetes cluster NOTE: **Note:** -You need Master [permissions] and above to access the Clusters page. +You need Master [permissions] and above to access the Kubernetes page. To add an existing Kubernetes cluster to your project: -1. Navigate to your project's **CI/CD > Clusters** page. -1. Click on **Add cluster**. -1. Click on **Add an existing cluster** and fill in the details: - - **Cluster name** (required) - The name you wish to give the cluster. +1. Navigate to your project's **CI/CD > Kubernetes** page. +1. Click on **Add Kuberntes cluster**. +1. Click on **Add an existing Kubernetes cluster** and fill in the details: + - **Kubernetes cluster name** (required) - The name you wish to give the cluster. - **Environment scope** (required)- The [associated environment](#setting-the-environment-scope) to this cluster. - **API URL** (required) - @@ -112,15 +101,13 @@ To add an existing Kubernetes cluster to your project: - If you or someone created a secret specifically for the project, usually with limited permissions, the secret's namespace and project namespace may be the same. -1. Finally, click the **Create cluster** button. - -The Kubernetes service takes the following parameters: +1. Finally, click the **Create Kuberntes cluster** button. After a few moments, your cluster should be created. If something goes wrong, you will be notified. You can now proceed to install some pre-defined applications and then -enable the Cluster integration. +enable the Kubernetes cluster integration. ## Installing applications @@ -139,7 +126,7 @@ added directly to your configured cluster. Those applications are needed for NOTE: **Note:** You need a load balancer installed in your cluster in order to obtain the external IP address with the following procedure. It can be deployed using the -**Ingress** application described in the previous section. +[**Ingress** application](#installing-appplications). In order to publish your web application, you first need to find the external IP address associated to your load balancer. @@ -153,7 +140,8 @@ the `gcloud` command in a local terminal or using the **Cloud Shell**. If the cluster is not on GKE, follow the specific instructions for your Kubernetes provider to configure `kubectl` with the right credentials. -If you installed the Ingress using the **Applications** section, run the following command: +If you installed the Ingress [via the **Applications**](#installing-applications), +run the following command: ```bash kubectl get svc --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip} ' @@ -171,9 +159,14 @@ your deployed applications. ## Setting the environment scope -When adding more than one clusters, you need to differentiate them with an -environment scope. The environment scope associates clusters and -[environments](../../../ci/environments.md) in an 1:1 relationship similar to how the +NOTE: **Note:** +This is only available for [GitLab Premium][ee] where you can add more than +one Kubernetes cluster. + +When adding more than one Kubernetes clusters to your project, you need to +differentiate them with an environment scope. The environment scope associates +clusters and [environments](../../../ci/environments.md) in an 1:1 relationship +similar to how the [environment-specific variables](../../../ci/variables/README.md#limiting-environment-scopes-of-secret-variables) work. @@ -183,7 +176,7 @@ cluster in a project, and a validation error will occur if otherwise. --- -For example, let's say the following clusters exist in a project: +For example, let's say the following Kubernetes clusters exist in a project: | Cluster | Environment scope | | ---------- | ------------------- | @@ -231,8 +224,7 @@ With GitLab Premium, you can associate more than one Kubernetes clusters to your project. That way you can have different clusters for different environments, like dev, staging, production, etc. -To add another cluster, follow the same steps as described in [adding a -Kubernetes cluster](#adding-a-kubernetes-cluster) and make sure to +Simply add another cluster, like you did the first time, and make sure to [set an environment scope](#setting-the-environment-scope) that will differentiate the new cluster with the rest. @@ -240,45 +232,42 @@ differentiate the new cluster with the rest. The Kubernetes cluster integration exposes the following [deployment variables](../../../ci/variables/README.md#deployment-variables) in the -GitLab CI/CD build environment: - -- `KUBE_URL` - Equal to the API URL. -- `KUBE_TOKEN` - The Kubernetes token. -- `KUBE_NAMESPACE` - The Kubernetes namespace is auto-generated if not specified. - The default value is `-`. You can overwrite it to - use different one if needed, otherwise the `KUBE_NAMESPACE` variable will - receive the default value. -- `KUBE_CA_PEM_FILE` - Only present if a custom CA bundle was specified. Path - to a file containing PEM data. -- `KUBE_CA_PEM` (deprecated) - Only if a custom CA bundle was specified. Raw PEM data. -- `KUBECONFIG` - Path to a file containing `kubeconfig` for this deployment. - CA bundle would be embedded if specified. - -## Enabling or disabling the Cluster integration +GitLab CI/CD build environment. + +| Variable | Description | +| -------- | ----------- | +| `KUBE_URL` | Equal to the API URL. | +| `KUBE_TOKEN` | The Kubernetes token. | +| `KUBE_NAMESPACE` | The Kubernetes namespace is auto-generated if not specified. The default value is `-`. You can overwrite it to use different one if needed, otherwise the `KUBE_NAMESPACE` variable will receive the default value. | +| `KUBE_CA_PEM_FILE` | Only present if a custom CA bundle was specified. Path to a file containing PEM data. | +| `KUBE_CA_PEM` | (**deprecated**) Only if a custom CA bundle was specified. Raw PEM data. | +| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. | + +## Enabling or disabling the Kubernetes cluster integration After you have successfully added your cluster information, you can enable the -Cluster integration: +Kubernetes cluster integration: 1. Click the "Enabled/Disabled" switch 1. Hit **Save** for the changes to take effect You can now start using your Kubernetes cluster for your deployments. -To disable the Cluster integration, follow the same procedure. +To disable the Kubernetes cluster integration, follow the same procedure. -## Removing the Cluster integration +## Removing the Kubernetes cluster integration NOTE: **Note:** -You need Master [permissions] and above to remove a cluster integration. +You need Master [permissions] and above to remove a Kubernetes cluster integration. NOTE: **Note:** When you remove a cluster, you only remove its relation to GitLab, not the cluster itself. To remove the cluster, you can do so by visiting the GKE dashboard or using `kubectl`. -To remove the Cluster integration from your project, simply click on the +To remove the Kubernetes cluster integration from your project, simply click on the **Remove integration** button. You will then be able to follow the procedure -and [add a cluster](#adding-a-cluster) again. +and add a Kubernetes cluster again. ## What you can get with the Kubernetes integration -- cgit v1.2.3 From 890d7b540b1ffbadcde490a2e1b741bbb1af3cf4 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 16 Feb 2018 14:37:26 +0100 Subject: update docs --- doc/api/README.md | 1 + doc/api/project_import_export.md | 6 +++--- doc/api/projects.md | 2 +- lib/api/project_import.rb | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/api/README.md b/doc/api/README.md index f226716c3b5..1927489b7bb 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -43,6 +43,7 @@ following locations: - [Pipeline Schedules](pipeline_schedules.md) - [Projects](projects.md) including setting Webhooks - [Project Access Requests](access_requests.md) +- [Project import/export](project_import_export.md) - [Project Members](members.md) - [Project Snippets](project_snippets.md) - [Protected Branches](protected_branches.md) diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md index c473b94aafe..e442442c750 100644 --- a/doc/api/project_import_export.md +++ b/doc/api/project_import_export.md @@ -2,7 +2,7 @@ [Introduced][ce-41899] in GitLab 10.6 -[Project import/export](../user/project/settings/import_export.md) +[See also the project import/export documentation](../user/project/settings/import_export.md) ## Import a file @@ -12,7 +12,7 @@ POST /projects/import | Attribute | Type | Required | Description | | --------- | -------------- | -------- | ---------------------------------------- | -| `namespace` | integer/string | no | The ID or path of the namespace that the project will be imported to. Defaults to the user's namespace | +| `namespace` | integer/string | no | The ID or path of the namespace that the project will be imported to. Defaults to the current user's namespace | | `file` | string | yes | The file to be uploaded | | `path` | string | yes | Name and path for new project | @@ -56,7 +56,7 @@ curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/a Status can be one of `none`, `scheduled`, `failed`, `started`, or `finished`. -If the status is `failed`, it will include the import error message. +If the status is `failed`, it will include the import error message under `import_error`. ```json { diff --git a/doc/api/projects.md b/doc/api/projects.md index 08df65b2f66..2cdfd1d2ca7 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1328,7 +1328,7 @@ Read more in the [Branches](branches.md) documentation. ## Project Import/Export -Read more in the [Project export](project_import_export.md) documentation. +Read more in the [Project import/export](project_import_export.md) documentation. ## Project members diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index a6da9b90fe3..c32e2f26ae3 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -24,7 +24,7 @@ module API params do requires :path, type: String, desc: 'The new project path and name' requires :file, type: File, desc: 'The project export file to be imported' - optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be imported into. Defaults to the user namespace.' + optional :namespace, type: String, desc: "The ID or name of the namespace that the project will be imported into. Defaults to the current user's namespace." end desc 'Create a new project import' do detail 'This feature was introduced in GitLab 10.6.' -- cgit v1.2.3 From e19d45ddcfefe13343312ba4bd8ad1c1563394ba Mon Sep 17 00:00:00 2001 From: Olivier Gonzalez Date: Fri, 16 Feb 2018 09:09:24 -0500 Subject: Update vendored AutoDevOps YML for latest SAST changes --- vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml index 094d6791505..0ac8885405e 100644 --- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml @@ -321,7 +321,15 @@ production: # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" SAST_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - docker run --volume "$PWD:/code" \ + # Deprecation notice for CONFIDENCE_LEVEL variable + if [ -z "$SAST_CONFIDENCE_LEVEL" -a "$CONFIDENCE_LEVEL" ]; then + SAST_CONFIDENCE_LEVEL="$CONFIDENCE_LEVEL" + echo "WARNING: CONFIDENCE_LEVEL is deprecated and MUST be replaced with SAST_CONFIDENCE_LEVEL" + fi + + docker run --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}" \ + --env SAST_DISABLE_REMOTE_CHECKS="${SAST_DISABLE_REMOTE_CHECKS:-false}" \ + --volume "$PWD:/code" \ --volume /var/run/docker.sock:/var/run/docker.sock \ "registry.gitlab.com/gitlab-org/security-products/sast:$SAST_VERSION" /app/bin/run /code ;; -- cgit v1.2.3 From b94131b6704a3240832a638844e9a07d1fbe93a3 Mon Sep 17 00:00:00 2001 From: Joshua Lambert Date: Fri, 16 Feb 2018 15:47:34 +0000 Subject: Update GCP install instructions to utilize a standard VM --- .../google_cloud_platform/img/boot_disk.png | Bin 0 -> 124175 bytes .../img/change_admin_passwd_email.png | Bin 7193 -> 0 bytes .../img/chrome_not_secure_page.png | Bin 21705 -> 0 bytes .../google_cloud_platform/img/first_signin.png | Bin 0 -> 69516 bytes .../img/gcp_gitlab_being_deployed.png | Bin 23486 -> 0 bytes .../img/gcp_gitlab_overview.png | Bin 42028 -> 0 bytes .../google_cloud_platform/img/gcp_landing.png | Bin 59912 -> 15025 bytes .../img/gcp_launcher_console_home_page.png | Bin 42090 -> 0 bytes .../img/gcp_search_for_gitlab.png | Bin 7648 -> 0 bytes .../img/gitlab_deployed_page.png | Bin 35573 -> 0 bytes .../img/gitlab_first_sign_in.png | Bin 20054 -> 0 bytes .../img/gitlab_launch_button.png | Bin 5198 -> 0 bytes .../google_cloud_platform/img/launch_vm.png | Bin 0 -> 47201 bytes .../img/new_gitlab_deployment_settings.png | Bin 50014 -> 0 bytes .../google_cloud_platform/img/ssh_terminal.png | Bin 0 -> 94161 bytes .../google_cloud_platform/img/ssh_via_button.png | Bin 3062 -> 0 bytes .../google_cloud_platform/img/vm_created.png | Bin 0 -> 13809 bytes .../google_cloud_platform/img/vm_details.png | Bin 0 -> 76519 bytes doc/install/google_cloud_platform/index.md | 86 ++++++--------------- 19 files changed, 24 insertions(+), 62 deletions(-) create mode 100644 doc/install/google_cloud_platform/img/boot_disk.png delete mode 100644 doc/install/google_cloud_platform/img/change_admin_passwd_email.png delete mode 100644 doc/install/google_cloud_platform/img/chrome_not_secure_page.png create mode 100644 doc/install/google_cloud_platform/img/first_signin.png delete mode 100644 doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png delete mode 100644 doc/install/google_cloud_platform/img/gcp_gitlab_overview.png delete mode 100644 doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png delete mode 100644 doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png delete mode 100644 doc/install/google_cloud_platform/img/gitlab_deployed_page.png delete mode 100644 doc/install/google_cloud_platform/img/gitlab_first_sign_in.png delete mode 100644 doc/install/google_cloud_platform/img/gitlab_launch_button.png create mode 100644 doc/install/google_cloud_platform/img/launch_vm.png delete mode 100644 doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png create mode 100644 doc/install/google_cloud_platform/img/ssh_terminal.png delete mode 100644 doc/install/google_cloud_platform/img/ssh_via_button.png create mode 100644 doc/install/google_cloud_platform/img/vm_created.png create mode 100644 doc/install/google_cloud_platform/img/vm_details.png diff --git a/doc/install/google_cloud_platform/img/boot_disk.png b/doc/install/google_cloud_platform/img/boot_disk.png new file mode 100644 index 00000000000..37b2d9eaae7 Binary files /dev/null and b/doc/install/google_cloud_platform/img/boot_disk.png differ diff --git a/doc/install/google_cloud_platform/img/change_admin_passwd_email.png b/doc/install/google_cloud_platform/img/change_admin_passwd_email.png deleted file mode 100644 index 1ffe14f60ff..00000000000 Binary files a/doc/install/google_cloud_platform/img/change_admin_passwd_email.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/chrome_not_secure_page.png b/doc/install/google_cloud_platform/img/chrome_not_secure_page.png deleted file mode 100644 index e732066908f..00000000000 Binary files a/doc/install/google_cloud_platform/img/chrome_not_secure_page.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/first_signin.png b/doc/install/google_cloud_platform/img/first_signin.png new file mode 100644 index 00000000000..6eb3392d674 Binary files /dev/null and b/doc/install/google_cloud_platform/img/first_signin.png differ diff --git a/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png b/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png deleted file mode 100644 index 2a1859da6e3..00000000000 Binary files a/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png b/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png deleted file mode 100644 index 1c4c870dbc9..00000000000 Binary files a/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/gcp_landing.png b/doc/install/google_cloud_platform/img/gcp_landing.png index 6398d247ba0..d6390c4dd4f 100644 Binary files a/doc/install/google_cloud_platform/img/gcp_landing.png and b/doc/install/google_cloud_platform/img/gcp_landing.png differ diff --git a/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png b/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png deleted file mode 100644 index f492888ea4a..00000000000 Binary files a/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png b/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png deleted file mode 100644 index b38af3966e2..00000000000 Binary files a/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/gitlab_deployed_page.png b/doc/install/google_cloud_platform/img/gitlab_deployed_page.png deleted file mode 100644 index fef9ae45f32..00000000000 Binary files a/doc/install/google_cloud_platform/img/gitlab_deployed_page.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png b/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png deleted file mode 100644 index 381c0fe48a5..00000000000 Binary files a/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/gitlab_launch_button.png b/doc/install/google_cloud_platform/img/gitlab_launch_button.png deleted file mode 100644 index 50f66f66118..00000000000 Binary files a/doc/install/google_cloud_platform/img/gitlab_launch_button.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/launch_vm.png b/doc/install/google_cloud_platform/img/launch_vm.png new file mode 100644 index 00000000000..3fd13f232bb Binary files /dev/null and b/doc/install/google_cloud_platform/img/launch_vm.png differ diff --git a/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png b/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png deleted file mode 100644 index 00060841619..00000000000 Binary files a/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/ssh_terminal.png b/doc/install/google_cloud_platform/img/ssh_terminal.png new file mode 100644 index 00000000000..6a1a418d8e9 Binary files /dev/null and b/doc/install/google_cloud_platform/img/ssh_terminal.png differ diff --git a/doc/install/google_cloud_platform/img/ssh_via_button.png b/doc/install/google_cloud_platform/img/ssh_via_button.png deleted file mode 100644 index 26106f159ad..00000000000 Binary files a/doc/install/google_cloud_platform/img/ssh_via_button.png and /dev/null differ diff --git a/doc/install/google_cloud_platform/img/vm_created.png b/doc/install/google_cloud_platform/img/vm_created.png new file mode 100644 index 00000000000..fb467f40838 Binary files /dev/null and b/doc/install/google_cloud_platform/img/vm_created.png differ diff --git a/doc/install/google_cloud_platform/img/vm_details.png b/doc/install/google_cloud_platform/img/vm_details.png new file mode 100644 index 00000000000..2d230416a4b Binary files /dev/null and b/doc/install/google_cloud_platform/img/vm_details.png differ diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md index c6b767fff02..3389f0260f9 100644 --- a/doc/install/google_cloud_platform/index.md +++ b/doc/install/google_cloud_platform/index.md @@ -2,12 +2,7 @@ ![GCP landing page](img/gcp_landing.png) -The fastest way to get started on [Google Cloud Platform (GCP)][gcp] is through -the [Google Cloud Launcher][launcher] program. - -GitLab's official Google Launcher apps: -1. [GitLab Community Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-community-edition?project=gitlab-public) -2. [GitLab Enterprise Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-enterprise-edition?project=gitlab-public) +Gettung started with GitLab on a [Google Cloud Platform (GCP)][gcp] instance is quick and easy. ## Prerequisites @@ -17,84 +12,52 @@ There are only two prerequisites in order to install GitLab on GCP: 1. You need to sign up for the GCP program. If this is your first time, Google gives you [$300 credit for free][freetrial] to consume over a 60-day period. -Once you have performed those two steps, you can visit the -[GCP launcher console][console] which has a list of all the things you can -deploy on GCP. - -![GCP launcher console](img/gcp_launcher_console_home_page.png) - -The next step is to find and install GitLab. +Once you have performed those two steps, you can [create a VM](#creating-the-vm). -## Configuring and deploying the VM +## Creating the VM To deploy GitLab on GCP you need to follow five simple steps: -1. Go to https://cloud.google.com/launcher and login with your Google credentials -1. Search for GitLab from GitLab Inc. (not the same as Bitnami) and click on - the tile. +1. Go to https://console.cloud.google.com/compute/instances and login with your Google credentials. - ![Search for GitLab](img/gcp_search_for_gitlab.png) +1. Click on **Create** -1. In the next page, you can see an overview of the GitLab VM as well as some - estimated costs. Click the **Launch on Compute Engine** button to choose the - hardware and network settings. + ![Search for GitLab](img/launch_vm.png) - ![Launch on Compute Engine](img/gcp_gitlab_overview.png) +1. On the next page, you can select the type of VM as well as the + estimated costs. Provide the name of the instance, desired datacenter, and machine type. Note that GitLab recommends at least 2 vCPU's and 4GB of RAM. -1. In the settings page you can choose things like the datacenter where your GitLab - server will be hosted, the number of CPUs and amount of RAM, the disk size - and type, etc. Read GitLab's [requirements documentation][req] for more - details on what to choose depending on your needs. + ![Launch on Compute Engine](img/vm_details.png) - ![Deploy settings](img/new_gitlab_deployment_settings.png) +1. Click **Change** under Boot disk to select the size, type, and desired operating system. GitLab supports a [variety of linux operating systems][req], including Ubuntu and Debian. Click **Select** when finished. -1. As a last step, hit **Deploy** when ready. The process will finish in a few - seconds. + ![Deploy in progress](img/boot_disk.png) - ![Deploy in progress](img/gcp_gitlab_being_deployed.png) +1. As a last step allow HTTP and HTTPS traffic, then click **Create**. The process will finish in a few seconds. +## Installing GitLab -## Visiting GitLab for the first time +After a few seconds, the instance will be created and available to log in. The next step is to install GitLab onto the instance. -After a few seconds, GitLab will be successfully deployed and you should be -able to see the IP address that Google assigned to the VM, as well as the -credentials to the GitLab admin account. +![Deploy settings](img/vm_created.png) -![Deploy settings](img/gitlab_deployed_page.png) +1. Make a note of the IP address of the instance, as you will need that in a later step. +1. Click on the SSH button to connect to the instance. +1. A new window will appear, with you logged into the instance. -1. Click on the IP under **Site address** to visit GitLab. -1. Accept the self-signed certificate that Google automatically deployed in - order to securely reach GitLab's login page. -1. Use the username and password that are present in the Google console page - to login into GitLab and click **Sign in**. + ![GitLab first sign in](img/ssh_terminal.png) - ![GitLab first sign in](img/gitlab_first_sign_in.png) +1. Next, follow the instructions for installing GitLab for the operating system you choose, at https://about.gitlab.com/installation/. You can use the IP address from the step above, as the hostname. -Congratulations! GitLab is now installed and you can access it via your browser, -but we're not done yet. There are some steps you need to take in order to have -a fully functional GitLab installation. +1. Congratulations! GitLab is now installed and you can access it via your browser. To finish installation, open the URL in your browser and provide the initial administrator password. The username for this account is `root`. + + ![GitLab first sign in](img/first_signin.png) ## Next steps These are the most important next steps to take after you installed GitLab for the first time. -### Changing the admin password and email - -Google assigned a random password for the GitLab admin account and you should -change it ASAP: - -1. Visit the GitLab admin page through the link in the Google console under - **Admin URL**. -1. Find the Administrator user under the **Users** page and hit **Edit**. -1. Change the email address to a real one and enter a new password. - - ![Change GitLab admin password](img/change_admin_passwd_email.png) - -1. Hit **Save changes** for the changes to take effect. -1. After changing the password, you will be signed out from GitLab. Use the - new credentials to login again. - ### Assigning a static IP By default, Google assigns an ephemeral IP to your instance. It is strongly @@ -112,7 +75,7 @@ here's how you configure GitLab to be aware of the change: 1. SSH into the VM. You can easily use the **SSH** button in the Google console and a new window will pop up. - ![SSH button](img/ssh_via_button.png) + ![SSH button](img/vm_created.png) In the future you might want to set up [connecting with an SSH key][ssh] instead. @@ -161,7 +124,6 @@ Kerberos, etc. Here are some documents you might be interested in reading: - [GitLab Pages configuration](https://docs.gitlab.com/ce/administration/pages/index.html) - [GitLab Container Registry configuration](https://docs.gitlab.com/ce/administration/container_registry.html) -[console]: https://console.cloud.google.com/launcher "GCP launcher console" [freetrial]: https://console.cloud.google.com/freetrial "GCP free trial" [ip]: https://cloud.google.com/compute/docs/configure-instance-ip-addresses#promote_ephemeral_ip "Configuring an Instance's IP Addresses" [gcp]: https://cloud.google.com/ "Google Cloud Platform" -- cgit v1.2.3 From d8dfec0ffa4fdbb383bf4f0d1ef71969adb50dd3 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 16 Feb 2018 18:03:34 +0100 Subject: Fix project import API after import service refactor --- lib/api/project_import.rb | 8 ++++++-- spec/requests/api/project_import_spec.rb | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index c32e2f26ae3..a509c1f32c1 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -41,8 +41,12 @@ module API current_user.namespace end - project_params = import_params.merge(namespace_id: namespace.id, - file: import_params[:file]['tempfile']) + project_params = { + path: import_params[:path], + namespace_id: namespace.id, + file: import_params[:file]['tempfile'] + } + project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute render_api_error!(project.errors.full_messages&.first, 400) unless project.saved? diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index eabf9095c13..987f6e26971 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -42,7 +42,7 @@ describe API::ProjectImport do it 'schedules an import at the user namespace level' do expect_any_instance_of(Project).not_to receive(:import_schedule) - expect(Gitlab::ImportExport::ProjectCreator).not_to receive(:new) + expect(::Projects::CreateService).not_to receive(:new) post api('/projects/import', user), namespace: 'nonexistent', path: 'test-import2', file: fixture_file_upload(file) @@ -73,7 +73,7 @@ describe API::ProjectImport do def stub_import(namespace) expect_any_instance_of(Project).to receive(:import_schedule) - expect(Gitlab::ImportExport::ProjectCreator).to receive(:new).with(namespace.id, any_args).and_call_original + expect(::Projects::CreateService).to receive(:new).with(user, hash_including(namespace_id: namespace.id)).and_call_original end end -- cgit v1.2.3 From 5d58949ea6ff3fb3ccb908518a0aef1f5bf6693e Mon Sep 17 00:00:00 2001 From: Ian Baum Date: Fri, 16 Feb 2018 12:13:39 -0500 Subject: Update CHANGELOG.md for 10.4.4 [ci skip] --- CHANGELOG.md | 19 +++++++++++++++++++ ...equest-undefined-method-index-for-nil-nilclass.yml | 5 ----- changelogs/unreleased/42591-update-nokogiri.yml | 5 ----- ...-gitlab-import-leaves-group_id-on-projectlabel.yml | 5 ----- ...0-on-fork-without-restricted-visibility-levels.yml | 5 ----- .../dm-user-namespace-route-path-validation.yml | 5 ----- .../fj-37528-error-after-disabling-ldap.yml | 6 ------ .../mk-fix-no-untracked-upload-files-error.yml | 5 ----- .../unreleased/remove_ldap_person_validation.yml | 5 ----- changelogs/unreleased/sh-fix-jira-trailing-slash.yml | 5 ----- .../unreleased/winh-new-branch-dropdown-style.yml | 5 ----- 11 files changed, 19 insertions(+), 51 deletions(-) delete mode 100644 changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml delete mode 100644 changelogs/unreleased/42591-update-nokogiri.yml delete mode 100644 changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml delete mode 100644 changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml delete mode 100644 changelogs/unreleased/dm-user-namespace-route-path-validation.yml delete mode 100644 changelogs/unreleased/fj-37528-error-after-disabling-ldap.yml delete mode 100644 changelogs/unreleased/mk-fix-no-untracked-upload-files-error.yml delete mode 100644 changelogs/unreleased/remove_ldap_person_validation.yml delete mode 100644 changelogs/unreleased/sh-fix-jira-trailing-slash.yml delete mode 100644 changelogs/unreleased/winh-new-branch-dropdown-style.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad603fdc75..c3bb93fbe3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.4.4 (2018-02-16) + +### Security (1 change) + +- Update nokogiri to 1.8.2. !16807 + +### Fixed (9 changes) + +- Fix 500 error when loading a merge request with an invalid comment. !16795 +- Cleanup new branch/merge request form in issues. !16854 +- Fix GitLab import leaving group_id on ProjectLabel. !16877 +- Fix forking projects when no restricted visibility levels are defined applicationwide. !16881 +- Resolve PrepareUntrackedUploads PostgreSQL syntax error. !17019 +- Fixed error 500 when removing an identity with synced attributes and visiting the profile page. !17054 +- Validate user namespace before saving so that errors persist on model. +- LDAP Person no longer throws exception on invalid entry. +- Fix JIRA not working when a trailing slash is included. + + ## 10.4.3 (2018-02-05) ### Security (4 changes) diff --git a/changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml b/changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml deleted file mode 100644 index 64340ab08cd..00000000000 --- a/changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix 500 error when loading a merge request with an invalid comment -merge_request: 16795 -author: -type: fixed diff --git a/changelogs/unreleased/42591-update-nokogiri.yml b/changelogs/unreleased/42591-update-nokogiri.yml deleted file mode 100644 index 5f9587d2d92..00000000000 --- a/changelogs/unreleased/42591-update-nokogiri.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update nokogiri to 1.8.2 -merge_request: 16807 -author: -type: security diff --git a/changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml b/changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml deleted file mode 100644 index c46cc86c99b..00000000000 --- a/changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix GitLab import leaving group_id on ProjectLabel -merge_request: 16877 -author: -type: fixed diff --git a/changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml b/changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml deleted file mode 100644 index 378f0ef7ce9..00000000000 --- a/changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix forking projects when no restricted visibility levels are defined applicationwide -merge_request: 16881 -author: -type: fixed diff --git a/changelogs/unreleased/dm-user-namespace-route-path-validation.yml b/changelogs/unreleased/dm-user-namespace-route-path-validation.yml deleted file mode 100644 index 36615e5b976..00000000000 --- a/changelogs/unreleased/dm-user-namespace-route-path-validation.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Validate user namespace before saving so that errors persist on model -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/fj-37528-error-after-disabling-ldap.yml b/changelogs/unreleased/fj-37528-error-after-disabling-ldap.yml deleted file mode 100644 index 1e35f2b537d..00000000000 --- a/changelogs/unreleased/fj-37528-error-after-disabling-ldap.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fixed error 500 when removing an identity with synced attributes and visiting - the profile page -merge_request: 17054 -author: -type: fixed diff --git a/changelogs/unreleased/mk-fix-no-untracked-upload-files-error.yml b/changelogs/unreleased/mk-fix-no-untracked-upload-files-error.yml deleted file mode 100644 index fddfba94192..00000000000 --- a/changelogs/unreleased/mk-fix-no-untracked-upload-files-error.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Resolve PrepareUntrackedUploads PostgreSQL syntax error -merge_request: 17019 -author: -type: fixed diff --git a/changelogs/unreleased/remove_ldap_person_validation.yml b/changelogs/unreleased/remove_ldap_person_validation.yml deleted file mode 100644 index da7f0a52886..00000000000 --- a/changelogs/unreleased/remove_ldap_person_validation.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: LDAP Person no longer throws exception on invalid entry -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/sh-fix-jira-trailing-slash.yml b/changelogs/unreleased/sh-fix-jira-trailing-slash.yml deleted file mode 100644 index 786f6cd3727..00000000000 --- a/changelogs/unreleased/sh-fix-jira-trailing-slash.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix JIRA not working when a trailing slash is included -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/winh-new-branch-dropdown-style.yml b/changelogs/unreleased/winh-new-branch-dropdown-style.yml deleted file mode 100644 index 007e9e9f453..00000000000 --- a/changelogs/unreleased/winh-new-branch-dropdown-style.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Cleanup new branch/merge request form in issues -merge_request: 16854 -author: -type: fixed -- cgit v1.2.3 From d9b299aced0ec7ed983dd8e6ece86cef25d03e1a Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 15:35:01 -0600 Subject: migrate projects:milestones:index to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/projects/milestones/index/index.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index e4288dc1317..d7ab737246f 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'projects:milestones:index': - import('./pages/projects/milestones/index') - .then(callDefault) - .catch(fail); - break; case 'projects:milestones:show': import('./pages/projects/milestones/show') .then(callDefault) diff --git a/app/assets/javascripts/pages/projects/milestones/index/index.js b/app/assets/javascripts/pages/projects/milestones/index/index.js index 8fb4d83d8a3..38789365a67 100644 --- a/app/assets/javascripts/pages/projects/milestones/index/index.js +++ b/app/assets/javascripts/pages/projects/milestones/index/index.js @@ -1,3 +1,3 @@ import milestones from '~/pages/milestones/shared'; -export default milestones; +document.addEventListener('DOMContentLoaded', milestones); -- cgit v1.2.3 From d163f906122d71a62e5c6bd2429a101a180863f6 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:06:26 -0600 Subject: migrate projects:milestones:show to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/projects/milestones/show/index.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index d7ab737246f..19a0a7ff1d5 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'projects:milestones:show': - import('./pages/projects/milestones/show') - .then(callDefault) - .catch(fail); - break; case 'groups:milestones:show': import('./pages/groups/milestones/show') .then(callDefault) diff --git a/app/assets/javascripts/pages/projects/milestones/show/index.js b/app/assets/javascripts/pages/projects/milestones/show/index.js index 35b5c9c2ced..84a52421598 100644 --- a/app/assets/javascripts/pages/projects/milestones/show/index.js +++ b/app/assets/javascripts/pages/projects/milestones/show/index.js @@ -1,7 +1,7 @@ import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; import milestones from '~/pages/milestones/shared'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { initMilestonesShow(); milestones(); -}; +}); -- cgit v1.2.3 From 5fa3bd178793d8084e495807b8b6cfa20974992b Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:09:34 -0600 Subject: migrate groups:milestones:show to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/groups/milestones/show/index.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 19a0a7ff1d5..216caceb9d5 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'groups:milestones:show': - import('./pages/groups/milestones/show') - .then(callDefault) - .catch(fail); - break; case 'dashboard:milestones:show': import('./pages/dashboard/milestones/show') .then(callDefault) diff --git a/app/assets/javascripts/pages/groups/milestones/show/index.js b/app/assets/javascripts/pages/groups/milestones/show/index.js index c9a18353f2e..88f40b5278e 100644 --- a/app/assets/javascripts/pages/groups/milestones/show/index.js +++ b/app/assets/javascripts/pages/groups/milestones/show/index.js @@ -1,3 +1,3 @@ import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; -export default initMilestonesShow; +document.addEventListener('DOMContentLoaded', initMilestonesShow); -- cgit v1.2.3 From feb86d49b016d2bd422f28b89284bebbe4bedf19 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:11:14 -0600 Subject: migrate dashboard:milestones:show to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/dashboard/milestones/show/index.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 216caceb9d5..5297104e9a1 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'dashboard:milestones:show': - import('./pages/dashboard/milestones/show') - .then(callDefault) - .catch(fail); - break; case 'dashboard:issues': import('./pages/dashboard/issues') .then(callDefault) diff --git a/app/assets/javascripts/pages/dashboard/milestones/show/index.js b/app/assets/javascripts/pages/dashboard/milestones/show/index.js index 2e7a08a369c..06195d73c0a 100644 --- a/app/assets/javascripts/pages/dashboard/milestones/show/index.js +++ b/app/assets/javascripts/pages/dashboard/milestones/show/index.js @@ -1,7 +1,7 @@ import Milestone from '~/milestone'; import Sidebar from '~/right_sidebar'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Milestone(); // eslint-disable-line no-new new Sidebar(); // eslint-disable-line no-new -}; +}); -- cgit v1.2.3 From 99bc2a4a2dbc3c53a030488d75136090b2c87aa1 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:13:06 -0600 Subject: migrate dashboard:issues to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/dashboard/issues/index.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 5297104e9a1..e7acde79b80 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'dashboard:issues': - import('./pages/dashboard/issues') - .then(callDefault) - .catch(fail); - break; case 'dashboard:merge_requests': import('./pages/dashboard/merge_requests') .then(callDefault) diff --git a/app/assets/javascripts/pages/dashboard/issues/index.js b/app/assets/javascripts/pages/dashboard/issues/index.js index b7353669e65..c4901dd1cb6 100644 --- a/app/assets/javascripts/pages/dashboard/issues/index.js +++ b/app/assets/javascripts/pages/dashboard/issues/index.js @@ -1,7 +1,7 @@ import projectSelect from '~/project_select'; import initLegacyFilters from '~/init_legacy_filters'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { projectSelect(); initLegacyFilters(); -}; +}); -- cgit v1.2.3 From 9bb3ff551c990db81246468314ef55a8a2a426b4 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:15:19 -0600 Subject: migrate dashboard:merge_requests to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/dashboard/merge_requests/index.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index e7acde79b80..c1716487fd3 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'dashboard:merge_requests': - import('./pages/dashboard/merge_requests') - .then(callDefault) - .catch(fail); - break; case 'groups:issues': import('./pages/groups/issues') .then(callDefault) diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js index b7353669e65..c4901dd1cb6 100644 --- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js +++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js @@ -1,7 +1,7 @@ import projectSelect from '~/project_select'; import initLegacyFilters from '~/init_legacy_filters'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { projectSelect(); initLegacyFilters(); -}; +}); -- cgit v1.2.3 From 17b8bfc8c6c4837b9f2fd825f8fcc192a6aaf962 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:15:57 -0600 Subject: migrate groups:issues to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/groups/issues/index.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index c1716487fd3..98142a1b9a6 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'groups:issues': - import('./pages/groups/issues') - .then(callDefault) - .catch(fail); - break; case 'groups:merge_requests': import('./pages/groups/merge_requests') .then(callDefault) diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js index fbdfabd1e95..d149b307e7f 100644 --- a/app/assets/javascripts/pages/groups/issues/index.js +++ b/app/assets/javascripts/pages/groups/issues/index.js @@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import { FILTERED_SEARCH } from '~/pages/constants'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { initFilteredSearch({ page: FILTERED_SEARCH.ISSUES, }); projectSelect(); -}; +}); -- cgit v1.2.3 From 8878dce8ce1a8a0550a5df9ee8d5f49be28abbd0 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:17:00 -0600 Subject: migrate groups:merge_requests to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/groups/merge_requests/index.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 98142a1b9a6..7ce8856e77e 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'groups:merge_requests': - import('./pages/groups/merge_requests') - .then(callDefault) - .catch(fail); - break; case 'dashboard:todos:index': import('./pages/dashboard/todos/index') .then(callDefault) diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js index f6d284bf9ef..a5cc1f34b63 100644 --- a/app/assets/javascripts/pages/groups/merge_requests/index.js +++ b/app/assets/javascripts/pages/groups/merge_requests/index.js @@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import { FILTERED_SEARCH } from '~/pages/constants'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { initFilteredSearch({ page: FILTERED_SEARCH.MERGE_REQUESTS, }); projectSelect(); -}; +}); -- cgit v1.2.3 From 96feb536e426e9f1f54fa14f50cab9f2a78754be Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:19:07 -0600 Subject: migrate dashboard:todos:index to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/dashboard/todos/index/index.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 7ce8856e77e..bd082bba09f 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'dashboard:todos:index': - import('./pages/dashboard/todos/index') - .then(callDefault) - .catch(fail); - break; case 'admin:jobs:index': import('./pages/admin/jobs/index') .then(callDefault) diff --git a/app/assets/javascripts/pages/dashboard/todos/index/index.js b/app/assets/javascripts/pages/dashboard/todos/index/index.js index 77c23685943..9d2c2f2994f 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/index.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/index.js @@ -1,3 +1,3 @@ import Todos from './todos'; -export default () => new Todos(); +document.addEventListener('DOMContentLoaded', () => new Todos()); -- cgit v1.2.3 From e95d4822960c6541bb429479853b8232aa669335 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:22:26 -0600 Subject: migrate admin:jobs:index to static bundle --- app/assets/javascripts/pages/admin/jobs/index/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js index 31d58eabaaf..5a4f8c6e745 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/index.js +++ b/app/assets/javascripts/pages/admin/jobs/index/index.js @@ -1,12 +1,10 @@ import Vue from 'vue'; - import Translate from '~/vue_shared/translate'; - import stopJobsModal from './components/stop_jobs_modal.vue'; Vue.use(Translate); -export default () => { +document.addEventListener('DOMContentLoaded', () => { const stopJobsButton = document.getElementById('stop-jobs-button'); if (stopJobsButton) { // eslint-disable-next-line no-new @@ -27,4 +25,4 @@ export default () => { }, }); } -}; +}); -- cgit v1.2.3 From c6d6623040e8be6c70e8d4166939143b21217658 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:27:21 -0600 Subject: migrate dashboard:projects:* to static bundle --- app/assets/javascripts/dispatcher.js | 46 ++++++++++------------ .../javascripts/pages/dashboard/projects/index.js | 2 +- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index bd082bba09f..8fac9cc0769 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,32 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'admin:jobs:index': - import('./pages/admin/jobs/index') - .then(callDefault) - .catch(fail); - break; - case 'admin:projects:index': - import('./pages/admin/projects/index/index') - .then(callDefault) - .catch(fail); - break; - case 'admin:users:index': - import('./pages/admin/users/shared') - .then(callDefault) - .catch(fail); - break; - case 'admin:users:show': - import('./pages/admin/users/shared') - .then(callDefault) - .catch(fail); - break; - case 'dashboard:projects:index': - case 'dashboard:projects:starred': - import('./pages/dashboard/projects') - .then(callDefault) - .catch(fail); - break; case 'explore:projects:index': case 'explore:projects:trending': case 'explore:projects:starred': @@ -469,6 +443,26 @@ var Dispatcher; .then(callDefault) .catch(fail); break; + case 'admin:jobs:index': + import('./pages/admin/jobs/index') + .then(callDefault) + .catch(fail); + break; + case 'admin:projects:index': + import('./pages/admin/projects/index/index') + .then(callDefault) + .catch(fail); + break; + case 'admin:users:index': + import('./pages/admin/users/shared') + .then(callDefault) + .catch(fail); + break; + case 'admin:users:show': + import('./pages/admin/users/shared') + .then(callDefault) + .catch(fail); + break; } switch (path[0]) { case 'sessions': diff --git a/app/assets/javascripts/pages/dashboard/projects/index.js b/app/assets/javascripts/pages/dashboard/projects/index.js index c88cbf1a6ba..0c585e162cb 100644 --- a/app/assets/javascripts/pages/dashboard/projects/index.js +++ b/app/assets/javascripts/pages/dashboard/projects/index.js @@ -1,3 +1,3 @@ import ProjectsList from '~/projects_list'; -export default () => new ProjectsList(); +document.addEventListener('DOMContentLoaded', () => new ProjectsList()); -- cgit v1.2.3 From 754dc137f70709eb3d7e6853ffa83f3017e93dbe Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:30:28 -0600 Subject: migrate explore:projects:* to static bundle --- app/assets/javascripts/dispatcher.js | 7 ------- app/assets/javascripts/pages/explore/projects/index.js | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 8fac9cc0769..4b4906bd6ee 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,13 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'explore:projects:index': - case 'explore:projects:trending': - case 'explore:projects:starred': - import('./pages/explore/projects') - .then(callDefault) - .catch(fail); - break; case 'explore:groups:index': import('./pages/explore/groups') .then(callDefault) diff --git a/app/assets/javascripts/pages/explore/projects/index.js b/app/assets/javascripts/pages/explore/projects/index.js index c88cbf1a6ba..0c585e162cb 100644 --- a/app/assets/javascripts/pages/explore/projects/index.js +++ b/app/assets/javascripts/pages/explore/projects/index.js @@ -1,3 +1,3 @@ import ProjectsList from '~/projects_list'; -export default () => new ProjectsList(); +document.addEventListener('DOMContentLoaded', () => new ProjectsList()); -- cgit v1.2.3 From 6e0f420ce95e94b1a2cd509577bf6953a48f9b6f Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:34:35 -0600 Subject: migrate explore:groups:index to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/explore/groups/index.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 4b4906bd6ee..aae499fe688 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,11 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'explore:groups:index': - import('./pages/explore/groups') - .then(callDefault) - .catch(fail); - break; case 'projects:milestones:new': case 'projects:milestones:create': import('./pages/projects/milestones/new') diff --git a/app/assets/javascripts/pages/explore/groups/index.js b/app/assets/javascripts/pages/explore/groups/index.js index e59c38b8bc4..3c7edbdd7c7 100644 --- a/app/assets/javascripts/pages/explore/groups/index.js +++ b/app/assets/javascripts/pages/explore/groups/index.js @@ -2,7 +2,7 @@ import GroupsList from '~/groups_list'; import Landing from '~/landing'; import initGroupsList from '../../../groups'; -export default function () { +document.addEventListener('DOMContentLoaded', () => { new GroupsList(); // eslint-disable-line no-new initGroupsList(); const landingElement = document.querySelector('.js-explore-groups-landing'); @@ -13,4 +13,4 @@ export default function () { 'explore_groups_landing_dismissed', ); exploreGroupsLanding.toggle(); -} +}); -- cgit v1.2.3 From bd36fe31327ea5f075e6b1896d7a634afde61fa6 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:41:04 -0600 Subject: migrate projects:milestones:* and groups:milestones:* to static bundle --- app/assets/javascripts/dispatcher.js | 24 ---------------------- .../pages/groups/milestones/edit/index.js | 2 +- .../pages/groups/milestones/new/index.js | 2 +- .../pages/projects/milestones/edit/index.js | 2 +- .../pages/projects/milestones/new/index.js | 2 +- 5 files changed, 4 insertions(+), 28 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index aae499fe688..156f564d0d0 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,30 +53,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'projects:milestones:new': - case 'projects:milestones:create': - import('./pages/projects/milestones/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:milestones:edit': - case 'projects:milestones:update': - import('./pages/projects/milestones/edit') - .then(callDefault) - .catch(fail); - break; - case 'groups:milestones:new': - case 'groups:milestones:create': - import('./pages/groups/milestones/new') - .then(callDefault) - .catch(fail); - break; - case 'groups:milestones:edit': - case 'groups:milestones:update': - import('./pages/groups/milestones/edit') - .then(callDefault) - .catch(fail); - break; case 'projects:compare:show': import('./pages/projects/compare/show') .then(callDefault) diff --git a/app/assets/javascripts/pages/groups/milestones/edit/index.js b/app/assets/javascripts/pages/groups/milestones/edit/index.js index 5c99c90e24d..ddd10fe5062 100644 --- a/app/assets/javascripts/pages/groups/milestones/edit/index.js +++ b/app/assets/javascripts/pages/groups/milestones/edit/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(false); +document.addEventListener('DOMContentLoaded', () => initForm(false)); diff --git a/app/assets/javascripts/pages/groups/milestones/new/index.js b/app/assets/javascripts/pages/groups/milestones/new/index.js index 5c99c90e24d..ddd10fe5062 100644 --- a/app/assets/javascripts/pages/groups/milestones/new/index.js +++ b/app/assets/javascripts/pages/groups/milestones/new/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(false); +document.addEventListener('DOMContentLoaded', () => initForm(false)); diff --git a/app/assets/javascripts/pages/projects/milestones/edit/index.js b/app/assets/javascripts/pages/projects/milestones/edit/index.js index 10e3979a36e..9a4ebf9890d 100644 --- a/app/assets/javascripts/pages/projects/milestones/edit/index.js +++ b/app/assets/javascripts/pages/projects/milestones/edit/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(); +document.addEventListener('DOMContentLoaded', () => initForm()); diff --git a/app/assets/javascripts/pages/projects/milestones/new/index.js b/app/assets/javascripts/pages/projects/milestones/new/index.js index 10e3979a36e..9a4ebf9890d 100644 --- a/app/assets/javascripts/pages/projects/milestones/new/index.js +++ b/app/assets/javascripts/pages/projects/milestones/new/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(); +document.addEventListener('DOMContentLoaded', () => initForm()); -- cgit v1.2.3 From 92168708a9a2ec44206148586df81ddccc1b2bb6 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:42:55 -0600 Subject: migrate projects:environments:metrics to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/projects/environments/metrics/index.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 156f564d0d0..3084042fda8 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -43,11 +43,6 @@ var Dispatcher; }); switch (page) { - case 'projects:environments:metrics': - import('./pages/projects/environments/metrics') - .then(callDefault) - .catch(fail); - break; case 'projects:merge_requests:index': case 'projects:issues:index': case 'projects:issues:show': diff --git a/app/assets/javascripts/pages/projects/environments/metrics/index.js b/app/assets/javascripts/pages/projects/environments/metrics/index.js index f4760cb2720..0b644780ad4 100644 --- a/app/assets/javascripts/pages/projects/environments/metrics/index.js +++ b/app/assets/javascripts/pages/projects/environments/metrics/index.js @@ -1,3 +1,3 @@ import monitoringBundle from '~/monitoring/monitoring_bundle'; -export default monitoringBundle; +document.addEventListener('DOMContentLoaded', monitoringBundle); -- cgit v1.2.3 From bba4126311257628bb136de6b7a1d83a262bbd8e Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:46:54 -0600 Subject: migrate projects:compare:show to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/projects/compare/show/index.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 3084042fda8..6cc71e03855 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -48,11 +48,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'projects:compare:show': - import('./pages/projects/compare/show') - .then(callDefault) - .catch(fail); - break; case 'projects:branches:new': import('./pages/projects/branches/new') .then(callDefault) diff --git a/app/assets/javascripts/pages/projects/compare/show/index.js b/app/assets/javascripts/pages/projects/compare/show/index.js index 6b8d4503568..2b4fd3c47c0 100644 --- a/app/assets/javascripts/pages/projects/compare/show/index.js +++ b/app/assets/javascripts/pages/projects/compare/show/index.js @@ -1,8 +1,8 @@ import Diff from '~/diff'; import initChangesDropdown from '~/init_changes_dropdown'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Diff(); // eslint-disable-line no-new const paddingTop = 16; initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop); -}; +}); -- cgit v1.2.3 From 8c92330c5074ad2cc723a9bbd56d80d44b52afd9 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:48:17 -0600 Subject: migrate projects:branches:new to static bundle --- app/assets/javascripts/dispatcher.js | 10 ---------- app/assets/javascripts/pages/projects/branches/new/index.js | 4 +++- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 6cc71e03855..1566084f2d8 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -48,16 +48,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'projects:branches:new': - import('./pages/projects/branches/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:branches:create': - import('./pages/projects/branches/new') - .then(callDefault) - .catch(fail); - break; case 'projects:branches:index': import('./pages/projects/branches/index') .then(callDefault) diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js index ae5e033e97e..d32d5c6cb29 100644 --- a/app/assets/javascripts/pages/projects/branches/new/index.js +++ b/app/assets/javascripts/pages/projects/branches/new/index.js @@ -1,3 +1,5 @@ import NewBranchForm from '~/new_branch_form'; -export default () => new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); +document.addEventListener('DOMContentLoaded', () => ( + new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)) +)); -- cgit v1.2.3 From be6bd83b4cb001ebe59c2b62f59faacc585e4fc7 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:50:24 -0600 Subject: migrate projects:branches:index to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/projects/branches/index/index.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 1566084f2d8..c239fbd1a43 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -48,11 +48,6 @@ var Dispatcher; case 'projects:issues:show': shortcut_handler = true; break; - case 'projects:branches:index': - import('./pages/projects/branches/index') - .then(callDefault) - .catch(fail); - break; case 'projects:issues:new': import('./pages/projects/issues/new') .then(callDefault) diff --git a/app/assets/javascripts/pages/projects/branches/index/index.js b/app/assets/javascripts/pages/projects/branches/index/index.js index cee0f19bf2a..8fa266a37ce 100644 --- a/app/assets/javascripts/pages/projects/branches/index/index.js +++ b/app/assets/javascripts/pages/projects/branches/index/index.js @@ -1,7 +1,7 @@ import AjaxLoadingSpinner from '~/ajax_loading_spinner'; import DeleteModal from '~/branches/branches_delete_modal'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { AjaxLoadingSpinner.init(); new DeleteModal(); // eslint-disable-line no-new -}; +}); -- cgit v1.2.3 From a6af8e8e643a2779f54c0a07b1d79d86221aacdc Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:55:18 -0600 Subject: migrate projects:issues:new and projects:issues:edit to static bundle --- app/assets/javascripts/dispatcher.js | 10 ---------- app/assets/javascripts/pages/projects/issues/edit/index.js | 4 +--- app/assets/javascripts/pages/projects/issues/new/index.js | 4 +--- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index c239fbd1a43..4c79c3c4948 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -46,18 +46,8 @@ var Dispatcher; case 'projects:merge_requests:index': case 'projects:issues:index': case 'projects:issues:show': - shortcut_handler = true; - break; case 'projects:issues:new': - import('./pages/projects/issues/new') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:issues:edit': - import('./pages/projects/issues/edit') - .then(callDefault) - .catch(fail); shortcut_handler = true; break; case 'projects:merge_requests:creations:new': diff --git a/app/assets/javascripts/pages/projects/issues/edit/index.js b/app/assets/javascripts/pages/projects/issues/edit/index.js index 7f27f379d8c..ffc84dc106b 100644 --- a/app/assets/javascripts/pages/projects/issues/edit/index.js +++ b/app/assets/javascripts/pages/projects/issues/edit/index.js @@ -1,5 +1,3 @@ import initForm from '../form'; -export default () => { - initForm(); -}; +document.addEventListener('DOMContentLoaded', initForm); diff --git a/app/assets/javascripts/pages/projects/issues/new/index.js b/app/assets/javascripts/pages/projects/issues/new/index.js index 7f27f379d8c..ffc84dc106b 100644 --- a/app/assets/javascripts/pages/projects/issues/new/index.js +++ b/app/assets/javascripts/pages/projects/issues/new/index.js @@ -1,5 +1,3 @@ import initForm from '../form'; -export default () => { - initForm(); -}; +document.addEventListener('DOMContentLoaded', initForm); -- cgit v1.2.3 From dc65609e726b78098e82becdd6224fc87a37130d Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:57:10 -0600 Subject: migrate projects:merge_requests:creations:new to static bundle --- app/assets/javascripts/dispatcher.js | 4 ---- .../javascripts/pages/projects/merge_requests/creations/new/index.js | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 4c79c3c4948..2e57a2d2c22 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -50,10 +50,6 @@ var Dispatcher; case 'projects:issues:edit': shortcut_handler = true; break; - case 'projects:merge_requests:creations:new': - import('./pages/projects/merge_requests/creations/new') - .then(callDefault) - .catch(fail); case 'projects:merge_requests:creations:diffs': import('./pages/projects/merge_requests/creations/diffs') .then(callDefault) diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js index ccd0b54c5ed..1d5aec4001d 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js @@ -1,7 +1,7 @@ import Compare from '~/compare'; import MergeRequest from '~/merge_request'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); if (mrNewCompareNode) { new Compare({ // eslint-disable-line no-new @@ -15,4 +15,4 @@ export default () => { action: mrNewSubmitNode.dataset.mrSubmitAction, }); } -}; +}); -- cgit v1.2.3 From a9f320b5a2acd092c7b4523ff8ab7160c337db20 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 16:59:36 -0600 Subject: migrate projects:merge_requests:creations:diffs to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- .../pages/projects/merge_requests/creations/diffs/index.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 2e57a2d2c22..04c5d220df2 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -48,12 +48,7 @@ var Dispatcher; case 'projects:issues:show': case 'projects:issues:new': case 'projects:issues:edit': - shortcut_handler = true; - break; case 'projects:merge_requests:creations:diffs': - import('./pages/projects/merge_requests/creations/diffs') - .then(callDefault) - .catch(fail); shortcut_handler = true; break; case 'projects:merge_requests:edit': diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js index 734d01ae6f2..febfecebbd2 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js @@ -1,3 +1,3 @@ import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; -export default initMergeRequest; +document.addEventListener('DOMContentLoaded', initMergeRequest); -- cgit v1.2.3 From 71ac339fcaee5611506ea250bf501ecb49d202b0 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 17:06:10 -0600 Subject: migrate projects:merge_requests:edit to static bundle --- app/assets/javascripts/dispatcher.js | 5 ----- app/assets/javascripts/pages/projects/merge_requests/edit/index.js | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 04c5d220df2..089ffa77677 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -49,12 +49,7 @@ var Dispatcher; case 'projects:issues:new': case 'projects:issues:edit': case 'projects:merge_requests:creations:diffs': - shortcut_handler = true; - break; case 'projects:merge_requests:edit': - import('./pages/projects/merge_requests/edit') - .then(callDefault) - .catch(fail); shortcut_handler = true; break; case 'projects:tags:new': diff --git a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js index 734d01ae6f2..febfecebbd2 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js @@ -1,3 +1,3 @@ import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; -export default initMergeRequest; +document.addEventListener('DOMContentLoaded', initMergeRequest); -- cgit v1.2.3 From fab0f24c67833a6640830febdd1b705395a13484 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 2 Feb 2018 18:14:24 -0600 Subject: alias create and update actions to new and edit --- app/helpers/webpack_helper.rb | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb index 9d071f2d59a..8bcced70d63 100644 --- a/app/helpers/webpack_helper.rb +++ b/app/helpers/webpack_helper.rb @@ -7,17 +7,24 @@ module WebpackHelper def webpack_controller_bundle_tags bundles = [] - segments = [*controller.controller_path.split('/'), controller.action_name].compact - until segments.empty? + action = case controller.action_name + when 'create' then 'new' + when 'update' then 'edit' + else controller.action_name + end + + route = [*controller.controller_path.split('/'), action].compact + + until route.empty? begin - asset_paths = gitlab_webpack_asset_paths("pages.#{segments.join('.')}", extension: 'js') + asset_paths = gitlab_webpack_asset_paths("pages.#{route.join('.')}", extension: 'js') bundles.unshift(*asset_paths) rescue Webpack::Rails::Manifest::EntryPointMissingError # no bundle exists for this path end - segments.pop + route.pop end javascript_include_tag(*bundles) -- cgit v1.2.3 From dc9b2a8fb2a282aefa946246b0fbaf15562fb4a5 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 16 Feb 2018 13:01:07 -0600 Subject: correct for missing break statement in dispatcher.js --- app/assets/javascripts/dispatcher.js | 1 + .../javascripts/pages/projects/merge_requests/creations/diffs/index.js | 3 --- .../javascripts/pages/projects/merge_requests/creations/index.js | 3 +++ 3 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js create mode 100644 app/assets/javascripts/pages/projects/merge_requests/creations/index.js diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 089ffa77677..b6754689466 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -48,6 +48,7 @@ var Dispatcher; case 'projects:issues:show': case 'projects:issues:new': case 'projects:issues:edit': + case 'projects:merge_requests:creations:new': case 'projects:merge_requests:creations:diffs': case 'projects:merge_requests:edit': shortcut_handler = true; diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js deleted file mode 100644 index febfecebbd2..00000000000 --- a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; - -document.addEventListener('DOMContentLoaded', initMergeRequest); diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/index.js new file mode 100644 index 00000000000..febfecebbd2 --- /dev/null +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/index.js @@ -0,0 +1,3 @@ +import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; + +document.addEventListener('DOMContentLoaded', initMergeRequest); -- cgit v1.2.3 From b63674e2c677bdbfc40b89d06af86771a807f6d1 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Fri, 16 Feb 2018 13:08:05 -0600 Subject: migrate admin:users:* to static bundle --- app/assets/javascripts/dispatcher.js | 20 --- .../pages/admin/projects/index/index.js | 4 +- .../admin/users/components/delete_user_modal.vue | 174 +++++++++++++++++++++ app/assets/javascripts/pages/admin/users/index.js | 43 +++++ .../users/shared/components/delete_user_modal.vue | 174 --------------------- .../javascripts/pages/admin/users/shared/index.js | 43 ----- 6 files changed, 219 insertions(+), 239 deletions(-) create mode 100644 app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue create mode 100644 app/assets/javascripts/pages/admin/users/index.js delete mode 100644 app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue delete mode 100644 app/assets/javascripts/pages/admin/users/shared/index.js diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index b6754689466..8467375f81a 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -359,26 +359,6 @@ var Dispatcher; .then(callDefault) .catch(fail); break; - case 'admin:jobs:index': - import('./pages/admin/jobs/index') - .then(callDefault) - .catch(fail); - break; - case 'admin:projects:index': - import('./pages/admin/projects/index/index') - .then(callDefault) - .catch(fail); - break; - case 'admin:users:index': - import('./pages/admin/users/shared') - .then(callDefault) - .catch(fail); - break; - case 'admin:users:show': - import('./pages/admin/users/shared') - .then(callDefault) - .catch(fail); - break; } switch (path[0]) { case 'sessions': diff --git a/app/assets/javascripts/pages/admin/projects/index/index.js b/app/assets/javascripts/pages/admin/projects/index/index.js index a87b27090a8..3c597a1093e 100644 --- a/app/assets/javascripts/pages/admin/projects/index/index.js +++ b/app/assets/javascripts/pages/admin/projects/index/index.js @@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf'; import deleteProjectModal from './components/delete_project_modal.vue'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { Vue.use(Translate); const deleteProjectModalEl = document.getElementById('delete-project-modal'); @@ -34,4 +34,4 @@ export default () => { deleteModal.projectName = buttonProps.projectName; } }); -}; +}); diff --git a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue new file mode 100644 index 00000000000..7b5e333011e --- /dev/null +++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue @@ -0,0 +1,174 @@ + + + diff --git a/app/assets/javascripts/pages/admin/users/index.js b/app/assets/javascripts/pages/admin/users/index.js new file mode 100644 index 00000000000..4f5d6b55031 --- /dev/null +++ b/app/assets/javascripts/pages/admin/users/index.js @@ -0,0 +1,43 @@ +import Vue from 'vue'; + +import Translate from '~/vue_shared/translate'; +import csrf from '~/lib/utils/csrf'; + +import deleteUserModal from './components/delete_user_modal.vue'; + +document.addEventListener('DOMContentLoaded', () => { + Vue.use(Translate); + + const deleteUserModalEl = document.getElementById('delete-user-modal'); + + const deleteModal = new Vue({ + el: deleteUserModalEl, + data: { + deleteUserUrl: '', + blockUserUrl: '', + deleteContributions: '', + username: '', + }, + render(createElement) { + return createElement(deleteUserModal, { + props: { + deleteUserUrl: this.deleteUserUrl, + blockUserUrl: this.blockUserUrl, + deleteContributions: this.deleteContributions, + username: this.username, + csrfToken: csrf.token, + }, + }); + }, + }); + + $(document).on('shown.bs.modal', (event) => { + if (event.relatedTarget.classList.contains('delete-user-button')) { + const buttonProps = event.relatedTarget.dataset; + deleteModal.deleteUserUrl = buttonProps.deleteUserUrl; + deleteModal.blockUserUrl = buttonProps.blockUserUrl; + deleteModal.deleteContributions = event.relatedTarget.hasAttribute('data-delete-contributions'); + deleteModal.username = buttonProps.username; + } + }); +}); diff --git a/app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue deleted file mode 100644 index 7b5e333011e..00000000000 --- a/app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue +++ /dev/null @@ -1,174 +0,0 @@ - - - diff --git a/app/assets/javascripts/pages/admin/users/shared/index.js b/app/assets/javascripts/pages/admin/users/shared/index.js deleted file mode 100644 index d2a0f82fa2b..00000000000 --- a/app/assets/javascripts/pages/admin/users/shared/index.js +++ /dev/null @@ -1,43 +0,0 @@ -import Vue from 'vue'; - -import Translate from '~/vue_shared/translate'; -import csrf from '~/lib/utils/csrf'; - -import deleteUserModal from './components/delete_user_modal.vue'; - -export default () => { - Vue.use(Translate); - - const deleteUserModalEl = document.getElementById('delete-user-modal'); - - const deleteModal = new Vue({ - el: deleteUserModalEl, - data: { - deleteUserUrl: '', - blockUserUrl: '', - deleteContributions: '', - username: '', - }, - render(createElement) { - return createElement(deleteUserModal, { - props: { - deleteUserUrl: this.deleteUserUrl, - blockUserUrl: this.blockUserUrl, - deleteContributions: this.deleteContributions, - username: this.username, - csrfToken: csrf.token, - }, - }); - }, - }); - - $(document).on('shown.bs.modal', (event) => { - if (event.relatedTarget.classList.contains('delete-user-button')) { - const buttonProps = event.relatedTarget.dataset; - deleteModal.deleteUserUrl = buttonProps.deleteUserUrl; - deleteModal.blockUserUrl = buttonProps.blockUserUrl; - deleteModal.deleteContributions = event.relatedTarget.hasAttribute('data-delete-contributions'); - deleteModal.username = buttonProps.username; - } - }); -}; -- cgit v1.2.3