Commit 6c4f6400dbf5daea661eb9eef5d5369573f77f56
1 parent
f078e729
Exists in
federation-webfinger
Makes ExternalProfile respond to all Profile methods to avoid breakage
Showing
4 changed files
with
221 additions
and
131 deletions
Show diff stats
app/models/concerns/profile_entity.rb
@@ -12,6 +12,8 @@ module ProfileEntity | @@ -12,6 +12,8 @@ module ProfileEntity | ||
12 | 12 | ||
13 | before_create :set_default_environment | 13 | before_create :set_default_environment |
14 | 14 | ||
15 | + scope :recent, -> limit=nil { order('id DESC').limit(limit) } | ||
16 | + | ||
15 | end | 17 | end |
16 | 18 | ||
17 | def disable | 19 | def disable |
@@ -65,4 +67,77 @@ module ProfileEntity | @@ -65,4 +67,77 @@ module ProfileEntity | ||
65 | end | 67 | end |
66 | end | 68 | end |
67 | 69 | ||
70 | + def to_liquid | ||
71 | + HashWithIndifferentAccess.new :name => name, :identifier => identifier | ||
72 | + end | ||
73 | + | ||
74 | + # Tells whether a specified profile has members or nor. | ||
75 | + # | ||
76 | + # On this class, returns <tt>false</tt> by default. | ||
77 | + def has_members? | ||
78 | + false | ||
79 | + end | ||
80 | + | ||
81 | + def apply_type_specific_template(template) | ||
82 | + end | ||
83 | + | ||
84 | + # Override this method in subclasses of Profile to create a default article | ||
85 | + # set upon creation. Note that this method will be called *only* if there is | ||
86 | + # no template for the type of profile (i.e. if the template was removed or in | ||
87 | + # the creation of the template itself). | ||
88 | + # | ||
89 | + # This method must return an array of pre-populated articles, which will be | ||
90 | + # associated to the profile before being saved. Example: | ||
91 | + # | ||
92 | + # def default_set_of_articles | ||
93 | + # [Blog.new(:name => 'Blog'), Gallery.new(:name => 'Gallery')] | ||
94 | + # end | ||
95 | + # | ||
96 | + # By default, this method returns an empty array. | ||
97 | + def default_set_of_articles | ||
98 | + [] | ||
99 | + end | ||
100 | + | ||
101 | + def blocks_to_expire_cache | ||
102 | + [] | ||
103 | + end | ||
104 | + | ||
105 | + def cache_keys(params = {}) | ||
106 | + [] | ||
107 | + end | ||
108 | + | ||
109 | + def members_cache_key(params = {}) | ||
110 | + page = params[:npage] || '1' | ||
111 | + sort = (params[:sort] == 'desc') ? params[:sort] : 'asc' | ||
112 | + cache_key + '-members-page-' + page + '-' + sort | ||
113 | + end | ||
114 | + | ||
115 | + def more_recent_label | ||
116 | + _("Since: ") | ||
117 | + end | ||
118 | + | ||
119 | + def control_panel_settings_button | ||
120 | + {:title => _('Edit Profile'), :icon => 'edit-profile'} | ||
121 | + end | ||
122 | + | ||
123 | + def control_panel_settings_button | ||
124 | + {:title => _('Profile Info and settings'), :icon => 'edit-profile'} | ||
125 | + end | ||
126 | + | ||
127 | + def exclude_verbs_on_activities | ||
128 | + %w[] | ||
129 | + end | ||
130 | + | ||
131 | + def allow_invitation_from(person) | ||
132 | + false | ||
133 | + end | ||
134 | + | ||
135 | + module ClassMethods | ||
136 | + | ||
137 | + def identification | ||
138 | + name | ||
139 | + end | ||
140 | + | ||
141 | + end | ||
142 | + | ||
68 | end | 143 | end |
app/models/external_person.rb
@@ -23,87 +23,92 @@ class ExternalPerson < ActiveRecord::Base | @@ -23,87 +23,92 @@ class ExternalPerson < ActiveRecord::Base | ||
23 | user | 23 | user |
24 | end | 24 | end |
25 | 25 | ||
26 | - def url | ||
27 | - "http://#{self.source}/#{self.identifier}" | 26 | + def privacy_setting |
27 | + _('Public profile') | ||
28 | end | 28 | end |
29 | 29 | ||
30 | - alias :public_profile_url :url | ||
31 | - | ||
32 | def avatar | 30 | def avatar |
33 | "http://#{self.source}/profile/#{self.identifier}/icon/" | 31 | "http://#{self.source}/profile/#{self.identifier}/icon/" |
34 | end | 32 | end |
35 | 33 | ||
34 | + def url | ||
35 | + "http://#{self.source}/#{self.identifier}" | ||
36 | + end | ||
37 | + | ||
38 | + alias :public_profile_url :url | ||
39 | + | ||
36 | def admin_url | 40 | def admin_url |
37 | "http://#{self.source}/myprofile/#{self.identifier}" | 41 | "http://#{self.source}/myprofile/#{self.identifier}" |
38 | end | 42 | end |
39 | 43 | ||
40 | - def profile_custom_icon(gravatar_default=nil) | ||
41 | - self.avatar | 44 | + def tasks_url |
45 | + self.url | ||
42 | end | 46 | end |
43 | - | ||
44 | - def preferred_login_redirection | ||
45 | - environment.redirection_after_login | 47 | + def leave_url(reload = false) |
48 | + self.url | ||
46 | end | 49 | end |
47 | - | ||
48 | - def person? | ||
49 | - true | 50 | + def join_url |
51 | + self.url | ||
50 | end | 52 | end |
51 | - | ||
52 | - def is_admin?(environment = nil) | ||
53 | - false | 53 | + def join_not_logged_url |
54 | + self.url | ||
54 | end | 55 | end |
55 | - | ||
56 | - def lat | ||
57 | - nil | 56 | + def check_membership_url |
57 | + self.url | ||
58 | end | 58 | end |
59 | - def lng | ||
60 | - nil | 59 | + def add_url |
60 | + self.url | ||
61 | end | 61 | end |
62 | - | ||
63 | - def role_assignments | ||
64 | - RoleAssignment.none | 62 | + def check_friendship_url |
63 | + self.url | ||
65 | end | 64 | end |
66 | - | ||
67 | - def favorite_enterprises | ||
68 | - Enterprise.none | 65 | + def people_suggestions_url |
66 | + self.url | ||
69 | end | 67 | end |
70 | - | ||
71 | - def memberships | ||
72 | - Profile.none | 68 | + def communities_suggestions_url |
69 | + self.url | ||
70 | + end | ||
71 | + def top_url(scheme = 'http') | ||
72 | + "#{scheme}://#{self.source}" | ||
73 | end | 73 | end |
74 | 74 | ||
75 | - def friendships | ||
76 | - Profile.none | 75 | + def profile_custom_icon(gravatar_default=nil) |
76 | + self.avatar | ||
77 | end | 77 | end |
78 | 78 | ||
79 | - def tasks | ||
80 | - Task.none | 79 | + def preferred_login_redirection |
80 | + environment.redirection_after_login | ||
81 | end | 81 | end |
82 | 82 | ||
83 | - def suggested_profiles | ||
84 | - ProfileSuggestion.none | 83 | + def location |
84 | + self.source | ||
85 | end | 85 | end |
86 | - def suggested_people | ||
87 | - ProfileSuggestion.none | 86 | + |
87 | + def default_hostname | ||
88 | + environment.default_hostname | ||
88 | end | 89 | end |
89 | - def suggested_communities | ||
90 | - ProfileSuggestion.none | 90 | + |
91 | + def possible_domains | ||
92 | + environment.domains | ||
91 | end | 93 | end |
92 | 94 | ||
93 | - def add_friend(friend, group = nil) | ||
94 | - false | 95 | + def person? |
96 | + true | ||
95 | end | 97 | end |
96 | 98 | ||
97 | - def follows?(profile) | ||
98 | - false | 99 | + def contact_email(*args) |
100 | + self.email | ||
99 | end | 101 | end |
100 | 102 | ||
101 | - def is_a_friend?(person) | ||
102 | - false | 103 | + def notification_emails |
104 | + [self.contact_email] | ||
103 | end | 105 | end |
104 | 106 | ||
105 | - def already_request_friendship?(person) | ||
106 | - false | 107 | + def jid(options = {}) |
108 | + "#{self.identifier}@#{self.source}" | ||
109 | + end | ||
110 | + def full_jid(options = {}) | ||
111 | + "#{jid(options)}/#{self.name}" | ||
107 | end | 112 | end |
108 | 113 | ||
109 | class ExternalPerson::Image | 114 | class ExternalPerson::Image |
@@ -126,18 +131,6 @@ class ExternalPerson < ActiveRecord::Base | @@ -126,18 +131,6 @@ class ExternalPerson < ActiveRecord::Base | ||
126 | ExternalPerson::Image.new(avatar) | 131 | ExternalPerson::Image.new(avatar) |
127 | end | 132 | end |
128 | 133 | ||
129 | - def is_template? | ||
130 | - false | ||
131 | - end | ||
132 | - | ||
133 | - def enterprises | ||
134 | - [] | ||
135 | - end | ||
136 | - | ||
137 | - def friends | ||
138 | - [] | ||
139 | - end | ||
140 | - | ||
141 | def data_hash(gravatar_default = nil) | 134 | def data_hash(gravatar_default = nil) |
142 | friends_list = {} | 135 | friends_list = {} |
143 | { | 136 | { |
@@ -155,4 +148,86 @@ class ExternalPerson < ActiveRecord::Base | @@ -155,4 +148,86 @@ class ExternalPerson < ActiveRecord::Base | ||
155 | 'chat_enabled' => false | 148 | 'chat_enabled' => false |
156 | } | 149 | } |
157 | end | 150 | end |
151 | + | ||
152 | + # External Person should respond to all methods in Person and Profile | ||
153 | + def person_instance_methods | ||
154 | + {} | ||
155 | + end | ||
156 | + | ||
157 | + def profile_instance_methods | ||
158 | + methods_and_responses = { | ||
159 | + role_assignments: RoleAssignment.none, favorite_enterprises: | ||
160 | + Enterprise.none, enterprises: Enterprise.none, memberships: Profile.none, | ||
161 | + friendships: Profile.none, friends: Profile.none, tasks: Task.none, | ||
162 | + suggested_profiles: ProfileSuggestion.none, suggested_people: | ||
163 | + ProfileSuggestion.none, suggested_communities: ProfileSuggestion.none, | ||
164 | + public_profile: true, nickname: nil, custom_footer: '', custom_header: '', | ||
165 | + address: '', zip_code: '', contact_phone: '', image_builder: nil, | ||
166 | + description: '', closed: false, template_id: nil, lat: nil, lng: nil, | ||
167 | + is_template: false, fields_privacy: {}, preferred_domain_id: nil, | ||
168 | + category_ids: [], country: '', city: '', state: '', national_region_code: | ||
169 | + '', redirect_l10n: false, notification_time: 0, custom_url_redirection: | ||
170 | + nil, email_suggestions: false, allow_members_to_invite: false, | ||
171 | + invite_friends_only: false, secret: false, profile_admin_mail_notification: | ||
172 | + false, redirection_after_login: nil, profile_activities: | ||
173 | + ProfileActivity.none, action_tracker_notifications: | ||
174 | + ActionTrackerNotification.none, tracked_notifications: | ||
175 | + ActionTracker::Record.none, scraps_received: Scrap.none, template: | ||
176 | + Profile.none, comments_received: Comment.none, email_templates: | ||
177 | + EmailTemplate.none, members: Profile.none, members_like: Profile.none, | ||
178 | + members_by: Profile.none, members_by_role: Profile.none, scraps: | ||
179 | + Scrap.none, welcome_page_content: nil, settings: {}, find_in_all_tasks: | ||
180 | + nil, top_level_categorization: {}, interests: Category.none, geolocation: | ||
181 | + '', country_name: '', pending_categorizations: [], add_category: false, | ||
182 | + create_pending_categorizations: false, top_level_articles: Article.none, | ||
183 | + valid_identifier: true, valid_template: false, create_default_set_of_boxes: | ||
184 | + true, copy_blocks_from: nil, default_template: nil, | ||
185 | + template_without_default: nil, template_with_default: nil, apply_template: | ||
186 | + false, iframe_whitelist: [], recent_documents: Article.none, last_articles: | ||
187 | + Article.none, is_validation_entity?: false, hostname: nil, own_hostname: | ||
188 | + nil, article_tags: {}, tagged_with: Article.none, | ||
189 | + insert_default_article_set: false, copy_articles_from: true, | ||
190 | + copy_article_tree: nil, copy_article?: false, add_member: false, | ||
191 | + remove_member: false, add_admin: false, remove_admin: false, add_moderator: | ||
192 | + false, display_info_to?: true, update_category_from_region: nil, | ||
193 | + accept_category?: false, custom_header_expanded: '', | ||
194 | + custom_footer_expanded: '', public?: true, themes: [], find_theme: nil, | ||
195 | + blogs: Blog.none, blog: nil, has_blog?: false, forums: Forum.none, forum: | ||
196 | + nil, has_forum?: false, admins: [], settings_field: {}, setting_changed: | ||
197 | + false, public_content: true, enable_contact?: false, folder_types: [], | ||
198 | + folders: Article.none, image_galleries: Article.none, image_valid: true, | ||
199 | + update_header_and_footer: nil, update_theme: nil, update_layout_template: | ||
200 | + nil, recent_actions: ActionTracker::Record.none, recent_notifications: | ||
201 | + ActionTracker::Record.none, more_active_label: _('no activity'), | ||
202 | + more_popular_label: _('no members'), profile_custom_image: nil, | ||
203 | + is_on_homepage?: false, activities: ProfileActivity.none, | ||
204 | + may_display_field_to?: true, may_display_location_to?: true, public_fields: | ||
205 | + {}, followed_by?: false, display_private_info_to?: true, can_view_field?: | ||
206 | + true, remove_from_suggestion_list: nil, layout_template: 'default', | ||
207 | + is_admin?: false, add_friend: false, follows?: false, is_a_friend?: false, | ||
208 | + already_request_friendship?: false | ||
209 | + } | ||
210 | + | ||
211 | + derivated_methods = {} | ||
212 | + methods_and_responses.keys.each do |method| | ||
213 | + derivated_methods[method.to_s.insert(-1, '?').to_sym] = false | ||
214 | + derivated_methods[method.to_s.insert(-1, '=').to_sym] = nil | ||
215 | + end | ||
216 | + derivated_methods.merge(methods_and_responses) | ||
217 | + end | ||
218 | + | ||
219 | + def method_missing(method, *args, &block) | ||
220 | + if person_instance_methods.keys.include?(method) | ||
221 | + return person_instance_methods[method] | ||
222 | + end | ||
223 | + if profile_instance_methods.keys.include? method | ||
224 | + return profile_instance_methods[method] | ||
225 | + end | ||
226 | + end | ||
227 | + | ||
228 | + def respond_to_missing?(method_name, include_private = false) | ||
229 | + person_instance_methods.keys.include?(method_name) || | ||
230 | + profile_instance_methods.keys.include?(method_name) || | ||
231 | + super | ||
232 | + end | ||
158 | end | 233 | end |
app/models/profile.rb
@@ -116,8 +116,6 @@ class Profile < ApplicationRecord | @@ -116,8 +116,6 @@ class Profile < ApplicationRecord | ||
116 | } | 116 | } |
117 | scope :no_templates, -> { where is_template: false } | 117 | scope :no_templates, -> { where is_template: false } |
118 | 118 | ||
119 | - scope :recent, -> limit=nil { order('id DESC').limit(limit) } | ||
120 | - | ||
121 | 119 | ||
122 | # Returns a scoped object to select profiles in a given location or in a radius | 120 | # Returns a scoped object to select profiles in a given location or in a radius |
123 | # distance from the given location center. | 121 | # distance from the given location center. |
@@ -483,9 +481,6 @@ class Profile < ApplicationRecord | @@ -483,9 +481,6 @@ class Profile < ApplicationRecord | ||
483 | self.save(:validate => false) | 481 | self.save(:validate => false) |
484 | end | 482 | end |
485 | 483 | ||
486 | - def apply_type_specific_template(template) | ||
487 | - end | ||
488 | - | ||
489 | xss_terminate :only => [ :name, :nickname, :address, :contact_phone, :description ], :on => 'validation' | 484 | xss_terminate :only => [ :name, :nickname, :address, :contact_phone, :description ], :on => 'validation' |
490 | xss_terminate :only => [ :custom_footer, :custom_header ], :with => 'white_list' | 485 | xss_terminate :only => [ :custom_footer, :custom_header ], :with => 'white_list' |
491 | 486 | ||
@@ -526,10 +521,6 @@ class Profile < ApplicationRecord | @@ -526,10 +521,6 @@ class Profile < ApplicationRecord | ||
526 | ).order('articles.published_at desc, articles.id desc') | 521 | ).order('articles.published_at desc, articles.id desc') |
527 | end | 522 | end |
528 | 523 | ||
529 | - def to_liquid | ||
530 | - HashWithIndifferentAccess.new :name => name, :identifier => identifier | ||
531 | - end | ||
532 | - | ||
533 | class << self | 524 | class << self |
534 | 525 | ||
535 | # finds a profile by its identifier. This method is a shortcut to | 526 | # finds a profile by its identifier. This method is a shortcut to |
@@ -653,13 +644,6 @@ private :generate_url, :url_options | @@ -653,13 +644,6 @@ private :generate_url, :url_options | ||
653 | self.articles.tagged_with(tag) | 644 | self.articles.tagged_with(tag) |
654 | end | 645 | end |
655 | 646 | ||
656 | - # Tells whether a specified profile has members or nor. | ||
657 | - # | ||
658 | - # On this class, returns <tt>false</tt> by default. | ||
659 | - def has_members? | ||
660 | - false | ||
661 | - end | ||
662 | - | ||
663 | after_create :insert_default_article_set | 647 | after_create :insert_default_article_set |
664 | def insert_default_article_set | 648 | def insert_default_article_set |
665 | if template | 649 | if template |
@@ -674,23 +658,6 @@ private :generate_url, :url_options | @@ -674,23 +658,6 @@ private :generate_url, :url_options | ||
674 | end | 658 | end |
675 | end | 659 | end |
676 | 660 | ||
677 | - # Override this method in subclasses of Profile to create a default article | ||
678 | - # set upon creation. Note that this method will be called *only* if there is | ||
679 | - # no template for the type of profile (i.e. if the template was removed or in | ||
680 | - # the creation of the template itself). | ||
681 | - # | ||
682 | - # This method must return an array of pre-populated articles, which will be | ||
683 | - # associated to the profile before being saved. Example: | ||
684 | - # | ||
685 | - # def default_set_of_articles | ||
686 | - # [Blog.new(:name => 'Blog'), Gallery.new(:name => 'Gallery')] | ||
687 | - # end | ||
688 | - # | ||
689 | - # By default, this method returns an empty array. | ||
690 | - def default_set_of_articles | ||
691 | - [] | ||
692 | - end | ||
693 | - | ||
694 | def copy_articles_from other | 661 | def copy_articles_from other |
695 | return false if other.top_level_articles.empty? | 662 | return false if other.top_level_articles.empty? |
696 | other.top_level_articles.each do |a| | 663 | other.top_level_articles.each do |a| |
@@ -891,14 +858,6 @@ private :generate_url, :url_options | @@ -891,14 +858,6 @@ private :generate_url, :url_options | ||
891 | articles.galleries | 858 | articles.galleries |
892 | end | 859 | end |
893 | 860 | ||
894 | - def blocks_to_expire_cache | ||
895 | - [] | ||
896 | - end | ||
897 | - | ||
898 | - def cache_keys(params = {}) | ||
899 | - [] | ||
900 | - end | ||
901 | - | ||
902 | validate :image_valid | 861 | validate :image_valid |
903 | 862 | ||
904 | def image_valid | 863 | def image_valid |
@@ -935,16 +894,6 @@ private :generate_url, :url_options | @@ -935,16 +894,6 @@ private :generate_url, :url_options | ||
935 | self.update_attribute(:layout_template, template) | 894 | self.update_attribute(:layout_template, template) |
936 | end | 895 | end |
937 | 896 | ||
938 | - def members_cache_key(params = {}) | ||
939 | - page = params[:npage] || '1' | ||
940 | - sort = (params[:sort] == 'desc') ? params[:sort] : 'asc' | ||
941 | - cache_key + '-members-page-' + page + '-' + sort | ||
942 | - end | ||
943 | - | ||
944 | - def more_recent_label | ||
945 | - _("Since: ") | ||
946 | - end | ||
947 | - | ||
948 | def recent_actions | 897 | def recent_actions |
949 | tracked_actions.recent | 898 | tracked_actions.recent |
950 | end | 899 | end |
@@ -999,18 +948,6 @@ private :generate_url, :url_options | @@ -999,18 +948,6 @@ private :generate_url, :url_options | ||
999 | end | 948 | end |
1000 | end | 949 | end |
1001 | 950 | ||
1002 | - def control_panel_settings_button | ||
1003 | - {:title => _('Edit Profile'), :icon => 'edit-profile'} | ||
1004 | - end | ||
1005 | - | ||
1006 | - def self.identification | ||
1007 | - name | ||
1008 | - end | ||
1009 | - | ||
1010 | - def exclude_verbs_on_activities | ||
1011 | - %w[] | ||
1012 | - end | ||
1013 | - | ||
1014 | # Customize in subclasses | 951 | # Customize in subclasses |
1015 | def activities | 952 | def activities |
1016 | self.profile_activities.includes(:activity).order('updated_at DESC') | 953 | self.profile_activities.includes(:activity).order('updated_at DESC') |
@@ -1045,10 +982,6 @@ private :generate_url, :url_options | @@ -1045,10 +982,6 @@ private :generate_url, :url_options | ||
1045 | self.active_fields | 982 | self.active_fields |
1046 | end | 983 | end |
1047 | 984 | ||
1048 | - def control_panel_settings_button | ||
1049 | - {:title => _('Profile Info and settings'), :icon => 'edit-profile'} | ||
1050 | - end | ||
1051 | - | ||
1052 | def followed_by?(person) | 985 | def followed_by?(person) |
1053 | person.is_member_of?(self) | 986 | person.is_member_of?(self) |
1054 | end | 987 | end |
@@ -1076,8 +1009,4 @@ private :generate_url, :url_options | @@ -1076,8 +1009,4 @@ private :generate_url, :url_options | ||
1076 | suggestion.disable if suggestion | 1009 | suggestion.disable if suggestion |
1077 | end | 1010 | end |
1078 | 1011 | ||
1079 | - def allow_invitation_from(person) | ||
1080 | - false | ||
1081 | - end | ||
1082 | - | ||
1083 | end | 1012 | end |
test/unit/external_person_test.rb
1 | # encoding: UTF-8 | 1 | # encoding: UTF-8 |
2 | require_relative "../test_helper" | 2 | require_relative "../test_helper" |
3 | 3 | ||
4 | -class PersonTest < ActiveSupport::TestCase | 4 | +class ExternalPersonTest < ActiveSupport::TestCase |
5 | fixtures :environments | 5 | fixtures :environments |
6 | 6 | ||
7 | def setup | 7 | def setup |
@@ -101,4 +101,15 @@ class PersonTest < ActiveSupport::TestCase | @@ -101,4 +101,15 @@ class PersonTest < ActiveSupport::TestCase | ||
101 | 101 | ||
102 | should 'profile image has public filename and mimetype' do | 102 | should 'profile image has public filename and mimetype' do |
103 | end | 103 | end |
104 | + | ||
105 | + should 'respond to all instance methods in Profile' do | ||
106 | + methods = Profile.public_instance_methods(false) | ||
107 | + methods.each do |method| | ||
108 | + # We test if ExternalPerson responds to same methods as Profile, but we | ||
109 | + # skip methods generated by plugins, libs and validations, which are | ||
110 | + # usually only used internally | ||
111 | + assert_respond_to ExternalPerson.new, method.to_sym unless method =~ /type_name|^autosave_.*|^after_.*|^before_.*|validate_.*|^attribute_.*|.*_?tags?_?.*|^custom_value.*|^custom_context.*|^xss.*|bar/ | ||
112 | + end | ||
113 | + end | ||
114 | + | ||
104 | end | 115 | end |