diff --git a/README.md b/README.md index 10cbe01..3d7cc42 100755 --- a/README.md +++ b/README.md @@ -89,8 +89,8 @@ In `app/lib/anylogin_omniauth.rb` ``` module AnyloginOmniauth module Controller - def self.any_login_current_user_method - @@any_login_current_user_method ||= "current_#{AnyLogin.klass.to_s.parameterize.underscore}".to_sym + def self.any_login_current_user_method(klass) + @@any_login_current_user_method ||= "current_#{klass.to_s.parameterize.underscore}".to_sym end def any_login_sign_in @@ -114,7 +114,7 @@ It will create the initializer file `config/initializers/any_login.rb`. ### Options - **enabled** - enable or disable gem (by default this gem is enabled only in development mode). -- **klass_name** - class name for "User" object. Defaults to `User`. +- **klass_name** - class name for "User" object. Defaults to `User`. An array of class names is also supported. - **collection_method** - method which returns collection of users. Sample: `.all`, `.active`, `.admins`, `.groupped_users`. Value is a simple. Defaults to `:all`. @@ -130,7 +130,6 @@ It will create the initializer file `config/initializers/any_login.rb`. - **position** - position of AnyLogin box on page. Possible values: `top_left`, `top_right`, `bottom_left`, `bottom_right`. Default: `bottom_left`. - **login_button_label** - login button label. -- **select_prompt** - select prompt message. - **auto_show** - automatically show AnyLogin box. - **http_basic_authentication_enabled** - Enable HTTP_BASIC authentication. - **http_basic_authentication_user_name** - HTTP_BASIC authentication user name. diff --git a/app/assets/javascripts/any_login/application.js b/app/assets/javascripts/any_login/application.js index fa8edc5..a8ab42b 100644 --- a/app/assets/javascripts/any_login/application.js +++ b/app/assets/javascripts/any_login/application.js @@ -1,21 +1,13 @@ -function submitOnChange() { - document.getElementById('any_login_form').submit(); -} - -function elementExists(element) { - return typeof (element) != 'undefined' && element != null; -} - -function addChangeListenerTo(elementId) { - let element = document.getElementById(elementId); - if (elementExists(element)) { - element.addEventListener("change", submitOnChange); - } -} - function setupAnyLoginHandlers() { - addChangeListenerTo("back_to_previous_id"); - addChangeListenerTo("selected_id"); + const forms = document.querySelectorAll('.any_login_form'); + forms.forEach(form => { + const selects = form.querySelectorAll('select'); + selects.forEach(select => { + select.addEventListener('change', () => { + form.submit(); + }) + }) + }) } ['DOMContentLoaded', 'turbolinks:load', 'turbo:load'].forEach(function(e) { diff --git a/app/assets/stylesheets/any_login/application.css b/app/assets/stylesheets/any_login/application.css index 4e9c31b..b29c3f8 100644 --- a/app/assets/stylesheets/any_login/application.css +++ b/app/assets/stylesheets/any_login/application.css @@ -66,6 +66,7 @@ cursor: pointer; vertical-align: middle; fill: #fff; + margin: 5px; } #any_login:hover { @@ -77,20 +78,24 @@ color: yellow; } -#any_login #any_login_id_input { +#any_login .any_login_id_input { width: 30px; } -#any_login span#current_user_information { +#any_login span.current_user_information { display: inline; } -#any_login span#current_user_information span { +#any_login span.current_user_information span { display: inline; } -#any_login span#anylogin_back_to_user { +#any_login span.anylogin_back_to_user { padding-left: 15px; color: cyan; display: inline; } + +.any_login_form_toggle_label { + display: inline-block; +} diff --git a/app/controllers/any_login/application_controller.rb b/app/controllers/any_login/application_controller.rb index 385b19d..48b5df3 100755 --- a/app/controllers/any_login/application_controller.rb +++ b/app/controllers/any_login/application_controller.rb @@ -41,6 +41,10 @@ def user_id params[:back_to_previous_id].presence || params[:selected_id].presence || params[:id] end + def klass + params[:as].constantize + end + def previous cookies[AnyLogin.cookie_name] ||= "" end diff --git a/app/views/any_login/_any_login.html.erb b/app/views/any_login/_any_login.html.erb index bc3c031..50b5e51 100755 --- a/app/views/any_login/_any_login.html.erb +++ b/app/views/any_login/_any_login.html.erb @@ -2,18 +2,21 @@
- <%= form_tag any_login.sign_in_path, :method => :post, :id => 'any_login_form' do %> - - > - - <%= any_login_select %> - or by - <%= any_login_id_input %> - <%= any_login_submit %> - <%= current_user_information %> - <%= any_login_previous_select %> - - <% end %> + + > + + + <% AnyLogin.klasses.each do |klass| %> + <%= form_tag any_login.sign_in_path(as: klass.to_s), :method => :post, :class => 'any_login_form' do %> + <%= any_login_select(klass) %> + or by + <%= any_login_id_input %> + <%= any_login_submit %> + <%= current_user_information(klass) %> + <%= any_login_previous_select(klass) %> + <% end %> + <% end %> +
diff --git a/app/views/any_login/svg/_user.html.erb b/app/views/any_login/svg/_user.html.erb index 1a07606..0db9b89 100644 --- a/app/views/any_login/svg/_user.html.erb +++ b/app/views/any_login/svg/_user.html.erb @@ -1,6 +1,5 @@ - <%= title %> diff --git a/lib/any_login.rb b/lib/any_login.rb index 0210d7d..868f848 100755 --- a/lib/any_login.rb +++ b/lib/any_login.rb @@ -52,10 +52,6 @@ module Provider mattr_accessor :login_button_label @@login_button_label = 'Login' - # prompt message in select - mattr_accessor :select_prompt - @@select_prompt = "Select #{AnyLogin.klass_name}" - # show any_login box by default mattr_accessor :auto_show @@auto_show = false @@ -88,12 +84,16 @@ def self.setup yield(self) end - def self.collection - Collection.new(collection_raw) + def self.collection(klass) + Collection.new(collection_raw(klass)) end - def self.klass - @@klass = AnyLogin.klass_name.constantize + def self.klasses + @@klasses = if AnyLogin.klass_name.is_a?(Array) + AnyLogin.klass_name.map(&:constantize) + else + [AnyLogin.klass_name.constantize] + end end def self.cookie_name @@ -115,9 +115,9 @@ def self.format_collection_raw(result) end end - def self.collection_raw + def self.collection_raw(klass) @@collection_raw = begin - result = AnyLogin.klass.send(AnyLogin.collection_method) + result = klass.send(AnyLogin.collection_method) if limit == :none format_collection_raw(result) else diff --git a/lib/any_login/helpers.rb b/lib/any_login/helpers.rb index b276420..692f228 100644 --- a/lib/any_login/helpers.rb +++ b/lib/any_login/helpers.rb @@ -9,37 +9,37 @@ def any_login_here if AnyLogin.enabled def any_login_id_input - text_field_tag :id, '', :placeholder => 'ID', :id => 'any_login_id_input', :required => true + text_field_tag :id, '', :placeholder => 'ID', :class => 'any_login_id_input', :required => true end def any_login_submit submit_tag AnyLogin.login_button_label end - def any_login_select - collection = AnyLogin.collection + def any_login_select(klass) + collection = AnyLogin.collection(klass) select_options = if collection.grouped? grouped_options_for_select(collection.to_a) else options_for_select(collection.to_a) end - select_tag :selected_id, select_options, select_html_options + select_tag :selected_id, select_options, select_html_options(klass).merge(id: nil) end - def any_login_previous_select + def any_login_previous_select(klass) ids = any_login_previous_ids return if ids.blank? users = ids.collect do |id| - AnyLogin.klass.where(AnyLogin.klass.primary_key => id).first + klass.where(klass.primary_key => id).first end.compact collection = AnyLogin::Collection.new(users).to_a if collection.any? select_options = options_for_select(collection) [ - content_tag(:span, id: 'anylogin_back_to_user') do + content_tag(:span, class: 'anylogin_back_to_user') do "History: " end, select_tag(:back_to_previous_id, select_options, select_html_options("Back to:")) @@ -51,7 +51,8 @@ def any_login_previous_ids (cookies[AnyLogin.cookie_name].presence || '').split(',').take(AnyLogin.previous_limit) end - def select_html_options(prompt = AnyLogin.select_prompt) + def select_html_options(klass) + prompt = "Select #{klass}" options = {} options[:prompt] = prompt options @@ -63,14 +64,14 @@ def any_login_klasses klasses.join(' ') end - def current_user_information - if respond_to?(AnyLogin.provider.constantize::Controller.any_login_current_user_method) && - user = send(AnyLogin.provider.constantize::Controller.any_login_current_user_method) + def current_user_information(klass) + if respond_to?(AnyLogin.provider.constantize::Controller.any_login_current_user_method(klass)) && + user = send(AnyLogin.provider.constantize::Controller.any_login_current_user_method(klass)) content_tag :span, :class => 'any_login_user_information' do if AnyLogin.name_method.is_a?(Symbol) - raw("Current #{AnyLogin.klass_name}: #{h(user.send(AnyLogin.name_method)[0])} — ID: #{user.id}") + raw("Current #{klass.to_s}: #{h(user.send(AnyLogin.name_method)[0])} — ID: #{user.id}") else - raw("Current #{AnyLogin.klass_name}: #{h(AnyLogin.name_method.call(user)[0])} — ID: #{user.id}") + raw("Current #{klass.to_s}: #{h(AnyLogin.name_method.call(user)[0])} — ID: #{user.id}") end end end diff --git a/lib/any_login/providers/authlogic.rb b/lib/any_login/providers/authlogic.rb index fd4bc88..e7fc86d 100755 --- a/lib/any_login/providers/authlogic.rb +++ b/lib/any_login/providers/authlogic.rb @@ -4,14 +4,14 @@ module Authlogic module Controller - def self.any_login_current_user_method - @@any_login_current_user_method ||= "current_#{AnyLogin.klass.to_s.parameterize.underscore}".to_sym + def self.any_login_current_user_method(klass) + @@any_login_current_user_method ||= "current_#{klass.to_s.parameterize.underscore}".to_sym end def any_login_sign_in reset_session - @loginable = AnyLogin.klass.find(user_id) - Object.const_get("#{AnyLogin.klass}Session").create(@loginable) + @loginable = klass.find(user_id) + Object.const_get("#{klass}Session").create(@loginable) redirect_to main_app.send(AnyLogin.redirect_path_after_login) end diff --git a/lib/any_login/providers/clearance.rb b/lib/any_login/providers/clearance.rb index 9c07295..f69e0f5 100755 --- a/lib/any_login/providers/clearance.rb +++ b/lib/any_login/providers/clearance.rb @@ -4,13 +4,13 @@ module Clearance module Controller - def self.any_login_current_user_method - @@any_login_current_user_method ||= "current_#{AnyLogin.klass.to_s.parameterize.underscore}".to_sym + def self.any_login_current_user_method(klass) + @@any_login_current_user_method ||= "current_#{klass.to_s.parameterize.underscore}".to_sym end def any_login_sign_in reset_session - @loginable = AnyLogin.klass.find(user_id) + @loginable = klass.find(user_id) sign_in @loginable redirect_to main_app.send(AnyLogin.redirect_path_after_login) end diff --git a/lib/any_login/providers/devise.rb b/lib/any_login/providers/devise.rb index 9527642..560fc86 100755 --- a/lib/any_login/providers/devise.rb +++ b/lib/any_login/providers/devise.rb @@ -6,19 +6,19 @@ module Controller DEFAULT_SIGN_IN = proc do |loginable| reset_session - sign_in Controller.mapping_key, loginable + sign_in Controller.mapping_key(klass), loginable end - def self.any_login_current_user_method - @@any_login_current_user_method ||= "current_#{mapping_key}".to_sym + def self.any_login_current_user_method(klass) + @@any_login_current_user_method = "current_#{mapping_key(klass)}".to_sym end - def self.mapping_key - @@mapping_key ||= ::Devise.mappings.detect {|_key, mapping| mapping.class_name == AnyLogin.klass.name }.first + def self.mapping_key(klass) + @@mapping_key = ::Devise.mappings.detect {|_key, mapping| mapping.class_name == klass.name }.first end def any_login_sign_in - @loginable = AnyLogin.klass.find(user_id) + @loginable = klass.find(user_id) sign_in = AnyLogin.sign_in || DEFAULT_SIGN_IN instance_exec(@loginable, &sign_in) diff --git a/lib/any_login/providers/sorcery.rb b/lib/any_login/providers/sorcery.rb index 631e480..eb85cb1 100755 --- a/lib/any_login/providers/sorcery.rb +++ b/lib/any_login/providers/sorcery.rb @@ -4,13 +4,13 @@ module Sorcery module Controller - def self.any_login_current_user_method - @@any_login_current_user_method ||= "current_#{AnyLogin.klass.to_s.parameterize.underscore}".to_sym + def self.any_login_current_user_method(klass) + @@any_login_current_user_method ||= "current_#{klass.to_s.parameterize.underscore}".to_sym end def any_login_sign_in reset_session - @loginable = AnyLogin.klass.find(user_id) + @loginable = klass.find(user_id) auto_login @loginable redirect_to main_app.send(AnyLogin.redirect_path_after_login) end diff --git a/lib/generators/any_login/templates/any_login.rb b/lib/generators/any_login/templates/any_login.rb index 7a5c397..2936984 100644 --- a/lib/generators/any_login/templates/any_login.rb +++ b/lib/generators/any_login/templates/any_login.rb @@ -5,7 +5,7 @@ # # enabled or not # config.enabled = Rails.env.to_s == 'development' - # # Account, User, Person, etc + # # Account, User, Person, etc, or ['Account', 'User', 'Person'] for multiple model support # config.klass_name = 'User' # # .all, .active, .admins, .groped_collection, etc ... need to return an array (or hash with arrays) of users diff --git a/test/authlogic/integration/navigation_test.rb b/test/authlogic/integration/navigation_test.rb index cd1a9a4..3f79a3c 100644 --- a/test/authlogic/integration/navigation_test.rb +++ b/test/authlogic/integration/navigation_test.rb @@ -15,8 +15,8 @@ class NavigationTest < ActionDispatch::IntegrationTest test "it properly logs users in and allows access to the secret page" do visit "/" - find("#any_login_form_toggle_label").click - find('#selected_id option:last-of-type').select_option + find(".any_login_form_toggle_label").click + find("select option:last-of-type", match: :first).select_option visit "/about" diff --git a/test/clearance/integration/navigation_test.rb b/test/clearance/integration/navigation_test.rb index ab82f7d..fc7616c 100644 --- a/test/clearance/integration/navigation_test.rb +++ b/test/clearance/integration/navigation_test.rb @@ -14,8 +14,8 @@ class NavigationTest < ActionDispatch::IntegrationTest test "it properly logs users in and allows access to the secret page" do visit "/" - find("#any_login_form_toggle_label").click - find('#selected_id option:last-of-type').select_option + find(".any_login_form_toggle_label").click + find("select option:last-of-type", match: :first).select_option visit "/about" diff --git a/test/devise/integration/navigation_test.rb b/test/devise/integration/navigation_test.rb index c121f3e..9b88def 100644 --- a/test/devise/integration/navigation_test.rb +++ b/test/devise/integration/navigation_test.rb @@ -14,10 +14,10 @@ class NavigationTest < ActionDispatch::IntegrationTest test "it properly logs users in and allows access to the secret page" do visit "/" - find("#any_login_form_toggle_label").click + find(".any_login_form_toggle_label").click # There is an instability in the test suite where the option on the dropdown sometimes shows # up as `1` and sometimes shows up as `test@test.com`. This works around the issue. - find('#selected_id option:last-of-type').select_option + find("select option:last-of-type", match: :first).select_option visit "/about" diff --git a/test/rails_apps/devise/app/models/staff.rb b/test/rails_apps/devise/app/models/staff.rb new file mode 100644 index 0000000..6996ed6 --- /dev/null +++ b/test/rails_apps/devise/app/models/staff.rb @@ -0,0 +1,6 @@ +class Staff < ActiveRecord::Base + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :trackable, :validatable +end diff --git a/test/rails_apps/devise/config/initializers/any_login.rb b/test/rails_apps/devise/config/initializers/any_login.rb index 414942b..b75f22f 100644 --- a/test/rails_apps/devise/config/initializers/any_login.rb +++ b/test/rails_apps/devise/config/initializers/any_login.rb @@ -2,6 +2,6 @@ config.enabled = true config.provider = :devise config.login_button_label = 'Sign in with Devise' - config.klass_name = 'User' + config.klass_name = ['User', 'Staff'] config.collection_method = :all end diff --git a/test/rails_apps/devise/config/routes.rb b/test/rails_apps/devise/config/routes.rb index cbaa9e1..ff0f252 100644 --- a/test/rails_apps/devise/config/routes.rb +++ b/test/rails_apps/devise/config/routes.rb @@ -1,5 +1,6 @@ Rails.application.routes.draw do devise_for :users + devise_for :staffs root to: "home#index" get '/about' => 'home#about' diff --git a/test/rails_apps/devise/db/migrate/20221019135010_create_staffs.rb b/test/rails_apps/devise/db/migrate/20221019135010_create_staffs.rb new file mode 100644 index 0000000..35886c1 --- /dev/null +++ b/test/rails_apps/devise/db/migrate/20221019135010_create_staffs.rb @@ -0,0 +1,9 @@ +class CreateStaffs < ActiveRecord::Migration[6.1] + def change + create_table :staffs do |t| + t.string :name + t.integer :age + t.timestamps + end + end +end diff --git a/test/rails_apps/devise/db/migrate/20221019135121_add_devise_to_staffs.rb b/test/rails_apps/devise/db/migrate/20221019135121_add_devise_to_staffs.rb new file mode 100644 index 0000000..1db9b58 --- /dev/null +++ b/test/rails_apps/devise/db/migrate/20221019135121_add_devise_to_staffs.rb @@ -0,0 +1,47 @@ +class AddDeviseToStaffs < ActiveRecord::Migration[6.1] + def self.up + change_table(:staffs) do |t| + ## Database authenticatable + t.string :email, null: false, default: "" + t.string :encrypted_password, null: false, default: "" + + ## Recoverable + t.string :reset_password_token + t.datetime :reset_password_sent_at + + ## Rememberable + t.datetime :remember_created_at + + ## Trackable + t.integer :sign_in_count, default: 0, null: false + t.datetime :current_sign_in_at + t.datetime :last_sign_in_at + t.string :current_sign_in_ip + t.string :last_sign_in_ip + + ## Confirmable + # t.string :confirmation_token + # t.datetime :confirmed_at + # t.datetime :confirmation_sent_at + # t.string :unconfirmed_email # Only if using reconfirmable + + ## Lockable + # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts + # t.string :unlock_token # Only if unlock strategy is :email or :both + # t.datetime :locked_at + + + # Uncomment below if timestamps were not included in your original model. + # t.timestamps null: false + end + + add_index :staffs, :email, unique: true + add_index :staffs, :reset_password_token, unique: true + end + + def self.down + # By default, we don't want to make any assumption about how to roll back a migration when your + # model already existed. Please edit below which fields you would like to remove in this migration. + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/test/rails_apps/devise/db/migrate/20221019135937_populate_test_staffs.rb b/test/rails_apps/devise/db/migrate/20221019135937_populate_test_staffs.rb new file mode 100644 index 0000000..d332198 --- /dev/null +++ b/test/rails_apps/devise/db/migrate/20221019135937_populate_test_staffs.rb @@ -0,0 +1,21 @@ +class PopulateTestStaffs < ActiveRecord::Migration[6.1] + def change + add_column :staffs, :role, :string + 100.times { |t| Staff.create(name: gen_name(t), age: rand(100), email: gen_email(t), role: gen_role, password: '12345678') } + end + + private + + def gen_name(number) + ['Igor', 'Michael' 'John', 'Stan', 'Bob', 'Kris', 'Alan'].sample + ' ' + number.to_s + end + + def gen_email(number) + email = ['Igor', 'Michael' 'John', 'Stan', 'Bob', 'Kris', 'Alan'].sample + number.to_s + '@gmail.com' + email.downcase + end + + def gen_role + ['admin', 'user', 'moderator'].sample + end +end diff --git a/test/rails_apps/devise/db/schema.rb b/test/rails_apps/devise/db/schema.rb index c7a86ec..23cabe8 100644 --- a/test/rails_apps/devise/db/schema.rb +++ b/test/rails_apps/devise/db/schema.rb @@ -10,7 +10,27 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2015_09_09_210357) do +ActiveRecord::Schema.define(version: 2022_10_19_135937) do + + create_table "staffs", force: :cascade do |t| + t.string "name" + t.integer "age" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.integer "sign_in_count", default: 0, null: false + t.datetime "current_sign_in_at" + t.datetime "last_sign_in_at" + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.string "role" + t.index ["email"], name: "index_staffs_on_email", unique: true + t.index ["reset_password_token"], name: "index_staffs_on_reset_password_token", unique: true + end create_table "users", force: :cascade do |t| t.string "name" diff --git a/test/rails_apps/devise/test/fixtures/staffs.yml b/test/rails_apps/devise/test/fixtures/staffs.yml new file mode 100644 index 0000000..5181636 --- /dev/null +++ b/test/rails_apps/devise/test/fixtures/staffs.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/rails_apps/devise/test/models/staff_test.rb b/test/rails_apps/devise/test/models/staff_test.rb new file mode 100644 index 0000000..211ae53 --- /dev/null +++ b/test/rails_apps/devise/test/models/staff_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class StaffTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/sorcery/integration/navigation_test.rb b/test/sorcery/integration/navigation_test.rb index 88b42eb..f6442be 100644 --- a/test/sorcery/integration/navigation_test.rb +++ b/test/sorcery/integration/navigation_test.rb @@ -14,8 +14,8 @@ class NavigationTest < ActionDispatch::IntegrationTest test "it properly logs users in and allows access to the secret page" do visit "/" - find("#any_login_form_toggle_label").click - find('#selected_id option:last-of-type').select_option + find(".any_login_form_toggle_label").click + find("select option:last-of-type", match: :first).select_option visit "/about"