[使用剪贴板.js复制html.slim中的文本

问题描述 投票:1回答:2

我有一个由两个因素组成的验证页面,上面显示了一个密钥(密文),并且我的应用程序中已经安装了剪贴板.js。

enter image description here

我想知道如何创建一个按钮来复制该密钥?

= 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
ruby slim-lang clipboard.js
2个回答
2
投票

[您似乎想做的只是将输入字段的值(已由您的其他代码填充)复制到系统剪贴板。您需要使用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中的某处,您需要使用以下内容来定位clip事件:

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 

否则,您将不得不使用其他查询选择器来定位它。但是您可能根本不需要剪贴板。

这里是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


0
投票

我设法根据朋友@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')
© www.soinside.com 2019 - 2024. All rights reserved.