Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ group :development, :test do
gem 'aws-sdk-cloudwatchlogs', require: false
gem 'brakeman', require: false
gem 'bullet', '>= 6.0.2'
gem 'data_uri', require: false
gem 'erb_lint', '~> 0.1.0', require: false
gem 'i18n-tasks', '>= 0.9.31'
gem 'knapsack'
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ GEM
addressable
cssbundling-rails (1.0.0)
railties (>= 6.0.0)
data_uri (0.1.0)
debug_inspector (1.1.0)
derailed_benchmarks (1.8.1)
benchmark-ips (~> 2)
Expand Down Expand Up @@ -702,6 +703,7 @@ DEPENDENCIES
capybara-selenium (>= 0.0.6)
connection_pool
cssbundling-rails
data_uri
derailed_benchmarks (~> 1.8)
devise (~> 4.8)
dotiw (>= 4.0.1)
Expand Down
1 change: 1 addition & 0 deletions app/controllers/idv/personal_key_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def update
redirect_to next_step
end

# Remove this after the next deploy
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

def download
personal_key = user_session[:personal_key]

Expand Down
15 changes: 15 additions & 0 deletions app/javascript/packs/personal-key-page-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const formEl = document.getElementById('confirm-key');
const input = formEl.querySelector('input[type="text"]');
const modalTrigger = document.querySelector('[data-toggle="modal"]');
const modalDismiss = document.querySelector('[data-dismiss="personal-key-confirm"]');
const downloadLink = document.querySelector('a[download]');

let isInvalidForm = false;

Expand Down Expand Up @@ -114,6 +115,20 @@ function hide() {
modal.hide();
}

function downloadForIE(event) {
event.preventDefault();

const filename = downloadLink.getAttribute('download');
const data = scrapePersonalKey();
const blob = new Blob([data], { type: 'text/plain' });

window.navigator.msSaveBlob(blob, filename);
}

modalTrigger.addEventListener('click', show);
modalDismiss.addEventListener('click', hide);
formEl.addEventListener('submit', handleSubmit);

if (window.navigator.msSaveBlob) {
downloadLink.addEventListener('click', downloadForIE);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not able to test this myself, but @mitchellhenke said in slack the download worked

}
7 changes: 6 additions & 1 deletion app/views/shared/_personal_key.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
</div>
<%= render ButtonComponent.new(
action: ->(**tag_options, &block) do
link_to(idv_download_personal_key_path, **tag_options, &block)
link_to(
"data:text/plain;charset=utf-8,#{CGI.escape(code)}",
download: 'personal_key.txt',
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one thought is maybe we can translate this filename? not that urgent IMO

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think fine for a follow-up, yeah

**tag_options,
&block
)
end,
icon: :file_download,
outline: true,
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@
get '/come_back_later' => 'come_back_later#show'
get '/personal_key' => 'personal_key#show'
post '/personal_key' => 'personal_key#update'
# Remove this after the next deploy
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another thought --- I think we can convert the backup codes download functionality that does something similar

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite follow?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do the same "download" stuff in another controller and I think if we link this client-only approach, we should consider it there, too: https://github.com/18F/identity-idp/blob/main/app/controllers/users/backup_code_setup_controller.rb#L31-L34

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohhh got it 👍🏼

get '/download_personal_key' => 'personal_key#download'
get '/forgot_password' => 'forgot_password#new'
post '/forgot_password' => 'forgot_password#update'
Expand Down
32 changes: 32 additions & 0 deletions spec/views/shared/_personal_key.html.erb_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require 'rails_helper'
require 'data_uri'

RSpec.describe 'shared/_personal_key.html.erb' do
let(:personal_key) { RandomPhrase.new(num_words: 4).to_s }

describe 'download link' do
around do |ex|
# data_uri depends on URI.decode which was removed in Ruby 3.0 :sob:
module URI
def self.decode(value)
CGI.unescape(value)
end
end

ex.run

URI.singleton_class.undef_method(:decode)
end

it 'has the download attribute and a data: url for the personal key' do
render 'shared/personal_key', code: personal_key, update_path: '/test'

doc = Nokogiri::HTML(rendered)
download_link = doc.at_css('a[download]')
data_uri = URI::Data.new(download_link[:href])

expect(data_uri.content_type).to eq('text/plain')
expect(data_uri.data).to eq(personal_key)
end
end
end