diff options
author | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2013-11-15 14:19:49 +0000 |
---|---|---|
committer | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2013-11-15 14:19:49 +0000 |
commit | 875728b1184d4524bf96a6de47b329eba7645446 (patch) | |
tree | 9dddb459c3f43bbbd49102ab132cc8ccec999cab | |
parent | 77e3fab89f633b70a0f805cce94d09304ad893e2 (diff) | |
parent | 27ad882693a1848b17c3841fec72f21a3b9428ee (diff) | |
download | gitlab-ce-875728b1184d4524bf96a6de47b329eba7645446.tar.gz |
Merge branch 'feature/admin_project_transfer' of /home/git/repositories/gitlab/gitlabhq
-rw-r--r-- | CHANGELOG | 1 | ||||
-rw-r--r-- | app/assets/javascripts/api.js.coffee | 15 | ||||
-rw-r--r-- | app/assets/javascripts/namespace_select.js.coffee | 24 | ||||
-rw-r--r-- | app/assets/stylesheets/common.scss | 30 | ||||
-rw-r--r-- | app/assets/stylesheets/selects.scss | 19 | ||||
-rw-r--r-- | app/controllers/admin/projects_controller.rb | 24 | ||||
-rw-r--r-- | app/helpers/namespaces_helper.rb | 9 | ||||
-rw-r--r-- | app/models/namespace.rb | 4 | ||||
-rw-r--r-- | app/views/admin/projects/show.html.haml | 17 | ||||
-rw-r--r-- | config/routes.rb | 8 | ||||
-rw-r--r-- | lib/api/api.rb | 1 | ||||
-rw-r--r-- | lib/api/entities.rb | 4 | ||||
-rw-r--r-- | lib/api/namespaces.rb | 23 | ||||
-rw-r--r-- | spec/requests/api/namespaces_spec.rb | 31 |
14 files changed, 187 insertions, 23 deletions
diff --git a/CHANGELOG b/CHANGELOG index 90dff97736d..baddbbf747b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 6.3.0 - Fix 500 error for repos with newline in file name - Extended html titles - API: create/update repo files + - Admin can transfer project to any namespace v 6.2.0 - Public project pages are now visible to everyone (files, issues, wik, etc.) diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee index a36d944cbcb..5f4a38ebbd0 100644 --- a/app/assets/javascripts/api.js.coffee +++ b/app/assets/javascripts/api.js.coffee @@ -2,6 +2,7 @@ users_path: "/api/:version/users.json" user_path: "/api/:version/users/:id.json" notes_path: "/api/:version/projects/:id/notes.json" + namespaces_path: "/api/:version/namespaces.json" # Get 20 (depends on api) recent notes # and sort the ascending from oldest to newest @@ -49,6 +50,20 @@ ).done (users) -> callback(users) + # Return namespaces list. Filtered by query + namespaces: (query, callback) -> + url = Api.buildUrl(Api.namespaces_path) + + $.ajax( + url: url + data: + private_token: gon.api_token + search: query + per_page: 20 + dataType: "json" + ).done (namespaces) -> + callback(namespaces) + buildUrl: (url) -> url = gon.relative_url_root + url if gon.relative_url_root? return url.replace(':version', gon.api_version) diff --git a/app/assets/javascripts/namespace_select.js.coffee b/app/assets/javascripts/namespace_select.js.coffee new file mode 100644 index 00000000000..00d135d1449 --- /dev/null +++ b/app/assets/javascripts/namespace_select.js.coffee @@ -0,0 +1,24 @@ +$ -> + namespaceFormatResult = (namespace) -> + markup = "<div class='namespace-result'>" + markup += "<span class='namespace-kind'>" + namespace.kind + "</span>" + markup += "<span class='namespace-path'>" + namespace.path + "</span>" + markup += "</div>" + markup + + formatSelection = (namespace) -> + namespace.kind + ": " + namespace.path + + $('.ajax-namespace-select').each (i, select) -> + $(select).select2 + placeholder: "Search for namespace" + multiple: $(select).hasClass('multiselect') + minimumInputLength: 0 + query: (query) -> + Api.namespaces query.term, (namespaces) -> + data = { results: namespaces } + query.callback(data) + + dropdownCssClass: "ajax-namespace-dropdown" + formatResult: namespaceFormatResult + formatSelection: formatSelection diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 56dbd9ac433..935d24e58ff 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -358,3 +358,33 @@ table { background: #555; color: #BBB; } + +.ajax-users-select { + width: 400px; + + &.input-large { + width: 210px; + } +} + +.user-result { + .user-image { + float: left; + } + .user-name { + } + .user-username { + color: #999; + } +} + +.namespace-result { + .namespace-kind { + color: #AAA; + font-weight: normal; + } + .namespace-path { + margin-left: 10px; + font-weight: bolder; + } +} diff --git a/app/assets/stylesheets/selects.scss b/app/assets/stylesheets/selects.scss index 3b61594e668..8a695d8b16d 100644 --- a/app/assets/stylesheets/selects.scss +++ b/app/assets/stylesheets/selects.scss @@ -1,22 +1,3 @@ -.ajax-users-select { - width: 400px; - - &.input-large { - width: 210px; - } -} - -.user-result { - .user-image { - float: left; - } - .user-name { - } - .user-username { - color: #999; - } -} - /** Chosen.js selectbox style override **/ .chosen-container { min-width: 100px; diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 088174fd3b8..4de0a65b5fe 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -1,5 +1,7 @@ class Admin::ProjectsController < Admin::ApplicationController - before_filter :project, only: [:edit, :show, :update, :destroy, :team_update] + before_filter :project, only: [:show, :transfer] + before_filter :group, only: [:show, :transfer] + before_filter :repository, only: [:show, :transfer] def index owner_id = params[:owner_id] @@ -14,8 +16,16 @@ class Admin::ProjectsController < Admin::ApplicationController end def show - @repository = @project.repository - @group = @project.group + end + + def transfer + result = ::Projects::TransferContext.new(@project, current_user, project: params).execute(:admin) + + if result + redirect_to [:admin, @project] + else + render :show + end end protected @@ -26,4 +36,12 @@ class Admin::ProjectsController < Admin::ApplicationController @project = Project.find_with_namespace(id) @project || render_404 end + + def group + @group ||= project.group + end + + def repository + @repository ||= project.repository + end end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index f7979c8b641..c363c7ffd74 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -16,4 +16,13 @@ module NamespacesHelper grouped_options_for_select(options, selected) end + + def namespace_select_tag(id, opts = {}) + css_class = "ajax-namespace-select " + css_class << "multiselect " if opts[:multiple] + css_class << (opts[:class] || '') + value = opts[:selected] || '' + + hidden_field_tag(id, value, class: css_class) + end end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index fde06649c78..8f837c72ff5 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -87,4 +87,8 @@ class Namespace < ActiveRecord::Base def send_update_instructions projects.each(&:send_move_instructions) end + + def kind + type == 'Group' ? 'group' : 'user' + end end diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 65b9911dc46..c9c9c38d9f5 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -74,6 +74,23 @@ %span.cgreen %i.icon-lock Private + .ui-box + .title + Transfer project + .ui-box-body + = form_for @project, url: transfer_admin_project_path(@project), method: :put do |f| + .control-group + = f.label :namespace_id, "Namespace" + .controls + = namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large' + + .control-group + .controls + = f.submit 'Transfer', class: 'btn btn-primary' + + + + .span6 - if @group .ui-box diff --git a/config/routes.rb b/config/routes.rb index 3b69239087c..06e8fddf705 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -89,7 +89,13 @@ Gitlab::Application.routes.draw do resources :broadcast_messages, only: [:index, :create, :destroy] resource :logs, only: [:show] resource :background_jobs, controller: 'background_jobs', only: [:show] - resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] + + resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do + member do + put :transfer + end + end + root to: "dashboard#index" end diff --git a/lib/api/api.rb b/lib/api/api.rb index 45efcc8cd47..283f7642f67 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -40,5 +40,6 @@ module API mount ProjectHooks mount Services mount Files + mount Namespaces end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 429083d75be..2bdcbdc8c7f 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -136,5 +136,9 @@ module API expose :target_id, :target_type, :author_id expose :data, :target_title end + + class Namespace < Grape::Entity + expose :id, :path, :kind + end end end diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb new file mode 100644 index 00000000000..3a9ab66957e --- /dev/null +++ b/lib/api/namespaces.rb @@ -0,0 +1,23 @@ +module API + # namespaces API + class Namespaces < Grape::API + before { + authenticate! + authenticated_as_admin! + } + + resource :namespaces do + # Get a namespaces list + # + # Example Request: + # GET /namespaces + get do + @namespaces = Namespace.scoped + @namespaces = @namespaces.search(params[:search]) if params[:search].present? + @namespaces = paginate @namespaces + + present @namespaces, with: Entities::Namespace + end + end + end +end diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb new file mode 100644 index 00000000000..2b1a4bf6ec8 --- /dev/null +++ b/spec/requests/api/namespaces_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + before(:each) { ActiveRecord::Base.observers.enable(:user_observer) } + after(:each) { ActiveRecord::Base.observers.disable(:user_observer) } + + let(:admin) { create(:admin) } + let!(:group1) { create(:group) } + let!(:group2) { create(:group) } + + describe "GET /namespaces" do + context "when unauthenticated" do + it "should return authentication error" do + get api("/namespaces") + response.status.should == 401 + end + end + + context "when authenticated as admin" do + it "admin: should return an array of all namespaces" do + get api("/namespaces", admin) + response.status.should == 200 + json_response.should be_an Array + + # Admin namespace + 2 group namespaces + json_response.length.should == 3 + end + end + end +end |