Commit c5dee301 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'dm-snippet-download-button' into 'master'

Add download button to project snippets

See merge request !11004
parents d32ecb23 0ca6ff67
...@@ -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
......
...@@ -6,10 +6,10 @@ class SnippetsController < ApplicationController ...@@ -6,10 +6,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]
...@@ -17,7 +17,7 @@ class SnippetsController < ApplicationController ...@@ -17,7 +17,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
...@@ -89,14 +89,6 @@ class SnippetsController < ApplicationController ...@@ -89,14 +89,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
......
...@@ -8,6 +8,14 @@ module SnippetsHelper ...@@ -8,6 +8,14 @@ module SnippetsHelper
end end
end end
def download_snippet_path(snippet)
if snippet.project_id
raw_namespace_project_snippet_path(@project.namespace, @project, snippet, inline: false)
else
raw_snippet_path(snippet, inline: false)
end
end
# Return the path of a snippets index for a user or for a project # Return the path of a snippets index for a user or for a project
# #
# @returns String, path to snippet index # @returns String, path to snippet index
......
...@@ -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'), download_snippet_path(@snippet), 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