使用 clipboard.js 复制 html.slim 中的文本
Copy text in html.slim using clipboard.js
我有一个双因素验证页面,上面显示了一个密钥(密文),我已经在我的应用程序中安装了 clipboard.js。
我想知道如何创建一个按钮来复制该密钥?
= simple_form_for @google_auth, as: 'google_auth', url: verify_google_auth_path do |f|
h4 = t('.step-1')
p
span = t('.download-app')
span == t('.guide-link')
h4 = t('.step-2')
p: span = t('.scan-qr-code')
= f.input :uri do
= qr_tag(@google_auth.uri)
= f.input :otp_secret do
.input-group
= f.input_field :otp_secret, class: 'upcase', readonly: true
span.input-group-btn
a.btn.btn-default href='#{verify_google_auth_path(:app, refresh: true)}'
i.fa.fa-refresh
h4 = t('.step-3')
p: span = t('.enter-passcode')
= f.input :otp
hr.split
= f.button :wrapped, t('.submit'), cancel: settings_path
= content_for :guide do
ul.list-unstyled
li: a target='_blank' href='https://apps.apple.com/br/app/authy/id494168017'
i.fa.fa-apple
span = t('.ios')
li: a target='_blank' href='https://play.google.com/store/apps/details?id=com.authy.authy'
i.fa.fa-android
span = t('.android')
我试过这样做,但没有成功:
a.btn.btn-default data-clipboard-action='copy' data-clipboard-target=':otp_secret'
i.fa.fa-clipboard
在上面的示例中,它仅复制纯 otp_secret 文本。
spec\models\two_factor\app_spec.rb:
require 'spec_helper'
describe TwoFactor::App do
let(:member) { create :member }
let(:app) { member.app_two_factor }
describe "generate code" do
subject { app }
its(:otp_secret) { should_not be_blank }
end
describe '#refresh' do
context 'inactivated' do
it {
orig_otp_secret = app.otp_secret.dup
app.refresh!
expect(app.otp_secret).not_to eq(orig_otp_secret)
}
end
context 'activated' do
subject { create :two_factor_app, activated: true }
it {
orig_otp_secret = subject.otp_secret.dup
subject.refresh!
expect(subject.otp_secret).to eq(orig_otp_secret)
}
end
end
describe 'uniq validate' do
let(:member) { create :member }
it "reject duplicate creation" do
duplicate = TwoFactor.new app.attributes
expect(duplicate).not_to be_valid
end
end
describe 'self.fetch_by_type' do
it "return nil for wrong type" do
expect(TwoFactor.by_type(:foobar)).to be_nil
end
it "create new one by type" do
expect {
expect(app).not_to be_nil
}.to change(TwoFactor::App, :count).by(1)
end
it "retrieve exist one instead of creating" do
two_factor = member.app_two_factor
expect(member.app_two_factor).to eq(two_factor)
end
end
describe '#active!' do
subject { member.app_two_factor }
before { subject.active! }
its(:activated?) { should be_true }
end
describe '#deactive!' do
subject { create :two_factor_app, activated: true }
before { subject.deactive! }
its(:activated?) { should_not be_true }
end
describe '.activated' do
before { create :member, :app_two_factor_activated }
it "should has activated" do
expect(TwoFactor.activated?).to be_true
end
end
describe 'send_notification_mail' do
let(:mail) { ActionMailer::Base.deliveries.last }
describe "activated" do
before { app.active! }
it { expect(mail.subject).to match('Google authenticator activated') }
end
describe "deactived" do
let(:member) { create :member, :app_two_factor_activated }
before { app.deactive! }
it { expect(mail.subject).to match('Google authenticator deactivated') }
end
end
end
app.rb:
class TwoFactor::App < ::TwoFactor
def verify?
return false if otp_secret.blank?
rotp = ROTP::TOTP.new(otp_secret)
if rotp.verify(otp)
touch(:last_verify_at)
true
else
errors.add :otp, :invalid
false
end
end
def uri
totp = ROTP::TOTP.new(otp_secret)
totp.provisioning_uri(member.email) + "&issuer=#{ENV['URL_HOST']}"
end
def now
ROTP::TOTP.new(otp_secret).now
end
def refresh!
return if activated?
super
end
private
def gen_code
self.otp_secret = ROTP::Base32.random_base32
self.refreshed_at = Time.new
end
def send_notification
return if not self.activated_changed?
if self.activated
MemberMailer.google_auth_activated(member.id).deliver
else
MemberMailer.google_auth_deactivated(member.id).deliver
end
end
end
编辑:
app\models\two_factor.rb:
class TwoFactor < ActiveRecord::Base
belongs_to :member
before_validation :gen_code, on: :create
after_update :send_notification
validates_presence_of :member, :otp_secret, :refreshed_at
attr_accessor :otp
SUBCLASS = ['app', 'sms', 'email', 'wechat']
validates_uniqueness_of :type, scope: :member_id
scope :activated, -> { where(activated: true) }
scope :require_signin, -> { where(require_signin: 1) }
class << self
def by_type(type)
return if not SUBCLASS.include?(type.to_s)
klass = "two_factor/#{type}".camelize.constantize
klass.find_or_create_by(type: klass.name)
end
def activated?
activated.any?
end
def require_signin?
require_signin.any?
end
end
def verify?
msg = "#{self.class.name}#verify? is not implemented."
raise NotImplementedError.new(msg)
end
def expired?
Time.now >= 30.minutes.since(refreshed_at)
end
def refresh!
gen_code
save
end
def active!
update activated: true, last_verify_at: Time.now
end
def set_require_signin
update require_signin: 1
end
def reset_require_signin
update require_signin: nil
end
def deactive!
update activated: false, require_signin: nil
end
private
def gen_code
msg = "#{self.class.name}#gen_code is not implemented."
raise NotImplementedError.new(msg)
end
def send_notification
msg = "#{self.class.name}#send_notification is not implemented."
raise NotImplementedError.new(msg)
end
end
您尝试做的似乎只是将输入字段的值(已由您拥有的其他代码填充)复制到系统剪贴板。您需要使用 javascript 来执行此操作,如果您有 jquery 这应该可以。
为了你的苗条,你需要一个 id 来定位它
a.btn.btn-default id= "copy"
i.fa.fa-clipboard
尝试向要从中复制的输入元素添加一个 id
= f.input_field :otp_secret, class: 'upcase', id: "secret", readonly: true
现在尝试更改它,看看是否有效。
a.btn.btn-default data-clipboard-action='copy' data-clipboard-target='secret'
i.fa.fa-clipboard
另外,在您的 javascript 中的某处,您需要使用类似这样的内容来定位剪辑事件:
new ClipboardJS('#secret');
请参阅此处示例 https://jsfiddle.net/ec3ywrzd/
然后你需要这个 javascript 来加载你的 html。但是您需要能够以密码字段为目标,在本例中我使用的是 id="secret"
。我不确定您拥有的 OTP 代码是否生成了它自己的 ID 或现在,因此您可能需要检查您的 dom 以确定如何定位它以添加 ID。您可以尝试在此处添加 ID:
= f.input_field :otp_secret, class: 'upcase', id: "secret", readonly: true
否则您将不得不使用其他查询选择器来定位它。
但是你可能根本不需要 clipboardjs。
这里有一个 basic example on jsfiddle 来测试它,您可以将任何字符串添加到输入字段。您需要将其添加到将由您的视图布局加载的 JS 文件中,即 application.js
$(document).ready(function() {
$('#copy').click(function(){
$('#secret').select();
document.execCommand('copy');
alert("copied!");
})
})
您也可以see answers to this question
我根据朋友@lacostenycoder 的建议设法解决了。
即使在 show.html.slim 文件中也只需要更改,如下所示:
= simple_form_for @google_auth, as: 'google_auth', url: verify_google_auth_path do |f|
h4 = t('.step-1')
p
span = t('.download-app')
span == t('.guide-link')
h4 = t('.step-2')
p: span = t('.scan-qr-code')
= f.input :uri do
= qr_tag(@google_auth.uri)
= f.input :otp_secret do
.input-group
.form-control.form-control-static = @google_auth.otp_secret
.input-group
a.btn.btn-default href="javascript:void(0)" data-clipboard-text = @google_auth.otp_secret
i.fa.fa-clipboard
a.btn.btn-default href='#{verify_google_auth_path(:app, refresh: true)}'
i.fa.fa-refresh
h4 = t('.step-3')
p: span = t('.enter-passcode')
= f.input :otp
hr.split
= f.button :wrapped, t('.submit'), cancel: settings_path
= content_for :guide do
ul.list-unstyled
li: a target='_blank' href='https://apps.apple.com/br/app/authy/id494168017'
i.fa.fa-apple
span = t('.ios')
li: a target='_blank' href='https://play.google.com/store/apps/details?id=com.authy.authy'
i.fa.fa-android
span = t('.android')
我有一个双因素验证页面,上面显示了一个密钥(密文),我已经在我的应用程序中安装了 clipboard.js。
我想知道如何创建一个按钮来复制该密钥?
= simple_form_for @google_auth, as: 'google_auth', url: verify_google_auth_path do |f|
h4 = t('.step-1')
p
span = t('.download-app')
span == t('.guide-link')
h4 = t('.step-2')
p: span = t('.scan-qr-code')
= f.input :uri do
= qr_tag(@google_auth.uri)
= f.input :otp_secret do
.input-group
= f.input_field :otp_secret, class: 'upcase', readonly: true
span.input-group-btn
a.btn.btn-default href='#{verify_google_auth_path(:app, refresh: true)}'
i.fa.fa-refresh
h4 = t('.step-3')
p: span = t('.enter-passcode')
= f.input :otp
hr.split
= f.button :wrapped, t('.submit'), cancel: settings_path
= content_for :guide do
ul.list-unstyled
li: a target='_blank' href='https://apps.apple.com/br/app/authy/id494168017'
i.fa.fa-apple
span = t('.ios')
li: a target='_blank' href='https://play.google.com/store/apps/details?id=com.authy.authy'
i.fa.fa-android
span = t('.android')
我试过这样做,但没有成功:
a.btn.btn-default data-clipboard-action='copy' data-clipboard-target=':otp_secret'
i.fa.fa-clipboard
在上面的示例中,它仅复制纯 otp_secret 文本。
spec\models\two_factor\app_spec.rb:
require 'spec_helper'
describe TwoFactor::App do
let(:member) { create :member }
let(:app) { member.app_two_factor }
describe "generate code" do
subject { app }
its(:otp_secret) { should_not be_blank }
end
describe '#refresh' do
context 'inactivated' do
it {
orig_otp_secret = app.otp_secret.dup
app.refresh!
expect(app.otp_secret).not_to eq(orig_otp_secret)
}
end
context 'activated' do
subject { create :two_factor_app, activated: true }
it {
orig_otp_secret = subject.otp_secret.dup
subject.refresh!
expect(subject.otp_secret).to eq(orig_otp_secret)
}
end
end
describe 'uniq validate' do
let(:member) { create :member }
it "reject duplicate creation" do
duplicate = TwoFactor.new app.attributes
expect(duplicate).not_to be_valid
end
end
describe 'self.fetch_by_type' do
it "return nil for wrong type" do
expect(TwoFactor.by_type(:foobar)).to be_nil
end
it "create new one by type" do
expect {
expect(app).not_to be_nil
}.to change(TwoFactor::App, :count).by(1)
end
it "retrieve exist one instead of creating" do
two_factor = member.app_two_factor
expect(member.app_two_factor).to eq(two_factor)
end
end
describe '#active!' do
subject { member.app_two_factor }
before { subject.active! }
its(:activated?) { should be_true }
end
describe '#deactive!' do
subject { create :two_factor_app, activated: true }
before { subject.deactive! }
its(:activated?) { should_not be_true }
end
describe '.activated' do
before { create :member, :app_two_factor_activated }
it "should has activated" do
expect(TwoFactor.activated?).to be_true
end
end
describe 'send_notification_mail' do
let(:mail) { ActionMailer::Base.deliveries.last }
describe "activated" do
before { app.active! }
it { expect(mail.subject).to match('Google authenticator activated') }
end
describe "deactived" do
let(:member) { create :member, :app_two_factor_activated }
before { app.deactive! }
it { expect(mail.subject).to match('Google authenticator deactivated') }
end
end
end
app.rb:
class TwoFactor::App < ::TwoFactor
def verify?
return false if otp_secret.blank?
rotp = ROTP::TOTP.new(otp_secret)
if rotp.verify(otp)
touch(:last_verify_at)
true
else
errors.add :otp, :invalid
false
end
end
def uri
totp = ROTP::TOTP.new(otp_secret)
totp.provisioning_uri(member.email) + "&issuer=#{ENV['URL_HOST']}"
end
def now
ROTP::TOTP.new(otp_secret).now
end
def refresh!
return if activated?
super
end
private
def gen_code
self.otp_secret = ROTP::Base32.random_base32
self.refreshed_at = Time.new
end
def send_notification
return if not self.activated_changed?
if self.activated
MemberMailer.google_auth_activated(member.id).deliver
else
MemberMailer.google_auth_deactivated(member.id).deliver
end
end
end
编辑: app\models\two_factor.rb:
class TwoFactor < ActiveRecord::Base
belongs_to :member
before_validation :gen_code, on: :create
after_update :send_notification
validates_presence_of :member, :otp_secret, :refreshed_at
attr_accessor :otp
SUBCLASS = ['app', 'sms', 'email', 'wechat']
validates_uniqueness_of :type, scope: :member_id
scope :activated, -> { where(activated: true) }
scope :require_signin, -> { where(require_signin: 1) }
class << self
def by_type(type)
return if not SUBCLASS.include?(type.to_s)
klass = "two_factor/#{type}".camelize.constantize
klass.find_or_create_by(type: klass.name)
end
def activated?
activated.any?
end
def require_signin?
require_signin.any?
end
end
def verify?
msg = "#{self.class.name}#verify? is not implemented."
raise NotImplementedError.new(msg)
end
def expired?
Time.now >= 30.minutes.since(refreshed_at)
end
def refresh!
gen_code
save
end
def active!
update activated: true, last_verify_at: Time.now
end
def set_require_signin
update require_signin: 1
end
def reset_require_signin
update require_signin: nil
end
def deactive!
update activated: false, require_signin: nil
end
private
def gen_code
msg = "#{self.class.name}#gen_code is not implemented."
raise NotImplementedError.new(msg)
end
def send_notification
msg = "#{self.class.name}#send_notification is not implemented."
raise NotImplementedError.new(msg)
end
end
您尝试做的似乎只是将输入字段的值(已由您拥有的其他代码填充)复制到系统剪贴板。您需要使用 javascript 来执行此操作,如果您有 jquery 这应该可以。
为了你的苗条,你需要一个 id 来定位它
a.btn.btn-default id= "copy"
i.fa.fa-clipboard
尝试向要从中复制的输入元素添加一个 id
= f.input_field :otp_secret, class: 'upcase', id: "secret", readonly: true
现在尝试更改它,看看是否有效。
a.btn.btn-default data-clipboard-action='copy' data-clipboard-target='secret'
i.fa.fa-clipboard
另外,在您的 javascript 中的某处,您需要使用类似这样的内容来定位剪辑事件:
new ClipboardJS('#secret');
请参阅此处示例 https://jsfiddle.net/ec3ywrzd/
然后你需要这个 javascript 来加载你的 html。但是您需要能够以密码字段为目标,在本例中我使用的是 id="secret"
。我不确定您拥有的 OTP 代码是否生成了它自己的 ID 或现在,因此您可能需要检查您的 dom 以确定如何定位它以添加 ID。您可以尝试在此处添加 ID:
= f.input_field :otp_secret, class: 'upcase', id: "secret", readonly: true
否则您将不得不使用其他查询选择器来定位它。 但是你可能根本不需要 clipboardjs。
这里有一个 basic example on jsfiddle 来测试它,您可以将任何字符串添加到输入字段。您需要将其添加到将由您的视图布局加载的 JS 文件中,即 application.js
$(document).ready(function() {
$('#copy').click(function(){
$('#secret').select();
document.execCommand('copy');
alert("copied!");
})
})
您也可以see answers to this question
我根据朋友@lacostenycoder 的建议设法解决了。
即使在 show.html.slim 文件中也只需要更改,如下所示:
= simple_form_for @google_auth, as: 'google_auth', url: verify_google_auth_path do |f|
h4 = t('.step-1')
p
span = t('.download-app')
span == t('.guide-link')
h4 = t('.step-2')
p: span = t('.scan-qr-code')
= f.input :uri do
= qr_tag(@google_auth.uri)
= f.input :otp_secret do
.input-group
.form-control.form-control-static = @google_auth.otp_secret
.input-group
a.btn.btn-default href="javascript:void(0)" data-clipboard-text = @google_auth.otp_secret
i.fa.fa-clipboard
a.btn.btn-default href='#{verify_google_auth_path(:app, refresh: true)}'
i.fa.fa-refresh
h4 = t('.step-3')
p: span = t('.enter-passcode')
= f.input :otp
hr.split
= f.button :wrapped, t('.submit'), cancel: settings_path
= content_for :guide do
ul.list-unstyled
li: a target='_blank' href='https://apps.apple.com/br/app/authy/id494168017'
i.fa.fa-apple
span = t('.ios')
li: a target='_blank' href='https://play.google.com/store/apps/details?id=com.authy.authy'
i.fa.fa-android
span = t('.android')