From 6676b4f0dd05ffb6071a3329cca4459e7cddcdc3 Mon Sep 17 00:00:00 2001
From: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Date: Tue, 14 Feb 2017 16:03:24 +0200
Subject: [PATCH] Use Namespace#full_path instead of Namespace#path

Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
---
 app/helpers/namespaces_helper.rb              |  2 +-
 app/helpers/submodule_helper.rb               |  2 +-
 app/models/merge_request.rb                   |  4 +-
 app/models/project.rb                         | 28 +++++++------
 .../project_services/drone_ci_service.rb      |  6 +--
 app/services/projects/transfer_service.rb     |  6 +--
 app/views/admin/dashboard/index.html.haml     |  2 +-
 app/views/import/base/unauthorized.js.haml    |  2 +-
 app/views/projects/edit.html.haml             |  2 +-
 .../projects/group_links/_index.html.haml     |  2 +-
 .../projects/pages_domains/show.html.haml     |  2 +-
 app/views/shared/members/_group.html.haml     |  2 +-
 doc/api/projects.md                           | 27 ++++++++-----
 lib/backup/repository.rb                      |  2 +-
 lib/gitlab/google_code_import/importer.rb     |  2 +-
 lib/gitlab/import_export.rb                   |  2 +-
 lib/gitlab/import_export/importer.rb          |  2 +-
 lib/gitlab/shell.rb                           |  2 +-
 .../import_export/import_export_spec.rb       |  5 ++-
 spec/models/project_spec.rb                   | 39 ++++++++++++++-----
 spec/requests/api/projects_spec.rb            |  1 +
 spec/requests/api/v3/projects_spec.rb         |  1 +
 22 files changed, 90 insertions(+), 53 deletions(-)

diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index e0b8dc1393b..0676767d910 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -10,7 +10,7 @@ module NamespacesHelper
     data_attr_users = { 'data-options-parent' => 'users' }
 
     group_opts = [
-      "Groups", groups.sort_by(&:human_name).map { |g| [display_path ? g.path : g.human_name, g.id, data_attr_group] }
+      "Groups", groups.sort_by(&:human_name).map { |g| [display_path ? g.full_path : g.human_name, g.id, data_attr_group] }
     ]
 
     users_opts = [
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index b3f50ceebe4..9a748aaaf33 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -63,7 +63,7 @@ module SubmoduleHelper
     namespace = components.pop.gsub(/^\.\.$/, '')
 
     if namespace.empty?
-      namespace = @project.namespace.path
+      namespace = @project.namespace.full_path
     end
 
     [
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 38646eba3ac..204d2b153ad 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -598,7 +598,7 @@ class MergeRequest < ActiveRecord::Base
 
   def source_project_namespace
     if source_project && source_project.namespace
-      source_project.namespace.path
+      source_project.namespace.full_path
     else
       "(removed)"
     end
@@ -606,7 +606,7 @@ class MergeRequest < ActiveRecord::Base
 
   def target_project_namespace
     if target_project && target_project.namespace
-      target_project.namespace.path
+      target_project.namespace.full_path
     else
       "(removed)"
     end
diff --git a/app/models/project.rb b/app/models/project.rb
index aa408b4556e..ed43fc2e575 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -454,7 +454,7 @@ class Project < ActiveRecord::Base
     if forked?
       job_id = RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path,
                                                   forked_from_project.path_with_namespace,
-                                                  self.namespace.path)
+                                                  self.namespace.full_path)
     else
       job_id = RepositoryImportWorker.perform_async(self.id)
     end
@@ -942,8 +942,8 @@ class Project < ActiveRecord::Base
 
     Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
 
-    Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path)
-    Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.path)
+    Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path)
+    Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path)
   end
 
   # Expires various caches before a project is renamed.
@@ -1150,19 +1150,25 @@ class Project < ActiveRecord::Base
   end
 
   def pages_url
+    subdomain, _, url_path = full_path.partition('/')
+
     # The hostname always needs to be in downcased
     # All web servers convert hostname to lowercase
-    host = "#{namespace.path}.#{Settings.pages.host}".downcase
+    host = "#{subdomain}.#{Settings.pages.host}".downcase
 
     # The host in URL always needs to be downcased
     url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix|
-      "#{prefix}#{namespace.path}."
+      "#{prefix}#{subdomain}."
     end.downcase
 
     # If the project path is the same as host, we serve it as group page
-    return url if host == path
+    return url if host == url_path
+
+    "#{url}/#{url_path}"
+  end
 
-    "#{url}/#{path}"
+  def pages_subdomain
+    full_path.partition('/').first
   end
 
   def pages_path
@@ -1179,8 +1185,8 @@ class Project < ActiveRecord::Base
     # 3. We asynchronously remove pages with force
     temp_path = "#{path}.#{SecureRandom.hex}.deleted"
 
-    if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.path)
-      PagesWorker.perform_in(5.minutes, :remove, namespace.path, temp_path)
+    if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.full_path)
+      PagesWorker.perform_in(5.minutes, :remove, namespace.full_path, temp_path)
     end
   end
 
@@ -1230,7 +1236,7 @@ class Project < ActiveRecord::Base
   end
 
   def ensure_dir_exist
-    gitlab_shell.add_namespace(repository_storage_path, namespace.path)
+    gitlab_shell.add_namespace(repository_storage_path, namespace.full_path)
   end
 
   def predefined_variables
@@ -1238,7 +1244,7 @@ class Project < ActiveRecord::Base
       { key: 'CI_PROJECT_ID', value: id.to_s, public: true },
       { key: 'CI_PROJECT_NAME', value: path, public: true },
       { key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true },
-      { key: 'CI_PROJECT_NAMESPACE', value: namespace.path, public: true },
+      { key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true },
       { key: 'CI_PROJECT_URL', value: web_url, public: true }
     ]
   end
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index 0a217d8caba..942ec9371e5 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -12,7 +12,7 @@ class DroneCiService < CiService
   def compose_service_hook
     hook = service_hook || build_service_hook
     # If using a service template, project may not be available
-    hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project
+    hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.full_path}", "&name=#{project.path}", "&access_token=#{token}"].join if project
     hook.enable_ssl_verification = !!enable_ssl_verification
     hook.save
   end
@@ -38,7 +38,7 @@ class DroneCiService < CiService
 
   def commit_status_path(sha, ref)
     url = [drone_url,
-           "gitlab/#{project.namespace.path}/#{project.path}/commits/#{sha}",
+           "gitlab/#{project.full_path}/commits/#{sha}",
            "?branch=#{URI::encode(ref.to_s)}&access_token=#{token}"]
 
     URI.join(*url).to_s
@@ -73,7 +73,7 @@ class DroneCiService < CiService
 
   def build_page(sha, ref)
     url = [drone_url,
-           "gitlab/#{project.namespace.path}/#{project.path}/redirect/commits/#{sha}",
+           "gitlab/#{project.full_path}/redirect/commits/#{sha}",
            "?branch=#{URI::encode(ref.to_s)}"]
 
     URI.join(*url).to_s
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 484700c8c29..20dfbddc823 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -30,7 +30,7 @@ module Projects
       Project.transaction do
         old_path = project.path_with_namespace
         old_group = project.group
-        new_path = File.join(new_namespace.try(:path) || '', project.path)
+        new_path = File.join(new_namespace.try(:full_path) || '', project.path)
 
         if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
           raise TransferError.new("Project with same path in target namespace already exists")
@@ -63,10 +63,10 @@ module Projects
         Labels::TransferService.new(current_user, old_group, project).execute
 
         # Move uploads
-        Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
+        Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.full_path, new_namespace.full_path)
 
         # Move pages
-        Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
+        Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.full_path, new_namespace.full_path)
 
         project.old_path_with_namespace = old_path
 
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 5238623e936..e67ad663720 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -163,6 +163,6 @@
         - @groups.each do |group|
           %p
             = link_to [:admin, group], class: 'str-truncated-60' do
-              = group.name
+              = group.full_name
             %span.light.pull-right
               #{time_ago_with_tooltip(group.created_at)}
diff --git a/app/views/import/base/unauthorized.js.haml b/app/views/import/base/unauthorized.js.haml
index 36f8069c1f7..ada5f99f4e2 100644
--- a/app/views/import/base/unauthorized.js.haml
+++ b/app/views/import/base/unauthorized.js.haml
@@ -4,7 +4,7 @@
   import_button = tr.find(".btn-import")
   origin_target = target_field.text()
   project_name = "#{@project_name}"
-  origin_namespace = "#{@target_namespace.path}"
+  origin_namespace = "#{@target_namespace.full_path}"
   target_field.empty()
   target_field.append("<p class='alert alert-danger'>This namespace has already been taken! Please choose another one.</p>")
   target_field.append("<input type='text' name='target_namespace' />")
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 9c5c1a6d707..f2905fa3ce9 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -232,7 +232,7 @@
           .form-group
             .input-group
               .input-group-addon
-                #{URI.join(root_url, @project.namespace.path)}/
+                #{URI.join(root_url, @project.namespace.full_path)}/
               = f.text_field :path, class: 'form-control'
           %ul
             %li Be careful. Renaming a project's repository can have unintended side effects.
diff --git a/app/views/projects/group_links/_index.html.haml b/app/views/projects/group_links/_index.html.haml
index 99d0df2ac34..b6116dbec41 100644
--- a/app/views/projects/group_links/_index.html.haml
+++ b/app/views/projects/group_links/_index.html.haml
@@ -39,7 +39,7 @@
               = icon("folder-open-o", class: "settings-list-icon")
             .pull-left
               = link_to group do
-                = group.name
+                = group.full_name
               %br
               up to #{group_link.human_access}
               - if group_link.expires?
diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml
index 52dddb052a7..876cac0dacb 100644
--- a/app/views/projects/pages_domains/show.html.haml
+++ b/app/views/projects/pages_domains/show.html.haml
@@ -17,7 +17,7 @@
         %p
           To access the domain create a new DNS record:
         %pre
-          #{@domain.domain} CNAME #{@domain.project.namespace.path}.#{Settings.pages.host}.
+          #{@domain.domain} CNAME #{@domain.project.pages_subdomain}.#{Settings.pages.host}.
     %tr
       %td
         Certificate
diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml
index 81b5bc1de30..1d5a61cffce 100644
--- a/app/views/shared/members/_group.html.haml
+++ b/app/views/shared/members/_group.html.haml
@@ -6,7 +6,7 @@
   %span.list-item-name
     = image_tag group_icon(group), class: "avatar s40", alt: ''
     %strong
-      = link_to group.name, group_path(group)
+      = link_to group.full_name, group_path(group)
     .cgray
       Joined #{time_ago_with_tooltip(group.created_at)}
       - if group_link.expires?
diff --git a/doc/api/projects.md b/doc/api/projects.md
index bad238f57d7..f4679d29a4b 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -75,7 +75,8 @@ Parameters:
       "id": 3,
       "name": "Diaspora",
       "path": "diaspora",
-      "kind": "group"
+      "kind": "group",
+      "full_path": "diaspora"
     },
     "archived": false,
     "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",
@@ -125,7 +126,8 @@ Parameters:
       "id": 4,
       "name": "Brightbox",
       "path": "brightbox",
-      "kind": "group"
+      "kind": "group",
+      "full_path": "brightbox"
     },
     "permissions": {
       "project_access": {
@@ -207,7 +209,8 @@ Parameters:
       "id": 3,
       "name": "Diaspora",
       "path": "diaspora",
-      "kind": "group"
+      "kind": "group",
+      "full_path": "diaspora"
     },
     "archived": false,
     "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",
@@ -254,7 +257,8 @@ Parameters:
       "id": 4,
       "name": "Brightbox",
       "path": "brightbox",
-      "kind": "group"
+      "kind": "group",
+      "full_path": "brightbox"
     },
     "permissions": {
       "project_access": {
@@ -389,7 +393,8 @@ Parameters:
     "id": 3,
     "name": "Diaspora",
     "path": "diaspora",
-    "kind": "group"
+    "kind": "group",
+    "full_path": "diaspora"
   },
   "permissions": {
     "project_access": {
@@ -767,7 +772,8 @@ Example response:
     "id": 3,
     "name": "Diaspora",
     "path": "diaspora",
-    "kind": "group"
+    "kind": "group",
+    "full_path": "diaspora"
   },
   "archived": true,
   "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
@@ -832,7 +838,8 @@ Example response:
     "id": 3,
     "name": "Diaspora",
     "path": "diaspora",
-    "kind": "group"
+    "kind": "group",
+    "full_path": "diaspora"
   },
   "archived": true,
   "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
@@ -903,7 +910,8 @@ Example response:
     "id": 3,
     "name": "Diaspora",
     "path": "diaspora",
-    "kind": "group"
+    "kind": "group",
+    "full_path": "diaspora"
   },
   "permissions": {
     "project_access": {
@@ -985,7 +993,8 @@ Example response:
     "id": 3,
     "name": "Diaspora",
     "path": "diaspora",
-    "kind": "group"
+    "kind": "group",
+    "full_path": "diaspora"
   },
   "permissions": {
     "project_access": {
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index d746070913d..91e43dcb114 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -12,7 +12,7 @@ module Backup
         path_to_project_bundle = path_to_bundle(project)
 
         # Create namespace dir if missing
-        FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
+        FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.full_path)) if project.namespace
 
         if project.empty_repo?
           $progress.puts "[SKIPPED]".color(:cyan)
diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb
index 1f4edc36928..b02b9737493 100644
--- a/lib/gitlab/google_code_import/importer.rb
+++ b/lib/gitlab/google_code_import/importer.rb
@@ -310,7 +310,7 @@ module Gitlab
           if name == project.import_source
             "##{id}"
           else
-            "#{project.namespace.path}/#{name}##{id}"
+            "#{project.namespace.full_path}/#{name}##{id}"
           end
         text = "~~#{text}~~" if deleted
         text
diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb
index d679edec36b..a46a41bc56e 100644
--- a/lib/gitlab/import_export.rb
+++ b/lib/gitlab/import_export.rb
@@ -35,7 +35,7 @@ module Gitlab
     end
 
     def export_filename(project:)
-      basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.namespace.path}_#{project.path}"
+      basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.namespace.full_path}_#{project.path}"
 
       "#{basename[0..FILENAME_LIMIT]}_export.tar.gz"
     end
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index e9ee47fc090..063ce74ecad 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -56,7 +56,7 @@ module Gitlab
       end
 
       def path_with_namespace
-        File.join(@project.namespace.path, @project.path)
+        File.join(@project.namespace.full_path, @project.path)
       end
 
       def repo_path
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 82e194c1af1..942cedd6cd4 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -172,7 +172,7 @@ module Gitlab
     #   add_namespace("/path/to/storage", "gitlab")
     #
     def add_namespace(storage, name)
-      FileUtils.mkdir(full_path(storage, name), mode: 0770) unless exists?(storage, name)
+      FileUtils.mkdir_p(full_path(storage, name), mode: 0770) unless exists?(storage, name)
     end
 
     # Remove directory from repositories storage
diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb
index 53f7d244d88..20743811dab 100644
--- a/spec/lib/gitlab/import_export/import_export_spec.rb
+++ b/spec/lib/gitlab/import_export/import_export_spec.rb
@@ -2,14 +2,15 @@ require 'spec_helper'
 
 describe Gitlab::ImportExport, services: true do
   describe 'export filename' do
-    let(:project) { create(:empty_project, :public, path: 'project-path') }
+    let(:group) { create(:group, :nested) }
+    let(:project) { create(:empty_project, :public, path: 'project-path', namespace: group) }
 
     it 'contains the project path' do
       expect(described_class.export_filename(project: project)).to include(project.path)
     end
 
     it 'contains the namespace path' do
-      expect(described_class.export_filename(project: project)).to include(project.namespace.path)
+      expect(described_class.export_filename(project: project)).to include(project.namespace.full_path)
     end
 
     it 'does not go over a certain length' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 35f3dd00870..b0087a9e15d 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1852,8 +1852,8 @@ describe Project, models: true do
   end
 
   describe '#pages_url' do
-    let(:group) { create :group, name: group_name }
-    let(:project) { create :empty_project, namespace: group, name: project_name }
+    let(:group) { create :group, name: 'Group' }
+    let(:nested_group) { create :group, parent: group }
     let(:domain) { 'Example.com' }
 
     subject { project.pages_url }
@@ -1863,18 +1863,37 @@ describe Project, models: true do
       allow(Gitlab.config.pages).to receive(:url).and_return('http://example.com')
     end
 
-    context 'group page' do
-      let(:group_name) { 'Group' }
-      let(:project_name) { 'group.example.com' }
+    context 'top-level group' do
+      let(:project) { create :empty_project, namespace: group, name: project_name }
 
-      it { is_expected.to eq("http://group.example.com") }
+      context 'group page' do
+        let(:project_name) { 'group.example.com' }
+
+        it { is_expected.to eq("http://group.example.com") }
+      end
+
+      context 'project page' do
+        let(:project_name) { 'Project' }
+
+        it { is_expected.to eq("http://group.example.com/project") }
+      end
     end
 
-    context 'project page' do
-      let(:group_name) { 'Group' }
-      let(:project_name) { 'Project' }
+    context 'nested group' do
+      let(:project) { create :empty_project, namespace: nested_group, name: project_name }
+      let(:expected_url) { "http://group.example.com/#{nested_group.path}/#{project.path}" }
 
-      it { is_expected.to eq("http://group.example.com/project") }
+      context 'group page' do
+        let(:project_name) { 'group.example.com' }
+
+        it { is_expected.to eq(expected_url) }
+      end
+
+      context 'project page' do
+        let(:project_name) { 'Project' }
+
+        it { is_expected.to eq(expected_url) }
+      end
     end
   end
 end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index ac0bbec44e0..5ea89c7e27c 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -639,6 +639,7 @@ describe API::Projects, api: true  do
           'name' => user.namespace.name,
           'path' => user.namespace.path,
           'kind' => user.namespace.kind,
+          'full_path' => user.namespace.full_path,
         })
       end
 
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index a495122bba7..36d99d80e79 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -682,6 +682,7 @@ describe API::V3::Projects, api: true do
           'name' => user.namespace.name,
           'path' => user.namespace.path,
           'kind' => user.namespace.kind,
+          'full_path' => user.namespace.full_path,
         })
       end
 
-- 
GitLab