diff --git a/app/controllers/tree_controller.rb b/app/controllers/tree_controller.rb new file mode 100644 index 00000000..15bbb1a3 --- /dev/null +++ b/app/controllers/tree_controller.rb @@ -0,0 +1,49 @@ +# Controller for viewing a repository's file structure +class TreeController < ApplicationController + # Thrown when given an invalid path + class InvalidPathError < StandardError; end + + include RefExtractor + + layout "project" + + before_filter :project + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_read_project! + before_filter :authorize_code_access! + before_filter :require_non_empty_project + + before_filter :define_tree_vars + + def show + respond_to do |format| + format.html + # Disable cache so browser history works + format.js { no_cache_headers } + end + end + + private + + def define_tree_vars + @ref, @path = extract_ref(params[:id]) + + @id = File.join(@ref, @path) + @repo = @project.repo + @commit = CommitDecorator.decorate(@project.commit(@ref)) + + @tree = Tree.new(@commit.tree, @project, @ref, @path.gsub(/^\//, '')) + @tree = TreeDecorator.new(@tree) + + raise InvalidPathError if @tree.invalid? + + @hex_path = Digest::SHA1.hexdigest(@path) + + @history_path = project_tree_path(@project, @id) + @logs_path = logs_file_project_ref_path(@project, @ref, @path) + rescue NoMethodError, InvalidPathError + not_found! + end +end diff --git a/app/models/tree.rb b/app/models/tree.rb index d65e50ab..88e8f2f4 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -1,5 +1,5 @@ class Tree - include Linguist::BlobHelper + include Linguist::BlobHelper attr_accessor :path, :tree, :project, :ref delegate :contents, @@ -14,8 +14,8 @@ class Tree to: :tree def initialize(raw_tree, project, ref = nil, path = nil) - @project, @ref, @path = project, ref, path, - @tree = if path + @project, @ref, @path = project, ref, path + @tree = if path.present? raw_tree / path.dup.force_encoding('ascii-8bit') else raw_tree @@ -26,6 +26,10 @@ class Tree tree.is_a?(Grit::Blob) end + def invalid? + tree.nil? + end + def empty? data.blank? end diff --git a/spec/controllers/tree_controller_spec.rb b/spec/controllers/tree_controller_spec.rb new file mode 100644 index 00000000..b9295537 --- /dev/null +++ b/spec/controllers/tree_controller_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe TreeController do + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + sign_in(user) + + project.add_access(user, :read, :admin) + + project.stub(:branches).and_return(['master', 'foo/bar/baz']) + project.stub(:tags).and_return(['v1.0.0', 'v2.0.0']) + controller.instance_variable_set(:@project, project) + end + + describe "GET show" do + # Make sure any errors accessing the tree in our views bubble up to this spec + render_views + + before { get :show, project_id: project.code, id: id } + + context "valid branch, no path" do + let(:id) { 'master' } + it { should respond_with(:success) } + end + + context "valid branch, valid path" do + let(:id) { 'master/README.md' } + it { should respond_with(:success) } + end + + context "valid branch, invalid path" do + let(:id) { 'master/invalid-path.rb' } + it { should respond_with(:not_found) } + end + + context "invalid branch, valid path" do + let(:id) { 'invalid-branch/README.md' } + it { should respond_with(:not_found) } + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d381b3f1..4700c3fe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -28,6 +28,7 @@ RSpec.configure do |config| config.include LoginHelpers, type: :request config.include GitoliteStub config.include FactoryGirl::Syntax::Methods + config.include Devise::TestHelpers, type: :controller # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false