summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-11-05 11:35:22 +0000
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-11-05 11:35:22 +0000
commit376dd3a3838622f73a444e5621257c831b284809 (patch)
treed924e72bbaa4b6dd2fbea631620a22dd5253df8f
parent2ee04b120a142ee227cb860ffddb991ef31a6910 (diff)
parent3083e5e4cdd0d158bc1c092f3e339eb8f83b2c4a (diff)
downloadgitlab-ce-376dd3a3838622f73a444e5621257c831b284809.tar.gz
Merge branch 'feature/create_file' of /home/git/repositories/gitlab/gitlabhq
-rw-r--r--app/contexts/base_context.rb1
-rw-r--r--app/contexts/files/base_context.rb31
-rw-r--r--app/contexts/files/create_context.rb50
-rw-r--r--app/contexts/files/update_context.rb38
-rw-r--r--app/controllers/projects/application_controller.rb6
-rw-r--r--app/controllers/projects/base_tree_controller.rb8
-rw-r--r--app/controllers/projects/edit_tree_controller.rb47
-rw-r--r--app/controllers/projects/new_tree_controller.rb18
-rw-r--r--app/controllers/projects/tree_controller.rb9
-rw-r--r--app/views/layouts/nav/_project.html.haml2
-rw-r--r--app/views/projects/new_tree/show.html.haml47
-rw-r--r--app/views/projects/tree/_tree.html.haml6
-rw-r--r--config/routes.rb20
-rw-r--r--features/project/source/browse_files.feature4
-rw-r--r--features/steps/project/project_browse_files.rb25
-rw-r--r--lib/gitlab/satellite/files/edit_file_action.rb (renamed from lib/gitlab/satellite/edit_file_action.rb)19
-rw-r--r--lib/gitlab/satellite/files/file_action.rb20
-rw-r--r--lib/gitlab/satellite/files/new_file_action.rb44
18 files changed, 315 insertions, 80 deletions
diff --git a/app/contexts/base_context.rb b/app/contexts/base_context.rb
index 101be50d54b..6accd9b2457 100644
--- a/app/contexts/base_context.rb
+++ b/app/contexts/base_context.rb
@@ -17,4 +17,3 @@ class BaseContext
abilities.allowed?(object, action, subject)
end
end
-
diff --git a/app/contexts/files/base_context.rb b/app/contexts/files/base_context.rb
new file mode 100644
index 00000000000..44f9826652c
--- /dev/null
+++ b/app/contexts/files/base_context.rb
@@ -0,0 +1,31 @@
+module Files
+ class BaseContext < ::BaseContext
+ attr_reader :ref, :path
+
+ def initialize(project, user, params, ref, path = nil)
+ @project, @current_user, @params = project, user, params.dup
+ @ref = ref
+ @path = path
+ end
+
+ private
+
+ def error(message)
+ {
+ error: message,
+ status: :error
+ }
+ end
+
+ def success
+ {
+ error: '',
+ status: :success
+ }
+ end
+
+ def repository
+ project.repository
+ end
+ end
+end
diff --git a/app/contexts/files/create_context.rb b/app/contexts/files/create_context.rb
new file mode 100644
index 00000000000..e1554c47bd6
--- /dev/null
+++ b/app/contexts/files/create_context.rb
@@ -0,0 +1,50 @@
+module Files
+ class CreateContext < BaseContext
+ def execute
+ allowed = if project.protected_branch?(ref)
+ can?(current_user, :push_code_to_protected_branches, project)
+ else
+ can?(current_user, :push_code, project)
+ end
+
+ unless allowed
+ return error("You are not allowed to create file in this branch")
+ end
+
+ unless repository.branch_names.include?(ref)
+ return error("You can only create files if you are on top of a branch")
+ end
+
+ file_name = params[:file_name]
+
+ unless file_name =~ Gitlab::Regex.path_regex
+ return error("Your changes could not be commited, because file name contains not allowed characters")
+ end
+
+ file_path = if path.blank?
+ file_name
+ else
+ File.join(path, file_name)
+ end
+
+ blob = repository.blob_at(ref, file_path)
+
+ if blob
+ return error("Your changes could not be commited, because file with such name exists")
+ end
+
+ new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, path)
+ created_successfully = new_file_action.commit!(
+ params[:content],
+ params[:commit_message],
+ file_name,
+ )
+
+ if created_successfully
+ success
+ else
+ error("Your changes could not be commited, because the file has been changed")
+ end
+ end
+ end
+end
diff --git a/app/contexts/files/update_context.rb b/app/contexts/files/update_context.rb
new file mode 100644
index 00000000000..000d3d02f12
--- /dev/null
+++ b/app/contexts/files/update_context.rb
@@ -0,0 +1,38 @@
+module Files
+ class UpdateContext < BaseContext
+ def execute
+ allowed = if project.protected_branch?(ref)
+ can?(current_user, :push_code_to_protected_branches, project)
+ else
+ can?(current_user, :push_code, project)
+ end
+
+ unless allowed
+ return error("You are not allowed to push into this branch")
+ end
+
+ unless repository.branch_names.include?(ref)
+ return error("You can only create files if you are on top of a branch")
+ end
+
+ blob = repository.blob_at(ref, path)
+
+ unless blob
+ return error("You can only edit text files")
+ end
+
+ new_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
+ created_successfully = new_file_action.commit!(
+ params[:content],
+ params[:commit_message],
+ params[:last_commit]
+ )
+
+ if created_successfully
+ success
+ else
+ error("Your changes could not be commited, because the file has been changed")
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 8fd4565f367..80aeb5cd6cc 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -23,4 +23,10 @@ class Projects::ApplicationController < ApplicationController
'public_projects'
end
end
+
+ def require_branch_head
+ unless @repository.branch_names.include?(@ref)
+ redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch"
+ end
+ end
end
diff --git a/app/controllers/projects/base_tree_controller.rb b/app/controllers/projects/base_tree_controller.rb
new file mode 100644
index 00000000000..5e305934433
--- /dev/null
+++ b/app/controllers/projects/base_tree_controller.rb
@@ -0,0 +1,8 @@
+class Projects::BaseTreeController < Projects::ApplicationController
+ include ExtractsPath
+
+ before_filter :authorize_read_project!
+ before_filter :authorize_code_access!
+ before_filter :require_non_empty_project
+end
+
diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb
index 0e51ff59f39..f6c547a020c 100644
--- a/app/controllers/projects/edit_tree_controller.rb
+++ b/app/controllers/projects/edit_tree_controller.rb
@@ -1,53 +1,26 @@
-# Controller for edit a repository's file
-class Projects::EditTreeController < Projects::ApplicationController
- include ExtractsPath
-
- # Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_code_access!
- before_filter :require_non_empty_project
-
- before_filter :edit_requirements, only: [:show, :update]
+class Projects::EditTreeController < Projects::BaseTreeController
+ before_filter :require_branch_head
+ before_filter :blob
def show
@last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
end
def update
- edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
- updated_successfully = edit_file_action.commit!(
- params[:content],
- params[:commit_message],
- params[:last_commit]
- )
+ result = Files::UpdateContext.new(@project, current_user, params, @ref, @path).execute
- if updated_successfully
- redirect_to project_blob_path(@project, @id), notice: "Your changes have been successfully commited"
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully commited"
+ redirect_to project_blob_path(@project, @id)
else
- flash[:notice] = "Your changes could not be commited, because the file has been changed"
+ flash[:alert] = result[:error]
render :show
end
end
private
- def edit_requirements
- @blob = @repository.blob_at(@commit.id, @path)
-
- unless @blob
- redirect_to project_blob_path(@project, @id), notice: "You can only edit text files"
- end
-
- allowed = if project.protected_branch? @ref
- can?(current_user, :push_code_to_protected_branches, project)
- else
- can?(current_user, :push_code, project)
- end
-
- return access_denied! unless allowed
-
- unless @repository.branch_names.include?(@ref)
- redirect_to project_blob_path(@project, @id), notice: "You can only edit this file if you are on top of a branch"
- end
+ def blob
+ @blob ||= @repository.blob_at(@commit.id, @path)
end
end
diff --git a/app/controllers/projects/new_tree_controller.rb b/app/controllers/projects/new_tree_controller.rb
new file mode 100644
index 00000000000..9f9e0191e98
--- /dev/null
+++ b/app/controllers/projects/new_tree_controller.rb
@@ -0,0 +1,18 @@
+class Projects::NewTreeController < Projects::BaseTreeController
+ before_filter :require_branch_head
+
+ def show
+ end
+
+ def update
+ result = Files::CreateContext.new(@project, current_user, params, @ref, @path).execute
+
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully commited"
+ redirect_to project_blob_path(@project, File.join(@id, params[:file_name]))
+ else
+ flash[:alert] = result[:error]
+ render :show
+ end
+ end
+end
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 1150efbea9d..30c94ec6da0 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -1,12 +1,5 @@
# Controller for viewing a repository's file structure
-class Projects::TreeController < Projects::ApplicationController
- include ExtractsPath
-
- # Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_code_access!
- before_filter :require_non_empty_project
-
+class Projects::TreeController < Projects::BaseTreeController
def show
return not_found! if tree.entries.empty?
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index e9d535f6972..1f70cf17987 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -4,7 +4,7 @@
%i.icon-home
- if project_nav_tab? :files
- = nav_link(controller: %w(tree blob blame edit_tree)) do
+ = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
= link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
- if project_nav_tab? :commits
diff --git a/app/views/projects/new_tree/show.html.haml b/app/views/projects/new_tree/show.html.haml
new file mode 100644
index 00000000000..d5525303d76
--- /dev/null
+++ b/app/views/projects/new_tree/show.html.haml
@@ -0,0 +1,47 @@
+%h3.page-title New file
+%hr
+.file-editor
+ = form_tag(project_new_tree_path(@project, @id), method: :put, class: "form-horizontal") do
+ .control-group.commit_message-group
+ = label_tag 'file_name', class: "control-label" do
+ File name
+ .controls
+ %span.monospace= @path[-1] == "/" ? @path : @path + "/"
+ &nbsp;
+ = text_field_tag 'file_name', params[:file_name], placeholder: "sample.rb", required: true
+ %span
+ &nbsp;
+ on
+ %span.label-branch= @ref
+
+ .control-group.commit_message-group
+ = label_tag 'commit_message', class: "control-label" do
+ Commit message
+ .controls
+ = text_area_tag 'commit_message', params[:commit_message], placeholder: "Added new file", required: true, rows: 3
+
+ .file-holder
+ .file-title
+ %i.icon-file
+ .file-content.code
+ %pre#editor= params[:content]
+
+ .form-actions
+ = hidden_field_tag 'content', '', id: "file-content"
+ .commit-button-annotation
+ = button_tag "Commit changes", class: 'btn commit-btn js-commit-button btn-create'
+ .message
+ to branch
+ %strong= @ref
+ = link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-cancel", confirm: leave_edit_message
+
+:javascript
+ ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
+ var editor = ace.edit("editor");
+
+ disableButtonIfEmptyField("#commit_message", ".js-commit-button");
+
+ $(".js-commit-button").click(function(){
+ $("#file-content").val(editor.getValue());
+ $(".file-editor form").submit();
+ });
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index eadfd33bd3c..6b5b84f83b0 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -10,6 +10,12 @@
= link_to truncate(title, length: 40), project_tree_path(@project, path)
- else
= link_to title, '#'
+ - if @repository.branch_names.include?(@ref)
+ \/
+ %li
+ = link_to project_new_tree_path(@project, @id), title: 'New file', id: 'new-file-link' do
+ %small
+ %i.icon-plus.light
%div#tree-content-holder.tree-content-holder
%table#tree-slider{class: "table_#{@hex_path} tree-table" }
diff --git a/config/routes.rb b/config/routes.rb
index 78f75d11835..8f1758394b6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -166,16 +166,18 @@ Gitlab::Application.routes.draw do
end
scope module: :projects do
- resources :blob, only: [:show], constraints: {id: /.+/}
- resources :raw, only: [:show], constraints: {id: /.+/}
- resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
- resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit'
- resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
- resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
- resources :compare, only: [:index, :create]
- resources :blame, only: [:show], constraints: {id: /.+/}
+ resources :blob, only: [:show], constraints: {id: /.+/}
+ resources :raw, only: [:show], constraints: {id: /.+/}
+ resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
+ resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit'
+ resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new'
+ resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
+ resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
+ resources :compare, only: [:index, :create]
+ resources :blame, only: [:show], constraints: {id: /.+/}
resources :network, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
- resources :graphs, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
+ resources :graphs, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
+
match "/compare/:from...:to" => "compare#show", as: "compare", via: [:get, :post], constraints: {from: /.+/, to: /.+/}
resources :snippets, constraints: {id: /\d+/} do
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index ee26f5371a9..fd9a2f01a28 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -20,6 +20,10 @@ Feature: Project Browse files
And I click link "raw"
Then I should see raw file content
+ Scenario: I can create file
+ Given I click on "new file" link in repo
+ Then I can see new file page
+
@javascript
Scenario: I can edit file
Given I click on "Gemfile.lock" file in repo
diff --git a/features/steps/project/project_browse_files.rb b/features/steps/project/project_browse_files.rb
index 71360fb6bd5..069086d5eac 100644
--- a/features/steps/project/project_browse_files.rb
+++ b/features/steps/project/project_browse_files.rb
@@ -3,42 +3,51 @@ class ProjectBrowseFiles < Spinach::FeatureSteps
include SharedProject
include SharedPaths
- Then 'I should see files from repository' do
+ step 'I should see files from repository' do
page.should have_content "app"
page.should have_content "history"
page.should have_content "Gemfile"
end
- Then 'I should see files from repository for "8470d70"' do
+ step 'I should see files from repository for "8470d70"' do
current_path.should == project_tree_path(@project, "8470d70")
page.should have_content "app"
page.should have_content "history"
page.should have_content "Gemfile"
end
- Given 'I click on "Gemfile.lock" file in repo' do
+ step 'I click on "Gemfile.lock" file in repo' do
click_link "Gemfile.lock"
end
- Then 'I should see it content' do
+ step 'I should see it content' do
page.should have_content "DEPENDENCIES"
end
- And 'I click link "raw"' do
+ step 'I click link "raw"' do
click_link "raw"
end
- Then 'I should see raw file content' do
+ step 'I should see raw file content' do
page.source.should == ValidCommit::BLOB_FILE
end
- Given 'I click button "edit"' do
+ step 'I click button "edit"' do
click_link 'edit'
end
- Then 'I can edit code' do
+ step 'I can edit code' do
page.execute_script('editor.setValue("GitlabFileEditor")')
page.evaluate_script('editor.getValue()').should == "GitlabFileEditor"
end
+ step 'I click on "new file" link in repo' do
+ click_link 'new-file-link'
+ end
+
+ step 'I can see new file page' do
+ page.should have_content "New file"
+ page.should have_content "File name"
+ page.should have_content "Commit message"
+ end
end
diff --git a/lib/gitlab/satellite/edit_file_action.rb b/lib/gitlab/satellite/files/edit_file_action.rb
index d793d0ba8dc..72e12fb077c 100644
--- a/lib/gitlab/satellite/edit_file_action.rb
+++ b/lib/gitlab/satellite/files/edit_file_action.rb
@@ -1,15 +1,9 @@
+require_relative 'file_action'
+
module Gitlab
module Satellite
# GitLab server-side file update and commit
- class EditFileAction < Action
- attr_accessor :file_path, :ref
-
- def initialize(user, project, ref, file_path)
- super user, project, git_timeout: 10.seconds
- @file_path = file_path
- @ref = ref
- end
-
+ class EditFileAction < FileAction
# Updates the files content and creates a new commit for it
#
# Returns false if the ref has been updated while editing the file
@@ -45,13 +39,6 @@ module Gitlab
Gitlab::GitLogger.error(ex.message)
false
end
-
- protected
-
- def can_edit?(last_commit)
- current_last_commit = Gitlab::Git::Commit.last_for_path(@project.repository, ref, file_path).sha
- last_commit == current_last_commit
- end
end
end
end
diff --git a/lib/gitlab/satellite/files/file_action.rb b/lib/gitlab/satellite/files/file_action.rb
new file mode 100644
index 00000000000..4ac53c2cd5a
--- /dev/null
+++ b/lib/gitlab/satellite/files/file_action.rb
@@ -0,0 +1,20 @@
+module Gitlab
+ module Satellite
+ class FileAction < Action
+ attr_accessor :file_path, :ref
+
+ def initialize(user, project, ref, file_path)
+ super user, project, git_timeout: 10.seconds
+ @file_path = file_path
+ @ref = ref
+ end
+
+ protected
+
+ def can_edit?(last_commit)
+ current_last_commit = Gitlab::Git::Commit.last_for_path(@project.repository, ref, file_path).sha
+ last_commit == current_last_commit
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/files/new_file_action.rb b/lib/gitlab/satellite/files/new_file_action.rb
new file mode 100644
index 00000000000..9fe5a38eb80
--- /dev/null
+++ b/lib/gitlab/satellite/files/new_file_action.rb
@@ -0,0 +1,44 @@
+require_relative 'file_action'
+
+module Gitlab
+ module Satellite
+ class NewFileAction < FileAction
+ # Updates the files content and creates a new commit for it
+ #
+ # Returns false if the ref has been updated while editing the file
+ # Returns false if committing the change fails
+ # Returns false if pushing from the satellite to Gitolite failed or was rejected
+ # Returns true otherwise
+ def commit!(content, commit_message, file_name)
+ in_locked_and_timed_satellite do |repo|
+ prepare_satellite!(repo)
+
+ # create target branch in satellite at the corresponding commit from Gitolite
+ repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
+
+ # update the file in the satellite's working dir
+ file_path_in_satellite = File.join(repo.working_dir, file_path, file_name)
+ File.open(file_path_in_satellite, 'w') { |f| f.write(content) }
+
+ # add new file
+ repo.add(file_path_in_satellite)
+
+ # commit the changes
+ # will raise CommandFailed when commit fails
+ repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
+
+
+ # push commit back to Gitolite
+ # will raise CommandFailed when push fails
+ repo.git.push({raise: true, timeout: true}, :origin, ref)
+
+ # everything worked
+ true
+ end
+ rescue Grit::Git::CommandFailed => ex
+ Gitlab::GitLogger.error(ex.message)
+ false
+ end
+ end
+ end
+end