diff --git a/app/models/concerns/has_custom_fields.rb b/app/models/concerns/has_custom_fields.rb index f6b926198..59cca8891 100644 --- a/app/models/concerns/has_custom_fields.rb +++ b/app/models/concerns/has_custom_fields.rb @@ -5,6 +5,31 @@ module HasCustomFields included do has_many :_custom_fields, dependent: :destroy, :class_name => "#{name}CustomField" after_save :save_custom_fields + + # To avoid n+1 queries, we have this function to retrieve lots of custom fields in one + # go and create a "sideloaded" version for easy querying by id. + def self.custom_fields_for_ids(ids, whitelisted_fields) + klass = "#{name}CustomField".constantize + foreign_key = "#{name.underscore}_id".to_sym + + result = {} + + return result if whitelisted_fields.blank? + klass.where(foreign_key => ids, :name => whitelisted_fields).pluck(foreign_key, :name, :value).each do |cf| + result[cf[0]] ||= {} + unload_field(result[cf[0]], cf[1], cf[2]) + end + result + end + + def self.unload_field(target, key, value) + if target.has_key?(key) + target[key] = [target[key]] if !target[key].is_a? Array + target[key] << value + else + target[key] = value + end + end end def reload(options = nil) @@ -32,14 +57,7 @@ module HasCustomFields def refresh_custom_fields_from_db target = Hash.new _custom_fields.pluck(:name,:value).each do |key, value| - if target.has_key? key - if !target[key].is_a? Array - target[key] = [target[key]] - end - target[key] << value - else - target[key] = value - end + self.class.unload_field(target, key, value) end @custom_fields_orig = target @custom_fields = @custom_fields_orig.dup @@ -90,4 +108,4 @@ module HasCustomFields refresh_custom_fields_from_db end end -end \ No newline at end of file +end diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb index 907afc803..d26e26987 100644 --- a/app/serializers/post_serializer.rb +++ b/app/serializers/post_serializer.rb @@ -48,8 +48,8 @@ class PostSerializer < BasicPostSerializer :user_deleted, :edit_reason, :can_view_edit_history, - :wiki - + :wiki, + :user_custom_fields def moderator? !!(object.user && object.user.moderator?) @@ -216,6 +216,16 @@ class PostSerializer < BasicPostSerializer scope.can_view_post_revisions?(object) end + def user_custom_fields + @topic_view.user_custom_fields[object.user_id] + end + + def include_user_custom_fields? + return if @topic_view.blank? + custom_fields = @topic_view.user_custom_fields + custom_fields && custom_fields[object.user_id] + end + private def post_actions diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index ccd9a12f0..8f7726cdc 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -861,6 +861,7 @@ en: detect_custom_avatars: "Whether or not to check that users have uploaded custom avatars" max_daily_gravatar_crawls: "The maximum amount of times Discourse will check gravatar for custom avatars in a day" + public_user_custom_fields: "A whitelist of custom fields for a user that can be shown publically." allow_profile_backgrounds: "Allows users to upload profile backgrounds" diff --git a/config/site_settings.yml b/config/site_settings.yml index 04eeee26c..221685f1c 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -530,3 +530,7 @@ uncategorized: hidden: true notify_about_flags_after: 48 + + public_user_custom_fields: + list: true + default: '' diff --git a/lib/topic_view.rb b/lib/topic_view.rb index 2aa0ad3cb..936fe77c0 100644 --- a/lib/topic_view.rb +++ b/lib/topic_view.rb @@ -7,7 +7,7 @@ require_dependency 'gaps' class TopicView attr_reader :topic, :posts, :guardian, :filtered_posts - attr_accessor :draft, :draft_key, :draft_sequence + attr_accessor :draft, :draft_key, :draft_sequence, :user_custom_fields def initialize(topic_id, user=nil, options={}) @user = user @@ -30,6 +30,10 @@ class TopicView filter_posts(options) + if SiteSetting.public_user_custom_fields.present? && @posts + @user_custom_fields = User.custom_fields_for_ids(@posts.map(&:user_id), SiteSetting.public_user_custom_fields.split('|')) + end + @draft_key = @topic.draft_key @draft_sequence = DraftSequence.current(@user, @draft_key) end diff --git a/spec/components/concern/has_custom_fields_spec.rb b/spec/components/concern/has_custom_fields_spec.rb index e37e9a72a..c137cc47e 100644 --- a/spec/components/concern/has_custom_fields_spec.rb +++ b/spec/components/concern/has_custom_fields_spec.rb @@ -152,5 +152,21 @@ describe HasCustomFields do test_item.custom_fields.should == {"jack" => "black", "bob" => "marley"} test_item2.custom_fields.should == {"sixto" => "rodriguez", "de" => "la playa"} end + + it "supports bulk retrieval with a list of ids" do + item1 = CustomFieldsTestItem.new + item1.custom_fields = {"a" => ["b", "c", "d"], 'not_whitelisted' => 'secret'} + item1.save + + item2 = CustomFieldsTestItem.new + item2.custom_fields = {"e" => 'hallo'} + item2.save + + fields = CustomFieldsTestItem.custom_fields_for_ids([item1.id, item2.id], ['a', 'e']) + fields.should be_present + fields[item1.id]['a'].should =~ ['b', 'c', 'd'] + fields[item1.id]['not_whitelisted'].should be_blank + fields[item2.id]['e'].should == 'hallo' + end end end