mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 22:43:33 +08:00
FEATURE: custom fields on User
This commit is contained in:
@ -8,10 +8,10 @@ end
|
|||||||
#
|
#
|
||||||
# Table name: badge_types
|
# Table name: badge_types
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# name :string(255) not null
|
# name :string(255) not null
|
||||||
# created_at :datetime
|
# created_at :datetime
|
||||||
# updated_at :datetime
|
# updated_at :datetime
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
@ -60,6 +60,10 @@ class DiscourseSingleSignOn < SingleSignOn
|
|||||||
user.enqueue_welcome_message('welcome_user')
|
user.enqueue_welcome_message('welcome_user')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
custom_fields.each do |k,v|
|
||||||
|
user.custom_fields[k] = v
|
||||||
|
end
|
||||||
|
|
||||||
# optionally save the user and sso_record if they have changed
|
# optionally save the user and sso_record if they have changed
|
||||||
user.save!
|
user.save!
|
||||||
sso_record.save!
|
sso_record.save!
|
||||||
|
@ -32,6 +32,7 @@ class User < ActiveRecord::Base
|
|||||||
has_many :invites, dependent: :destroy
|
has_many :invites, dependent: :destroy
|
||||||
has_many :topic_links, dependent: :destroy
|
has_many :topic_links, dependent: :destroy
|
||||||
has_many :uploads
|
has_many :uploads
|
||||||
|
has_many :user_custom_fields, dependent: :destroy
|
||||||
|
|
||||||
has_one :facebook_user_info, dependent: :destroy
|
has_one :facebook_user_info, dependent: :destroy
|
||||||
has_one :twitter_user_info, dependent: :destroy
|
has_one :twitter_user_info, dependent: :destroy
|
||||||
@ -68,6 +69,7 @@ class User < ActiveRecord::Base
|
|||||||
|
|
||||||
after_save :update_tracked_topics
|
after_save :update_tracked_topics
|
||||||
after_save :clear_global_notice_if_needed
|
after_save :clear_global_notice_if_needed
|
||||||
|
after_save :save_custom_fields
|
||||||
|
|
||||||
after_create :create_email_token
|
after_create :create_email_token
|
||||||
after_create :create_user_stat
|
after_create :create_user_stat
|
||||||
@ -587,8 +589,35 @@ class User < ActiveRecord::Base
|
|||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def custom_fields
|
||||||
|
@custom_fields ||= begin
|
||||||
|
@custom_fields_orig = Hash[*user_custom_fields.pluck(:name,:value).flatten]
|
||||||
|
@custom_fields_orig.dup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
def save_custom_fields
|
||||||
|
if @custom_fields && @custom_fields_orig != @custom_fields
|
||||||
|
dup = @custom_fields.dup
|
||||||
|
|
||||||
|
user_custom_fields.each do |f|
|
||||||
|
if dup[f.name] != f.value
|
||||||
|
f.destroy
|
||||||
|
else
|
||||||
|
dup.remove[f.name]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
dup.each do |k,v|
|
||||||
|
user_custom_fields.create(name: k, value: v)
|
||||||
|
end
|
||||||
|
|
||||||
|
@custom_fields_orig = @custom_fields
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def cook
|
def cook
|
||||||
if bio_raw.present?
|
if bio_raw.present?
|
||||||
self.bio_cooked = PrettyText.cook(bio_raw, omit_nofollow: self.has_trust_level?(:leader)) if bio_raw_changed?
|
self.bio_cooked = PrettyText.cook(bio_raw, omit_nofollow: self.has_trust_level?(:leader)) if bio_raw_changed?
|
||||||
|
19
app/models/user_custom_field.rb
Normal file
19
app/models/user_custom_field.rb
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
class UserCustomField < ActiveRecord::Base
|
||||||
|
belongs_to :user
|
||||||
|
end
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: user_custom_fields
|
||||||
|
#
|
||||||
|
# id :integer not null, primary key
|
||||||
|
# user_id :integer not null
|
||||||
|
# name :string(256) not null
|
||||||
|
# value :text
|
||||||
|
# created_at :datetime
|
||||||
|
# updated_at :datetime
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_user_custom_fields_on_user_id_and_name (user_id,name)
|
||||||
|
#
|
12
db/migrate/20140421235646_add_user_custom_fields.rb
Normal file
12
db/migrate/20140421235646_add_user_custom_fields.rb
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
class AddUserCustomFields < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :user_custom_fields do |t|
|
||||||
|
t.integer :user_id, null: false
|
||||||
|
t.string :name, limit: 256, null: false
|
||||||
|
t.text :value
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :user_custom_fields, [:user_id, :name]
|
||||||
|
end
|
||||||
|
end
|
@ -1,5 +1,7 @@
|
|||||||
class SingleSignOn
|
class SingleSignOn
|
||||||
ACCESSORS = [:nonce, :name, :username, :email, :about_me, :external_email, :external_username, :external_name, :external_id]
|
ACCESSORS = [:nonce, :name, :username, :email,
|
||||||
|
:about_me, :external_email, :external_username,
|
||||||
|
:external_name, :external_id]
|
||||||
FIXNUMS = []
|
FIXNUMS = []
|
||||||
NONCE_EXPIRY_TIME = 10.minutes
|
NONCE_EXPIRY_TIME = 10.minutes
|
||||||
|
|
||||||
@ -14,14 +16,6 @@ class SingleSignOn
|
|||||||
raise RuntimeError, "sso_url not implemented on class, be sure to set it on instance"
|
raise RuntimeError, "sso_url not implemented on class, be sure to set it on instance"
|
||||||
end
|
end
|
||||||
|
|
||||||
def sso_secret
|
|
||||||
@sso_secret || self.class.sso_secret
|
|
||||||
end
|
|
||||||
|
|
||||||
def sso_url
|
|
||||||
@sso_url || self.class.sso_url
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.parse(payload, sso_secret = nil)
|
def self.parse(payload, sso_secret = nil)
|
||||||
sso = new
|
sso = new
|
||||||
sso.sso_secret = sso_secret if sso_secret
|
sso.sso_secret = sso_secret if sso_secret
|
||||||
@ -39,9 +33,33 @@ class SingleSignOn
|
|||||||
val = val.to_i if FIXNUMS.include? k
|
val = val.to_i if FIXNUMS.include? k
|
||||||
sso.send("#{k}=", val)
|
sso.send("#{k}=", val)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
decoded_hash.each do |k,v|
|
||||||
|
# 1234567
|
||||||
|
# custom.
|
||||||
|
#
|
||||||
|
if k[0..6] == "custom."
|
||||||
|
field = k[7..-1]
|
||||||
|
sso.custom_fields[field] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
sso
|
sso
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sso_secret
|
||||||
|
@sso_secret || self.class.sso_secret
|
||||||
|
end
|
||||||
|
|
||||||
|
def sso_url
|
||||||
|
@sso_url || self.class.sso_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def custom_fields
|
||||||
|
@custom_fields ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def sign(payload)
|
def sign(payload)
|
||||||
OpenSSL::HMAC.hexdigest("sha256", sso_secret, payload)
|
OpenSSL::HMAC.hexdigest("sha256", sso_secret, payload)
|
||||||
end
|
end
|
||||||
@ -65,6 +83,12 @@ class SingleSignOn
|
|||||||
payload[k] = val
|
payload[k] = val
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if @custom_fields
|
||||||
|
@custom_fields.each do |k,v|
|
||||||
|
payload["custom.#{k}"] = v.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Rack::Utils.build_query(payload)
|
Rack::Utils.build_query(payload)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,7 +43,6 @@ describe SessionController do
|
|||||||
response.should redirect_to('/')
|
response.should redirect_to('/')
|
||||||
logged_on_user = Discourse.current_user_provider.new(request.env).current_user
|
logged_on_user = Discourse.current_user_provider.new(request.env).current_user
|
||||||
logged_on_user.email.should == user.email
|
logged_on_user.email.should == user.email
|
||||||
|
|
||||||
logged_on_user.single_sign_on_record.external_id.should == "abc"
|
logged_on_user.single_sign_on_record.external_id.should == "abc"
|
||||||
logged_on_user.single_sign_on_record.external_username.should == 'sam'
|
logged_on_user.single_sign_on_record.external_username.should == 'sam'
|
||||||
end
|
end
|
||||||
@ -54,12 +53,17 @@ describe SessionController do
|
|||||||
sso.email = 'bob@bob.com'
|
sso.email = 'bob@bob.com'
|
||||||
sso.name = 'Sam Saffron'
|
sso.name = 'Sam Saffron'
|
||||||
sso.username = 'sam'
|
sso.username = 'sam'
|
||||||
|
sso.custom_fields["shop_url"] = "http://my_shop.com"
|
||||||
|
sso.custom_fields["shop_name"] = "Sam"
|
||||||
|
|
||||||
get :sso_login, Rack::Utils.parse_query(sso.payload)
|
get :sso_login, Rack::Utils.parse_query(sso.payload)
|
||||||
response.should redirect_to('/a/')
|
response.should redirect_to('/a/')
|
||||||
|
|
||||||
logged_on_user = Discourse.current_user_provider.new(request.env).current_user
|
logged_on_user = Discourse.current_user_provider.new(request.env).current_user
|
||||||
|
|
||||||
|
# ensure nothing is transient
|
||||||
|
logged_on_user = User.find(logged_on_user.id)
|
||||||
|
|
||||||
logged_on_user.email.should == 'bob@bob.com'
|
logged_on_user.email.should == 'bob@bob.com'
|
||||||
logged_on_user.name.should == 'Sam Saffron'
|
logged_on_user.name.should == 'Sam Saffron'
|
||||||
logged_on_user.username.should == 'sam'
|
logged_on_user.username.should == 'sam'
|
||||||
@ -67,6 +71,9 @@ describe SessionController do
|
|||||||
logged_on_user.single_sign_on_record.external_id.should == "666"
|
logged_on_user.single_sign_on_record.external_id.should == "666"
|
||||||
logged_on_user.single_sign_on_record.external_username.should == 'sam'
|
logged_on_user.single_sign_on_record.external_username.should == 'sam'
|
||||||
logged_on_user.active.should == true
|
logged_on_user.active.should == true
|
||||||
|
logged_on_user.custom_fields["shop_url"].should == "http://my_shop.com"
|
||||||
|
logged_on_user.custom_fields["shop_name"].should == "Sam"
|
||||||
|
logged_on_user.custom_fields["bla"].should == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows login to existing account with valid nonce' do
|
it 'allows login to existing account with valid nonce' do
|
||||||
|
@ -19,6 +19,8 @@ describe DiscourseSingleSignOn do
|
|||||||
sso.username = "sam"
|
sso.username = "sam"
|
||||||
sso.name = "sam saffron"
|
sso.name = "sam saffron"
|
||||||
sso.external_id = "100"
|
sso.external_id = "100"
|
||||||
|
sso.custom_fields["a"] = "Aa"
|
||||||
|
sso.custom_fields["b.b"] = "B.b"
|
||||||
sso
|
sso
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -28,6 +30,8 @@ describe DiscourseSingleSignOn do
|
|||||||
parsed.username.should == sso.username
|
parsed.username.should == sso.username
|
||||||
parsed.name.should == sso.name
|
parsed.name.should == sso.name
|
||||||
parsed.external_id.should == sso.external_id
|
parsed.external_id.should == sso.external_id
|
||||||
|
parsed.custom_fields["a"].should == "Aa"
|
||||||
|
parsed.custom_fields["b.b"].should == "B.b"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can fill in data on way back" do
|
it "can fill in data on way back" do
|
||||||
|
@ -1149,4 +1149,29 @@ describe User do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "custom fields" do
|
||||||
|
it "allows modification of custom fields" do
|
||||||
|
user = Fabricate(:user)
|
||||||
|
|
||||||
|
user.custom_fields["a"].should == nil
|
||||||
|
|
||||||
|
user.custom_fields["bob"] = "marley"
|
||||||
|
user.custom_fields["jack"] = "black"
|
||||||
|
user.save
|
||||||
|
|
||||||
|
user = User.find(user.id)
|
||||||
|
|
||||||
|
user.custom_fields["bob"].should == "marley"
|
||||||
|
user.custom_fields["jack"].should == "black"
|
||||||
|
|
||||||
|
user.custom_fields.delete("bob")
|
||||||
|
user.custom_fields["jack"] = "jill"
|
||||||
|
|
||||||
|
user.save
|
||||||
|
user = User.find(user.id)
|
||||||
|
|
||||||
|
user.custom_fields.should == {"jack" => "jill"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user