summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-11-15 14:19:49 +0000
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-11-15 14:19:49 +0000
commit875728b1184d4524bf96a6de47b329eba7645446 (patch)
tree9dddb459c3f43bbbd49102ab132cc8ccec999cab
parent77e3fab89f633b70a0f805cce94d09304ad893e2 (diff)
parent27ad882693a1848b17c3841fec72f21a3b9428ee (diff)
downloadgitlab-ce-875728b1184d4524bf96a6de47b329eba7645446.tar.gz
Merge branch 'feature/admin_project_transfer' of /home/git/repositories/gitlab/gitlabhq
-rw-r--r--CHANGELOG1
-rw-r--r--app/assets/javascripts/api.js.coffee15
-rw-r--r--app/assets/javascripts/namespace_select.js.coffee24
-rw-r--r--app/assets/stylesheets/common.scss30
-rw-r--r--app/assets/stylesheets/selects.scss19
-rw-r--r--app/controllers/admin/projects_controller.rb24
-rw-r--r--app/helpers/namespaces_helper.rb9
-rw-r--r--app/models/namespace.rb4
-rw-r--r--app/views/admin/projects/show.html.haml17
-rw-r--r--config/routes.rb8
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/entities.rb4
-rw-r--r--lib/api/namespaces.rb23
-rw-r--r--spec/requests/api/namespaces_spec.rb31
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