Commit 2b3fc5e6 authored by Douwe Maan's avatar Douwe Maan

Add download button to project snippets

parent 54040ce0
...@@ -5,10 +5,12 @@ module SnippetsActions ...@@ -5,10 +5,12 @@ module SnippetsActions
end end
def raw def raw
disposition = params[:inline] == 'false' ? 'attachment' : 'inline'
send_data( send_data(
convert_line_endings(@snippet.content), convert_line_endings(@snippet.content),
type: 'text/plain; charset=utf-8', type: 'text/plain; charset=utf-8',
disposition: 'inline', disposition: disposition,
filename: @snippet.sanitized_file_name filename: @snippet.sanitized_file_name
) )
end end
......
...@@ -5,10 +5,10 @@ class SnippetsController < ApplicationController ...@@ -5,10 +5,10 @@ class SnippetsController < ApplicationController
include MarkdownPreview include MarkdownPreview
include RendersBlob include RendersBlob
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download] before_action :snippet, only: [:show, :edit, :destroy, :update, :raw]
# Allow read snippet # Allow read snippet
before_action :authorize_read_snippet!, only: [:show, :raw, :download] before_action :authorize_read_snippet!, only: [:show, :raw]
# Allow modify snippet # Allow modify snippet
before_action :authorize_update_snippet!, only: [:edit, :update] before_action :authorize_update_snippet!, only: [:edit, :update]
...@@ -16,7 +16,7 @@ class SnippetsController < ApplicationController ...@@ -16,7 +16,7 @@ class SnippetsController < ApplicationController
# Allow destroy snippet # Allow destroy snippet
before_action :authorize_admin_snippet!, only: [:destroy] before_action :authorize_admin_snippet!, only: [:destroy]
skip_before_action :authenticate_user!, only: [:index, :show, :raw, :download] skip_before_action :authenticate_user!, only: [:index, :show, :raw]
layout 'snippets' layout 'snippets'
respond_to :html respond_to :html
...@@ -83,14 +83,6 @@ class SnippetsController < ApplicationController ...@@ -83,14 +83,6 @@ class SnippetsController < ApplicationController
redirect_to snippets_path redirect_to snippets_path
end end
def download
send_data(
convert_line_endings(@snippet.content),
type: 'text/plain; charset=utf-8',
filename: @snippet.sanitized_file_name
)
end
def preview_markdown def preview_markdown
render_markdown_preview(params[:text], skip_project_check: true) render_markdown_preview(params[:text], skip_project_check: true)
end end
......
...@@ -118,15 +118,15 @@ module BlobHelper ...@@ -118,15 +118,15 @@ module BlobHelper
icon("#{file_type_icon_class('file', mode, name)} fw") icon("#{file_type_icon_class('file', mode, name)} fw")
end end
def blob_raw_url def blob_raw_url(params = {})
if @snippet if @snippet
if @snippet.project_id if @snippet.project_id
raw_namespace_project_snippet_path(@project.namespace, @project, @snippet) raw_namespace_project_snippet_path(@project.namespace, @project, @snippet, params)
else else
raw_snippet_path(@snippet) raw_snippet_path(@snippet, params)
end end
elsif @blob elsif @blob
namespace_project_raw_path(@project.namespace, @project, @id) namespace_project_raw_path(@project.namespace, @project, @id, params)
end end
end end
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
= copy_blob_source_button(blob) = copy_blob_source_button(blob)
= open_raw_blob_button(blob) = open_raw_blob_button(blob)
- if defined?(download_path) && download_path = link_to icon('download'), blob_raw_url(inline: false), target: '_blank', class: "btn btn-sm has-tooltip", title: 'Download', data: { container: 'body' }
= link_to icon('download'), download_path, class: "btn btn-sm has-tooltip", title: 'Download', data: { container: 'body' }
= render 'projects/blob/content', blob: blob = render 'projects/blob/content', blob: blob
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= render 'shared/snippets/header' = render 'shared/snippets/header'
%article.file-holder.snippet-file-content %article.file-holder.snippet-file-content
= render 'shared/snippets/blob', download_path: download_snippet_path(@snippet) = render 'shared/snippets/blob'
.row-content-block.top-block.content-component-block .row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true = render 'award_emoji/awards_block', awardable: @snippet, inline: true
---
title: Add download button to project snippets
merge_request:
author:
...@@ -44,7 +44,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -44,7 +44,7 @@ constraints(ProjectUrlConstrainer.new) do
resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do
member do member do
get 'raw' get :raw
post :mark_as_spam post :mark_as_spam
end end
end end
......
resources :snippets, concerns: :awardable do resources :snippets, concerns: :awardable do
member do member do
get 'raw' get :raw
get 'download'
post :mark_as_spam post :mark_as_spam
post :preview_markdown post :preview_markdown
end end
......
...@@ -350,144 +350,138 @@ describe SnippetsController do ...@@ -350,144 +350,138 @@ describe SnippetsController do
end end
end end
%w(raw download).each do |action| describe "GET #raw" do
describe "GET #{action}" do context 'when the personal snippet is private' do
context 'when the personal snippet is private' do let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
context 'when signed in' do context 'when signed in' do
before do before do
sign_in(user) sign_in(user)
end end
context 'when signed in user is not the author' do context 'when signed in user is not the author' do
let(:other_author) { create(:author) } let(:other_author) { create(:author) }
let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) }
it 'responds with status 404' do it 'responds with status 404' do
get action, id: other_personal_snippet.to_param get :raw, id: other_personal_snippet.to_param
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end
end end
end
context 'when signed in user is the author' do context 'when signed in user is the author' do
before { get action, id: personal_snippet.to_param } before { get :raw, id: personal_snippet.to_param }
it 'responds with status 200' do it 'responds with status 200' do
expect(assigns(:snippet)).to eq(personal_snippet) expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
it 'has expected headers' do it 'has expected headers' do
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
if action == :download expect(response.header['Content-Disposition']).to match(/inline/)
expect(response.header['Content-Disposition']).to match(/attachment/)
elsif action == :raw
expect(response.header['Content-Disposition']).to match(/inline/)
end
end
end end
end end
end
context 'when not signed in' do context 'when not signed in' do
it 'redirects to the sign in page' do it 'redirects to the sign in page' do
get action, id: personal_snippet.to_param get :raw, id: personal_snippet.to_param
expect(response).to redirect_to(new_user_session_path) expect(response).to redirect_to(new_user_session_path)
end
end end
end end
end
context 'when the personal snippet is internal' do context 'when the personal snippet is internal' do
let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } let(:personal_snippet) { create(:personal_snippet, :internal, author: user) }
context 'when signed in' do context 'when signed in' do
before do before do
sign_in(user) sign_in(user)
end end
it 'responds with status 200' do it 'responds with status 200' do
get action, id: personal_snippet.to_param get :raw, id: personal_snippet.to_param
expect(assigns(:snippet)).to eq(personal_snippet) expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
end end
end
context 'when not signed in' do context 'when not signed in' do
it 'redirects to the sign in page' do it 'redirects to the sign in page' do
get action, id: personal_snippet.to_param get :raw, id: personal_snippet.to_param
expect(response).to redirect_to(new_user_session_path) expect(response).to redirect_to(new_user_session_path)
end
end end
end end
end
context 'when the personal snippet is public' do context 'when the personal snippet is public' do
let(:personal_snippet) { create(:personal_snippet, :public, author: user) } let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
context 'when signed in' do context 'when signed in' do
before do before do
sign_in(user) sign_in(user)
end end
it 'responds with status 200' do it 'responds with status 200' do
get action, id: personal_snippet.to_param get :raw, id: personal_snippet.to_param
expect(assigns(:snippet)).to eq(personal_snippet) expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
context 'CRLF line ending' do context 'CRLF line ending' do
let(:personal_snippet) do let(:personal_snippet) do
create(:personal_snippet, :public, author: user, content: "first line\r\nsecond line\r\nthird line") create(:personal_snippet, :public, author: user, content: "first line\r\nsecond line\r\nthird line")
end end
it 'returns LF line endings by default' do it 'returns LF line endings by default' do
get action, id: personal_snippet.to_param get :raw, id: personal_snippet.to_param
expect(response.body).to eq("first line\nsecond line\nthird line") expect(response.body).to eq("first line\nsecond line\nthird line")
end end
it 'does not convert line endings when parameter present' do it 'does not convert line endings when parameter present' do
get action, id: personal_snippet.to_param, line_ending: :raw get :raw, id: personal_snippet.to_param, line_ending: :raw
expect(response.body).to eq("first line\r\nsecond line\r\nthird line") expect(response.body).to eq("first line\r\nsecond line\r\nthird line")
end
end end
end end
end
context 'when not signed in' do context 'when not signed in' do
it 'responds with status 200' do it 'responds with status 200' do
get action, id: personal_snippet.to_param get :raw, id: personal_snippet.to_param
expect(assigns(:snippet)).to eq(personal_snippet) expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end
end end
end end
end
context 'when the personal snippet does not exist' do context 'when the personal snippet does not exist' do
context 'when signed in' do context 'when signed in' do
before do before do
sign_in(user) sign_in(user)
end end
it 'responds with status 404' do it 'responds with status 404' do
get action, id: 'doesntexist' get :raw, id: 'doesntexist'
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end
end end
end
context 'when not signed in' do context 'when not signed in' do
it 'responds with status 404' do it 'responds with status 404' do
get action, id: 'doesntexist' get :raw, id: 'doesntexist'
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end
end end
end end
end end
......
...@@ -30,6 +30,12 @@ feature 'Project snippet', :js, feature: true do ...@@ -30,6 +30,12 @@ feature 'Project snippet', :js, feature: true do
# shows an enabled copy button # shows an enabled copy button
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
# shows a raw button
expect(page).to have_link('Open raw')
# shows a download button
expect(page).to have_link('Download')
end end
end end
end end
...@@ -59,6 +65,12 @@ feature 'Project snippet', :js, feature: true do ...@@ -59,6 +65,12 @@ feature 'Project snippet', :js, feature: true do
# shows a disabled copy button # shows a disabled copy button
expect(page).to have_selector('.js-copy-blob-source-btn.disabled') expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
# shows a raw button
expect(page).to have_link('Open raw')
# shows a download button
expect(page).to have_link('Download')
end end
end end
......
...@@ -24,6 +24,12 @@ feature 'Snippet', :js, feature: true do ...@@ -24,6 +24,12 @@ feature 'Snippet', :js, feature: true do
# shows an enabled copy button # shows an enabled copy button
expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)') expect(page).to have_selector('.js-copy-blob-source-btn:not(.disabled)')
# shows a raw button
expect(page).to have_link('Open raw')
# shows a download button
expect(page).to have_link('Download')
end end
end end
end end
...@@ -53,6 +59,12 @@ feature 'Snippet', :js, feature: true do ...@@ -53,6 +59,12 @@ feature 'Snippet', :js, feature: true do
# shows a disabled copy button # shows a disabled copy button
expect(page).to have_selector('.js-copy-blob-source-btn.disabled') expect(page).to have_selector('.js-copy-blob-source-btn.disabled')
# shows a raw button
expect(page).to have_link('Open raw')
# shows a download button
expect(page).to have_link('Download')
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment