Commit fa46a8f5fc858a9410e0f3b20753f5ea9e9710f2

Authored by Larissa Reis
2 parents 219aa665 f9165094

Merge branch 'external-person' into 'federation'

External person

Allow external users to login in a network that is federated with the user's network.

See merge request !951
Showing 53 changed files with 1289 additions and 275 deletions   Show diff stats
app/api/app.rb
1 require_dependency 'api/helpers' 1 require_dependency 'api/helpers'
2 2
3 module Api 3 module Api
  4 + class NoosferoFederation < Grape::API
  5 + use Rack::JSONP
  6 + helpers Helpers
  7 + before { detect_stuff_by_domain }
  8 + format :json
  9 + content_type :json, "application/jrd+json"
  10 + prefix [ENV['RAILS_RELATIVE_URL_ROOT'], ".well-known"].compact.join('/')
  11 + mount Federation::Webfinger
  12 + end
  13 +
4 class App < Grape::API 14 class App < Grape::API
5 use Rack::JSONP 15 use Rack::JSONP
6 16
@@ -23,6 +33,8 @@ module Api @@ -23,6 +33,8 @@ module Api
23 end 33 end
24 end 34 end
25 35
  36 + mount NoosferoFederation
  37 +
26 before { set_locale } 38 before { set_locale }
27 before { setup_multitenancy } 39 before { setup_multitenancy }
28 before { detect_stuff_by_domain } 40 before { detect_stuff_by_domain }
app/api/federation/webfinger.rb 0 → 100644
@@ -0,0 +1,108 @@ @@ -0,0 +1,108 @@
  1 +require 'rails/commands/server'
  2 +
  3 +module Api
  4 + module Federation
  5 + class Webfinger < Grape::API
  6 + get 'webfinger' do
  7 + result = generate_jrd
  8 + present result, with: Grape::Presenters::Presenter
  9 + end
  10 + end
  11 + end
  12 +end
  13 +
  14 +def generate_jrd
  15 + unless valid_domain?
  16 + not_found!
  17 + Rails.logger.error 'Domain Not Found'
  18 + end
  19 + if request_acct?
  20 + acct_hash
  21 + elsif valid_uri?(params[:resource])
  22 + uri_hash
  23 + end
  24 +end
  25 +
  26 +def domain
  27 + if request_acct?
  28 + params[:resource].split('@')[1]
  29 + else
  30 + params[:resource].split('/')[2]
  31 + end
  32 +end
  33 +
  34 +def valid_domain?
  35 + environment.domains.map(&:name).include? domain
  36 +end
  37 +
  38 +def request_acct?
  39 + params[:resource].include? 'acct:'
  40 +end
  41 +
  42 +def acct_hash
  43 + rails = Rails::Server.new
  44 + acct = Hash.new{|hash, key| hash[key] = Hash.new{|hash, key| hash[key] = Array.new}}
  45 + url = rails.options[:Host] + ':' + rails.options[:Port].to_s + '/'
  46 + person = Person.find_by_identifier(extract_person_identifier)
  47 +
  48 + if person.nil?
  49 + Rails.logger.error 'Person not found'
  50 + not_found!
  51 + else
  52 + acct[:subject] = params[:resource]
  53 + acct[:alias] = url + person.identifier
  54 + acct[:properties][:identifier] = person.identifier
  55 + acct[:properties][:created_at] = person.created_at
  56 + for blog in person.blogs do
  57 + acct[:links][:rel] << url + 'rel/' + blog.path
  58 + acct[:links][:href] << url + person.identifier + '/' + blog.path
  59 + end
  60 +
  61 + for galleries in person.articles.galleries do
  62 + acct[:links][:rel] << url + 'rel/' + galleries.path
  63 + acct[:links][:href] << url + person.identifier + '/' + galleries.path
  64 + end
  65 +
  66 + acct[:titles][:name] = person.name
  67 + end
  68 + acct
  69 +end
  70 +
  71 +def extract_person_identifier
  72 + params[:resource].split('@')[0].split(':')[1]
  73 +end
  74 +
  75 +def valid_uri?(url)
  76 + uri = URI.parse(url)
  77 + if uri.is_a?(URI::HTTP)
  78 + true
  79 + else
  80 + Rails.logger.error 'Bad URI Error'
  81 + not_found!
  82 + end
  83 +end
  84 +
  85 +def uri_hash
  86 + uri = {}
  87 + uri[:subject] = params[:resource]
  88 + entity = find_entity(params[:resource])
  89 + id = params[:resource].split('/').last.to_i
  90 + begin
  91 + uri[:properties] = entity.classify.constantize.find(id)
  92 + rescue ActiveRecord::RecordNotFound
  93 + Rails.logger.error "Entity: #{entity} with id: #{id} not found"
  94 + not_found!
  95 + end
  96 + uri
  97 +end
  98 +
  99 +def find_entity(uri)
  100 + possible_entity = uri.split('/')
  101 + possible_entity.map! { |entity| "#{entity}s" }
  102 + entity = (ActiveRecord::Base.connection.tables & possible_entity).first
  103 + unless entity
  104 + Rails.logger.error 'Entity not found on records'
  105 + not_found!
  106 + end
  107 + entity
  108 +end
app/api/helpers.rb
@@ -10,6 +10,9 @@ module Api @@ -10,6 +10,9 @@ module Api
10 include Noosfero::Plugin::HotSpot 10 include Noosfero::Plugin::HotSpot
11 include ForgotPasswordHelper 11 include ForgotPasswordHelper
12 include SearchTermHelper 12 include SearchTermHelper
  13 + include ProfileImageHelper
  14 + include Noosfero::Gravatar
  15 + include ThemeLoaderHelper
13 16
14 def set_locale 17 def set_locale
15 I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en') 18 I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
@@ -412,7 +415,7 @@ module Api @@ -412,7 +415,7 @@ module Api
412 content_type == 'TextArticle' ? Article.text_article_types : content_type 415 content_type == 'TextArticle' ? Article.text_article_types : content_type
413 end 416 end
414 content_types.flatten.uniq 417 content_types.flatten.uniq
415 - end 418 + end
416 419
417 def period(from_date, until_date) 420 def period(from_date, until_date)
418 begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date 421 begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date
app/api/v1/articles.rb
@@ -62,7 +62,7 @@ module Api @@ -62,7 +62,7 @@ module Api
62 { :success => true } 62 { :success => true }
63 rescue Exception => exception 63 rescue Exception => exception
64 render_api_error!(_('The article couldn\'t be removed due to some problem. Please contact the administrator.'), 400) 64 render_api_error!(_('The article couldn\'t be removed due to some problem. Please contact the administrator.'), 400)
65 - end 65 + end
66 end 66 end
67 67
68 desc 'Report a abuse and/or violent content in a article by id' do 68 desc 'Report a abuse and/or violent content in a article by id' do
app/api/v1/people.rb
@@ -108,6 +108,23 @@ module Api @@ -108,6 +108,23 @@ module Api
108 end 108 end
109 present output 109 present output
110 end 110 end
  111 +
  112 + desc "Return the person profile picture (you can optionally pass a 'size' parameter)"
  113 + get ":id/icon" do
  114 + person = environment.people.find(params[:id])
  115 +
  116 + size = params[:size] || :portrait
  117 + image = profile_icon(person, size.to_sym)
  118 + output = {}
  119 +
  120 + unless image.match(/^\/\/www\.gravatar\.com/).nil?
  121 + output[:icon] = 'https:' + image
  122 + else
  123 + output[:icon] = request.url.gsub(/\/api\/.*/, '') + image
  124 + end
  125 +
  126 + present output
  127 + end
111 end 128 end
112 129
113 resource :profiles do 130 resource :profiles do
app/concerns/authenticated_system.rb
@@ -25,7 +25,19 @@ module AuthenticatedSystem @@ -25,7 +25,19 @@ module AuthenticatedSystem
25 # Accesses the current user from the session. 25 # Accesses the current user from the session.
26 def current_user user_id = session[:user] 26 def current_user user_id = session[:user]
27 @current_user ||= begin 27 @current_user ||= begin
28 - user = User.find_by id: user_id if user_id 28 + user = nil
  29 + if session[:external]
  30 + user = User.new
  31 + external_person = ExternalPerson.find_by(id: session[:external])
  32 + if external_person
  33 + user.external_person_id = external_person.id
  34 + user.email = external_person.email
  35 + else
  36 + session[:external] = nil
  37 + end
  38 + else
  39 + user = User.find_by(id: user_id) if user_id
  40 + end
29 user.session = session if user 41 user.session = session if user
30 User.current = user 42 User.current = user
31 user 43 user
@@ -37,9 +49,13 @@ module AuthenticatedSystem @@ -37,9 +49,13 @@ module AuthenticatedSystem
37 if new_user.nil? 49 if new_user.nil?
38 session.delete(:user) 50 session.delete(:user)
39 else 51 else
40 - session[:user] = new_user.id 52 + if new_user.id
  53 + session[:user] = new_user.id
  54 + else
  55 + session[:external] = new_user.external_person_id
  56 + end
41 new_user.session = session 57 new_user.session = session
42 - new_user.register_login 58 + new_user.register_login if new_user.id
43 end 59 end
44 @current_user = User.current = new_user 60 @current_user = User.current = new_user
45 end 61 end
app/controllers/application_controller.rb
@@ -8,6 +8,7 @@ class ApplicationController &lt; ActionController::Base @@ -8,6 +8,7 @@ class ApplicationController &lt; ActionController::Base
8 before_filter :allow_cross_domain_access 8 before_filter :allow_cross_domain_access
9 9
10 include AuthenticatedSystem 10 include AuthenticatedSystem
  11 +
11 before_filter :require_login_for_environment, :if => :private_environment? 12 before_filter :require_login_for_environment, :if => :private_environment?
12 13
13 before_filter :verify_members_whitelist, :if => [:private_environment?, :user] 14 before_filter :verify_members_whitelist, :if => [:private_environment?, :user]
@@ -120,7 +121,9 @@ class ApplicationController &lt; ActionController::Base @@ -120,7 +121,9 @@ class ApplicationController &lt; ActionController::Base
120 end 121 end
121 122
122 def user 123 def user
123 - current_user.person if logged_in? 124 + if logged_in?
  125 + current_user.person || current_user.external_person
  126 + end
124 end 127 end
125 128
126 alias :current_person :user 129 alias :current_person :user
app/controllers/my_profile/email_templates_controller.rb
@@ -64,7 +64,7 @@ class EmailTemplatesController &lt; ApplicationController @@ -64,7 +64,7 @@ class EmailTemplatesController &lt; ApplicationController
64 private 64 private
65 65
66 def template_params 66 def template_params
67 - {:profile_name => current_user.name, :environment_name => environment.name } 67 + {:profile_name => current_person.name, :environment_name => environment.name }
68 end 68 end
69 69
70 def template_params_allowed params 70 def template_params_allowed params
app/controllers/public/profile_controller.rb
@@ -176,7 +176,7 @@ class ProfileController &lt; PublicController @@ -176,7 +176,7 @@ class ProfileController &lt; PublicController
176 end 176 end
177 177
178 def unblock 178 def unblock
179 - if current_user.person.is_admin?(profile.environment) 179 + if current_person.is_admin?(profile.environment)
180 profile.unblock 180 profile.unblock
181 session[:notice] = _("You have unblocked %s successfully. ") % profile.name 181 session[:notice] = _("You have unblocked %s successfully. ") % profile.name
182 redirect_to :controller => 'profile', :action => 'index' 182 redirect_to :controller => 'profile', :action => 'index'
@@ -187,7 +187,7 @@ class ProfileController &lt; PublicController @@ -187,7 +187,7 @@ class ProfileController &lt; PublicController
187 end 187 end
188 188
189 def leave_scrap 189 def leave_scrap
190 - sender = params[:sender_id].nil? ? current_user.person : Person.find(params[:sender_id]) 190 + sender = params[:sender_id].nil? ? current_person : Person.find(params[:sender_id])
191 receiver = params[:receiver_id].nil? ? @profile : Person.find(params[:receiver_id]) 191 receiver = params[:receiver_id].nil? ? @profile : Person.find(params[:receiver_id])
192 @scrap = Scrap.new(params[:scrap]) 192 @scrap = Scrap.new(params[:scrap])
193 @scrap.sender= sender 193 @scrap.sender= sender
@@ -270,7 +270,7 @@ class ProfileController &lt; PublicController @@ -270,7 +270,7 @@ class ProfileController &lt; PublicController
270 270
271 def remove_scrap 271 def remove_scrap
272 begin 272 begin
273 - scrap = current_user.person.scraps(params[:scrap_id]) 273 + scrap = current_person.scraps(params[:scrap_id])
274 scrap.destroy 274 scrap.destroy
275 finish_successful_removal 'Scrap successfully removed.' 275 finish_successful_removal 'Scrap successfully removed.'
276 rescue 276 rescue
@@ -395,6 +395,17 @@ class ProfileController &lt; PublicController @@ -395,6 +395,17 @@ class ProfileController &lt; PublicController
395 end 395 end
396 end 396 end
397 397
  398 + def icon
  399 + size = params[:size] || :portrait
  400 + image, mime = profile_icon(profile, size.to_sym, true)
  401 +
  402 + unless image.match(/^\/\/www\.gravatar\.com/).nil?
  403 + redirect_to 'https:' + image
  404 + else
  405 + @file = File.join(Rails.root, 'public', image)
  406 + send_file @file, type: mime, disposition: 'inline'
  407 + end
  408 + end
398 409
399 protected 410 protected
400 411
app/controllers/public/search_controller.rb
@@ -247,7 +247,7 @@ class SearchController &lt; PublicController @@ -247,7 +247,7 @@ class SearchController &lt; PublicController
247 def visible_profiles(klass, *extra_relations) 247 def visible_profiles(klass, *extra_relations)
248 relations = [:image, :domains, :environment, :preferred_domain] 248 relations = [:image, :domains, :environment, :preferred_domain]
249 relations += extra_relations 249 relations += extra_relations
250 - if current_user && current_user.person.is_admin? 250 + if current_user && current_person.is_admin?
251 @environment.send(klass.name.underscore.pluralize).includes(relations) 251 @environment.send(klass.name.underscore.pluralize).includes(relations)
252 else 252 else
253 @environment.send(klass.name.underscore.pluralize).visible.includes(relations) 253 @environment.send(klass.name.underscore.pluralize).visible.includes(relations)
app/helpers/application_helper.rb
@@ -146,12 +146,12 @@ module ApplicationHelper @@ -146,12 +146,12 @@ module ApplicationHelper
146 end 146 end
147 147
148 def link_to_cms(text, profile = nil, options = {}) 148 def link_to_cms(text, profile = nil, options = {})
149 - profile ||= current_user.login 149 + profile ||= current_person.identifier
150 link_to text, myprofile_path(:controller => 'cms', :profile => profile), options 150 link_to text, myprofile_path(:controller => 'cms', :profile => profile), options
151 end 151 end
152 152
153 def link_to_profile(text, profile = nil, options = {}) 153 def link_to_profile(text, profile = nil, options = {})
154 - profile ||= current_user.login 154 + profile ||= current_person.identifier
155 link_to text, profile_path(:profile => profile) , options 155 link_to text, profile_path(:profile => profile) , options
156 end 156 end
157 157
@@ -160,7 +160,7 @@ module ApplicationHelper @@ -160,7 +160,7 @@ module ApplicationHelper
160 end 160 end
161 161
162 def link_if_permitted(link, permission = nil, target = nil) 162 def link_if_permitted(link, permission = nil, target = nil)
163 - if permission.nil? || current_user.person.has_permission?(permission, target) 163 + if permission.nil? || current_person.has_permission?(permission, target)
164 link 164 link
165 else 165 else
166 nil 166 nil
@@ -814,7 +814,7 @@ module ApplicationHelper @@ -814,7 +814,7 @@ module ApplicationHelper
814 {s_('contents|Most commented') => {href: url_for({host: host, controller: 'search', action: 'contents', filter: 'more_comments'})}} 814 {s_('contents|Most commented') => {href: url_for({host: host, controller: 'search', action: 'contents', filter: 'more_comments'})}}
815 ] 815 ]
816 if logged_in? 816 if logged_in?
817 - links.push(_('New content') => modal_options({:href => url_for({:controller => 'cms', :action => 'new', :profile => current_user.login, :cms => true})})) 817 + links.push(_('New content') => modal_options({:href => url_for({:controller => 'cms', :action => 'new', :profile => current_person.identifier, :cms => true})}))
818 end 818 end
819 819
820 link_to(content_tag(:span, _('Contents'), :class => 'icon-menu-articles'), {:controller => "search", :action => 'contents', :category_path => nil}, :id => 'submenu-contents') + 820 link_to(content_tag(:span, _('Contents'), :class => 'icon-menu-articles'), {:controller => "search", :action => 'contents', :category_path => nil}, :id => 'submenu-contents') +
@@ -830,8 +830,8 @@ module ApplicationHelper @@ -830,8 +830,8 @@ module ApplicationHelper
830 {s_('people|More popular') => {href: url_for({host: host, controller: 'search', action: 'people', filter: 'more_popular'})}} 830 {s_('people|More popular') => {href: url_for({host: host, controller: 'search', action: 'people', filter: 'more_popular'})}}
831 ] 831 ]
832 if logged_in? 832 if logged_in?
833 - links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})})  
834 - links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})}) 833 + links.push(_('My friends') => {:href => url_for({:profile => current_person.identifier, :controller => 'friends'})})
  834 + links.push(_('Invite friends') => {:href => url_for({:profile => current_person.identifier, :controller => 'invite', :action => 'friends'})})
835 end 835 end
836 836
837 link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "search", :action => 'people', :category_path => ''}, :id => 'submenu-people') + 837 link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "search", :action => 'people', :category_path => ''}, :id => 'submenu-people') +
@@ -847,8 +847,8 @@ module ApplicationHelper @@ -847,8 +847,8 @@ module ApplicationHelper
847 {s_('communities|More popular') => {href: url_for({host: host, controller: 'search', action: 'communities', filter: 'more_popular'})}} 847 {s_('communities|More popular') => {href: url_for({host: host, controller: 'search', action: 'communities', filter: 'more_popular'})}}
848 ] 848 ]
849 if logged_in? 849 if logged_in?
850 - links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})})  
851 - links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})}) 850 + links.push(_('My communities') => {:href => url_for({:profile => current_person.identifier, :controller => 'memberships'})})
  851 + links.push(_('New community') => {:href => url_for({:profile => current_person.identifier, :controller => 'memberships', :action => 'new_community'})})
852 end 852 end
853 853
854 link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "search", :action => 'communities'}, :id => 'submenu-communities') + 854 link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "search", :action => 'communities'}, :id => 'submenu-communities') +
app/helpers/theme_loader_helper.rb
@@ -2,7 +2,7 @@ module ThemeLoaderHelper @@ -2,7 +2,7 @@ module ThemeLoaderHelper
2 def current_theme 2 def current_theme
3 @current_theme ||= 3 @current_theme ||=
4 begin 4 begin
5 - if session[:user_theme] 5 + if defined?(session).present? && session[:user_theme]
6 session[:user_theme] 6 session[:user_theme]
7 else 7 else
8 # utility for developers: set the theme to 'random' in development mode and 8 # utility for developers: set the theme to 'random' in development mode and
@@ -14,7 +14,7 @@ module ThemeLoaderHelper @@ -14,7 +14,7 @@ module ThemeLoaderHelper
14 elsif Rails.env.development? && respond_to?(:params) && params[:user_theme] && File.exists?(Rails.root.join('public/designs/themes', params[:user_theme])) 14 elsif Rails.env.development? && respond_to?(:params) && params[:user_theme] && File.exists?(Rails.root.join('public/designs/themes', params[:user_theme]))
15 params[:user_theme] 15 params[:user_theme]
16 else 16 else
17 - if profile && !profile.theme.nil? 17 + if defined?(profile) && profile && !profile.theme.nil?
18 profile.theme 18 profile.theme
19 elsif environment 19 elsif environment
20 environment.theme 20 environment.theme
@@ -34,9 +34,9 @@ module ThemeLoaderHelper @@ -34,9 +34,9 @@ module ThemeLoaderHelper
34 end 34 end
35 35
36 def theme_path 36 def theme_path
37 - if session[:user_theme] 37 + if defined?(session).present? && session[:user_theme]
38 '/user_themes/' + current_theme 38 '/user_themes/' + current_theme
39 - elsif session[:theme] 39 + elsif defined?(session).present? && session[:theme]
40 '/designs/themes/' + session[:theme] 40 '/designs/themes/' + session[:theme]
41 else 41 else
42 '/designs/themes/' + current_theme 42 '/designs/themes/' + current_theme
app/models/abuse_complaint.rb
1 class AbuseComplaint < Task 1 class AbuseComplaint < Task
2 has_many :abuse_reports, :dependent => :destroy 2 has_many :abuse_reports, :dependent => :destroy
3 - belongs_to :reported, :class_name => "Profile", :foreign_key => "requestor_id" 3 + belongs_to :reported, :polymorphic => true, :foreign_key => "requestor_id"
4 4
5 validates_presence_of :reported 5 validates_presence_of :reported
6 alias :requestor :reported 6 alias :requestor :reported
app/models/abuse_report.rb
@@ -2,7 +2,7 @@ class AbuseReport &lt; ApplicationRecord @@ -2,7 +2,7 @@ class AbuseReport &lt; ApplicationRecord
2 2
3 attr_accessible :content, :reason 3 attr_accessible :content, :reason
4 4
5 - belongs_to :reporter, :class_name => 'Person' 5 + belongs_to :reporter, :polymorphic => true
6 belongs_to :abuse_complaint 6 belongs_to :abuse_complaint
7 has_many :reported_images, :dependent => :destroy 7 has_many :reported_images, :dependent => :destroy
8 8
app/models/comment.rb
@@ -15,7 +15,7 @@ class Comment &lt; ApplicationRecord @@ -15,7 +15,7 @@ class Comment &lt; ApplicationRecord
15 alias :article= :source= 15 alias :article= :source=
16 attr_accessor :follow_article 16 attr_accessor :follow_article
17 17
18 - belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id' 18 + belongs_to :author, :polymorphic => true, :foreign_key => 'author_id'
19 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy 19 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
20 belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' 20 belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id'
21 21
app/models/concerns/external_user.rb 0 → 100644
@@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
  1 +require 'ostruct'
  2 +
  3 +module ExternalUser
  4 + extend ActiveSupport::Concern
  5 +
  6 + included do
  7 + attr_accessor :external_person_id
  8 + end
  9 +
  10 + def external_person
  11 + ExternalPerson.where(id: self.external_person_id).first
  12 + end
  13 +
  14 + def person_with_external
  15 + self.external_person || self.person_without_external
  16 + end
  17 +
  18 + module ClassMethods
  19 + def webfinger_lookup(login, domain, environment)
  20 + if login && domain && environment.has_federated_network?(domain)
  21 + external_environment = environment.external_environments.find_by_url(domain)
  22 + scheme = "http#{external_environment.uses_ssl? ? 's' : ''}"
  23 + url = URI.parse(scheme+"://"+ domain +'/.well-known/webfinger?resource=acct:'+
  24 + login+'@'+domain)
  25 + http = build_request(url)
  26 + req = Net::HTTP::Get.new(url.to_s)
  27 + res = http.request(req)
  28 + JSON.parse(res.body)
  29 + else
  30 + nil
  31 + end
  32 + end
  33 +
  34 + def build_request(uri)
  35 + request = Net::HTTP.new(uri.host, uri.port)
  36 + if uri.scheme == "https" # enable SSL/TLS
  37 + request.use_ssl = true
  38 + #TODO There may be self-signed certificates that we would not be able
  39 + #to verify, so we'll not verify the ssl certificate for now. Since
  40 + #this requests will go only towards trusted federated networks the admin
  41 + #configured we consider this not to be a big deal. Nonetheless we may be
  42 + #able in the future to require/provide the CA Files on the federation
  43 + #process which would allow us to verify the certificate.
  44 + request.verify_mode = OpenSSL::SSL::VERIFY_NONE
  45 + end
  46 + request
  47 + end
  48 +
  49 + def external_login(login, password, domain)
  50 + # Call Noosfero /api/login
  51 + result = nil
  52 + response = nil
  53 + redirections_allowed = 3
  54 + external_environment = ExternalEnvironment.find_by_url(domain)
  55 + scheme = "http#{external_environment.uses_ssl? ? 's' : ''}"
  56 + location = scheme + '://' + domain + '/api/v1/login'
  57 + request_params = CGI.unescape({ login: login, password: password }.to_query)
  58 + begin
  59 + while redirections_allowed > 0 && (response.blank? || response.code == '301')
  60 + uri = URI.parse(location)
  61 + request = build_request(uri)
  62 + response = request.post(uri.to_s, request_params)
  63 + location = response.header['location']
  64 + redirections_allowed -= 1
  65 + end
  66 + result = response.code.to_i / 100 === 2 ? JSON.parse(response.body) : nil
  67 + rescue
  68 + # Could not make request
  69 + end
  70 + result
  71 + end
  72 +
  73 + # Authenticates a user from an external social network
  74 + def external_authenticate(username, password, environment)
  75 + if username && username.include?('@')
  76 + login, domain = username.split('@')
  77 + webfinger = User.webfinger_lookup(login, domain, environment)
  78 + if webfinger
  79 + user = User.external_login(login, password, domain)
  80 + if user
  81 + u = User.new
  82 + u.email = user['user']['email']
  83 + u.login = login
  84 + webfinger = OpenStruct.new(
  85 + identifier: webfinger['properties']['identifier'],
  86 + name: webfinger['titles']['name'],
  87 + created_at: webfinger['properties']['created_at'],
  88 + domain: domain,
  89 + email: user['user']['email']
  90 + )
  91 + u.external_person_id = ExternalPerson.get_or_create(webfinger).id
  92 + return u
  93 + end
  94 + end
  95 + end
  96 + nil
  97 + end
  98 +
  99 + end
  100 +end
app/models/concerns/human.rb 0 → 100644
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
  1 +module Human
  2 + extend ActiveSupport::Concern
  3 +
  4 + included do
  5 + has_many :comments, :as => :author, :foreign_key => :author_id
  6 + has_many :abuse_reports, :as => :reporter, :foreign_key => 'reporter_id', :dependent => :destroy
  7 +
  8 + scope :abusers, -> {
  9 + joins(:abuse_complaints).where('tasks.status = 3').distinct.select("#{self.table_name}.*")
  10 + }
  11 + scope :non_abusers, -> {
  12 + distinct.select("#{self.table_name}.*").
  13 + joins("LEFT JOIN tasks ON #{self.table_name}.id = tasks.requestor_id AND tasks.type='AbuseComplaint'").
  14 + where("tasks.status != 3 OR tasks.id is NULL")
  15 + }
  16 + end
  17 +
  18 + def already_reported?(profile)
  19 + abuse_reports.any? { |report| report.abuse_complaint.reported == profile && report.abuse_complaint.opened? }
  20 + end
  21 +
  22 + def register_report(abuse_report, profile)
  23 + AbuseComplaint.create!(:reported => profile, :target => profile.environment) if !profile.opened_abuse_complaint
  24 + abuse_report.abuse_complaint = profile.opened_abuse_complaint
  25 + abuse_report.reporter = self
  26 + abuse_report.save!
  27 + end
  28 +
  29 + def abuser?
  30 + AbuseComplaint.finished.where(:requestor_id => self).count > 0
  31 + end
  32 +
  33 + # Sets the identifier for this person. Raises an exception when called on a
  34 + # existing person (since peoples' identifiers cannot be changed)
  35 + def identifier=(value)
  36 + unless self.new_record?
  37 + raise ArgumentError.new(_('An existing person cannot be renamed.'))
  38 + end
  39 + self[:identifier] = value
  40 + end
  41 +
  42 +end
app/models/concerns/profile_entity.rb 0 → 100644
@@ -0,0 +1,155 @@ @@ -0,0 +1,155 @@
  1 +module ProfileEntity
  2 + extend ActiveSupport::Concern
  3 +
  4 + included do
  5 + attr_accessible :name, :identifier, :environment
  6 +
  7 + validates_presence_of :identifier, :name
  8 +
  9 + belongs_to :environment
  10 + has_many :search_terms, :as => :context
  11 + has_many :abuse_complaints, :as => :reported, :foreign_key => 'requestor_id', :dependent => :destroy
  12 +
  13 + before_create :set_default_environment
  14 +
  15 + scope :recent, -> limit=nil { order('id DESC').limit(limit) }
  16 +
  17 + end
  18 +
  19 + def disable
  20 + self.visible = false
  21 + self.save
  22 + end
  23 +
  24 + def enable
  25 + self.visible = true
  26 + self.save
  27 + end
  28 +
  29 + def opened_abuse_complaint
  30 + abuse_complaints.opened.first
  31 + end
  32 +
  33 + def set_default_environment
  34 + if self.environment.nil?
  35 + self.environment = Environment.default
  36 + end
  37 + true
  38 + end
  39 +
  40 + # returns +false+
  41 + def person?
  42 + self.kind_of?(Person)
  43 + end
  44 +
  45 + def enterprise?
  46 + self.kind_of?(Enterprise)
  47 + end
  48 +
  49 + def organization?
  50 + self.kind_of?(Organization)
  51 + end
  52 +
  53 + def community?
  54 + self.kind_of?(Community)
  55 + end
  56 +
  57 + include ActionView::Helpers::TextHelper
  58 + def short_name(chars = 40)
  59 + if self[:nickname].blank?
  60 + if chars
  61 + truncate self.name, length: chars, omission: '...'
  62 + else
  63 + self.name
  64 + end
  65 + else
  66 + self[:nickname]
  67 + end
  68 + end
  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 + def allow_post_content?(person = nil)
  136 + person.kind_of?(Profile) && person.has_permission?('post_content', self)
  137 + end
  138 +
  139 + def allow_edit?(person = nil)
  140 + person.kind_of?(Profile) && person.has_permission?('edit_profile', self)
  141 + end
  142 +
  143 + def allow_destroy?(person = nil)
  144 + person.kind_of?(Profile) && person.has_permission?('destroy_profile', self)
  145 + end
  146 +
  147 + module ClassMethods
  148 +
  149 + def identification
  150 + name
  151 + end
  152 +
  153 + end
  154 +
  155 +end
app/models/environment.rb
@@ -1009,6 +1009,10 @@ class Environment &lt; ApplicationRecord @@ -1009,6 +1009,10 @@ class Environment &lt; ApplicationRecord
1009 HashWithIndifferentAccess.new :name => name 1009 HashWithIndifferentAccess.new :name => name
1010 end 1010 end
1011 1011
  1012 + def has_federated_network?(domain)
  1013 + self.external_environments.map(&:url).any? { |url| /http[s]?:\/\/#{domain}\/?/ =~ url }
  1014 + end
  1015 +
1012 private 1016 private
1013 1017
1014 def default_language_available 1018 def default_language_available
app/models/external_environment.rb
@@ -6,4 +6,9 @@ class ExternalEnvironment &lt; ActiveRecord::Base @@ -6,4 +6,9 @@ class ExternalEnvironment &lt; ActiveRecord::Base
6 6
7 has_many :environment_external_environments, dependent: :destroy 7 has_many :environment_external_environments, dependent: :destroy
8 has_many :environments, through: :environment_external_environments 8 has_many :environments, through: :environment_external_environments
  9 +
  10 + def uses_ssl?
  11 + url.starts_with? 'https'
  12 + end
  13 +
9 end 14 end
app/models/external_person.rb 0 → 100644
@@ -0,0 +1,290 @@ @@ -0,0 +1,290 @@
  1 +# A pseudo profile is a person from a remote network
  2 +class ExternalPerson < ActiveRecord::Base
  3 +
  4 + include Human
  5 + include ProfileEntity
  6 +
  7 + validates_uniqueness_of :identifier, scope: :source
  8 +
  9 + validates_presence_of :source, :email, :created_at
  10 +
  11 + attr_accessible :source, :email, :created_at
  12 +
  13 + def self.get_or_create(webfinger)
  14 + user = ExternalPerson.find_by(identifier: webfinger.identifier, source: webfinger.domain)
  15 + if user.nil?
  16 + user = ExternalPerson.create!(identifier: webfinger.identifier,
  17 + name: webfinger.name,
  18 + source: webfinger.domain,
  19 + email: webfinger.email,
  20 + created_at: webfinger.created_at
  21 + )
  22 + end
  23 + user
  24 + end
  25 +
  26 + def privacy_setting
  27 + _('Public profile')
  28 + end
  29 +
  30 + def avatar
  31 + "http://#{self.source}/profile/#{self.identifier}/icon/"
  32 + end
  33 +
  34 + def url
  35 + "http://#{self.source}/profile/#{self.identifier}"
  36 + end
  37 +
  38 + alias :public_profile_url :url
  39 +
  40 + def admin_url
  41 + "http://#{self.source}/myprofile/#{self.identifier}"
  42 + end
  43 +
  44 + def wall_url
  45 + self.url
  46 + end
  47 + def tasks_url
  48 + self.url
  49 + end
  50 + def leave_url(reload = false)
  51 + self.url
  52 + end
  53 + def join_url
  54 + self.url
  55 + end
  56 + def join_not_logged_url
  57 + self.url
  58 + end
  59 + def check_membership_url
  60 + self.url
  61 + end
  62 + def add_url
  63 + self.url
  64 + end
  65 + def check_friendship_url
  66 + self.url
  67 + end
  68 + def people_suggestions_url
  69 + self.url
  70 + end
  71 + def communities_suggestions_url
  72 + self.url
  73 + end
  74 + def top_url(scheme = 'http')
  75 + "#{scheme}://#{self.source}"
  76 + end
  77 +
  78 + def profile_custom_icon(gravatar_default=nil)
  79 + self.avatar
  80 + end
  81 +
  82 + def preferred_login_redirection
  83 + environment.redirection_after_login
  84 + end
  85 +
  86 + def location
  87 + self.source
  88 + end
  89 +
  90 + def default_hostname
  91 + environment.default_hostname
  92 + end
  93 +
  94 + def possible_domains
  95 + environment.domains
  96 + end
  97 +
  98 + def person?
  99 + true
  100 + end
  101 +
  102 + def contact_email(*args)
  103 + self.email
  104 + end
  105 +
  106 + def notification_emails
  107 + [self.contact_email]
  108 + end
  109 +
  110 + def email_domain
  111 + self.source
  112 + end
  113 +
  114 + def email_addresses
  115 + ['%s@%s' % [self.identifier, self.source] ]
  116 + end
  117 +
  118 + def jid(options = {})
  119 + "#{self.identifier}@#{self.source}"
  120 + end
  121 + def full_jid(options = {})
  122 + "#{jid(options)}/#{self.name}"
  123 + end
  124 +
  125 + class ExternalPerson::Image
  126 + def initialize(path)
  127 + @path = path
  128 + end
  129 +
  130 + def public_filename(size = nil)
  131 + URI.join(@path, size.to_s)
  132 + end
  133 +
  134 + def content_type
  135 + # This is not really going to be used anywhere that matters
  136 + # so we are hardcodding it here.
  137 + 'image/png'
  138 + end
  139 + end
  140 +
  141 + def image
  142 + ExternalPerson::Image.new(avatar)
  143 + end
  144 +
  145 + def data_hash(gravatar_default = nil)
  146 + friends_list = {}
  147 + {
  148 + 'login' => self.identifier,
  149 + 'name' => self.name,
  150 + 'email' => self.email,
  151 + 'avatar' => self.profile_custom_icon(gravatar_default),
  152 + 'is_admin' => self.is_admin?,
  153 + 'since_month' => self.created_at.month,
  154 + 'since_year' => self.created_at.year,
  155 + 'email_domain' => self.source,
  156 + 'friends_list' => friends_list,
  157 + 'enterprises' => [],
  158 + 'amount_of_friends' => friends_list.count,
  159 + 'chat_enabled' => false
  160 + }
  161 + end
  162 +
  163 + # External Person should respond to all methods in Person and Profile
  164 + def person_instance_methods
  165 + methods_and_responses = {
  166 + enterprises: Enterprise.none, communities: Community.none, friends:
  167 + Person.none, memberships: Profile.none, friendships: Person.none,
  168 + following_articles: Article.none, article_followers: ArticleFollower.none,
  169 + requested_tasks: Task.none, mailings: Mailing.none, scraps_sent:
  170 + Scrap.none, favorite_enterprise_people: FavoriteEnterprisePerson.none,
  171 + favorite_enterprises: Enterprise.none, acepted_forums: Forum.none,
  172 + articles_with_access: Article.none, suggested_profiles:
  173 + ProfileSuggestion.none, suggested_people: ProfileSuggestion.none,
  174 + suggested_communities: ProfileSuggestion.none, user: nil,
  175 + refused_communities: Community.none, has_permission?: false,
  176 + has_permission_with_admin?: false, has_permission_without_admin?: false,
  177 + has_permission_with_plugins?: false, has_permission_without_plugins?:
  178 + false, memberships_by_role: Person.none, can_change_homepage?: false,
  179 + can_control_scrap?: false, receives_scrap_notification?: false,
  180 + can_control_activity?: false, can_post_content?: false,
  181 + suggested_friend_groups: [], friend_groups: [], add_friend: nil,
  182 + already_request_friendship?: false, remove_friend: nil,
  183 + presence_of_required_fields: nil, active_fields: [], required_fields: [],
  184 + signup_fields: [], default_set_of_blocks: [], default_set_of_boxes: [],
  185 + default_set_of_articles: [], cell_phone: nil, comercial_phone: nil,
  186 + nationality: nil, schooling: nil, contact_information: nil, sex: nil,
  187 + birth_date: nil, jabber_id: nil, personal_website: nil, address_reference:
  188 + nil, district: nil, schooling_status: nil, formation: nil,
  189 + custom_formation: nil, area_of_study: nil, custom_area_of_study: nil,
  190 + professional_activity: nil, organization_website: nil, organization: nil,
  191 + photo: nil, city: nil, state: nil, country: nil, zip_code: nil,
  192 + address_line2: nil, copy_communities_from: nil,
  193 + has_organization_pending_tasks?: false, organizations_with_pending_tasks:
  194 + Organization.none, pending_tasks_for_organization: Task.none,
  195 + build_contact: nil, is_a_friend?: false, ask_to_join?: false, refuse_join:
  196 + nil, blocks_to_expire_cache: [], cache_keys: [], communities_cache_key: '',
  197 + friends_cache_key: '', manage_friends_cache_key: '',
  198 + relationships_cache_key: '', is_member_of?: false, follows?: false,
  199 + each_friend: nil, is_last_admin?: false, is_last_admin_leaving?: false,
  200 + leave: nil, last_notification: nil, notification_time: 0, notifier: nil,
  201 + remove_suggestion: nil, allow_invitation_from?: false
  202 + }
  203 +
  204 + derivated_methods = generate_derivated_methods(methods_and_responses)
  205 + derivated_methods.merge(methods_and_responses)
  206 + end
  207 +
  208 + def profile_instance_methods
  209 + methods_and_responses = {
  210 + role_assignments: RoleAssignment.none, favorite_enterprises:
  211 + Enterprise.none, memberships: Profile.none, friendships: Profile.none,
  212 + tasks: Task.none, suggested_profiles: ProfileSuggestion.none,
  213 + suggested_people: ProfileSuggestion.none, suggested_communities:
  214 + ProfileSuggestion.none, public_profile: true, nickname: nil, custom_footer:
  215 + '', custom_header: '', address: '', zip_code: '', contact_phone: '',
  216 + image_builder: nil, description: '', closed: false, template_id: nil, lat:
  217 + nil, lng: nil, is_template: false, fields_privacy: {}, preferred_domain_id:
  218 + nil, category_ids: [], country: '', city: '', state: '',
  219 + national_region_code: '', redirect_l10n: false, notification_time: 0,
  220 + custom_url_redirection: nil, email_suggestions: false,
  221 + allow_members_to_invite: false, invite_friends_only: false, secret: false,
  222 + profile_admin_mail_notification: false, redirection_after_login: nil,
  223 + profile_activities: ProfileActivity.none, action_tracker_notifications:
  224 + ActionTrackerNotification.none, tracked_notifications:
  225 + ActionTracker::Record.none, scraps_received: Scrap.none, template:
  226 + Profile.none, comments_received: Comment.none, email_templates:
  227 + EmailTemplate.none, members: Profile.none, members_like: Profile.none,
  228 + members_by: Profile.none, members_by_role: Profile.none, scraps:
  229 + Scrap.none, welcome_page_content: nil, settings: {}, find_in_all_tasks:
  230 + nil, top_level_categorization: {}, interests: Category.none, geolocation:
  231 + '', country_name: '', pending_categorizations: [], add_category: false,
  232 + create_pending_categorizations: false, top_level_articles: Article.none,
  233 + valid_identifier: true, valid_template: false, create_default_set_of_boxes:
  234 + true, copy_blocks_from: nil, default_template: nil,
  235 + template_without_default: nil, template_with_default: nil, apply_template:
  236 + false, iframe_whitelist: [], recent_documents: Article.none, last_articles:
  237 + Article.none, is_validation_entity?: false, hostname: nil, own_hostname:
  238 + nil, article_tags: {}, tagged_with: Article.none,
  239 + insert_default_article_set: false, copy_articles_from: true,
  240 + copy_article_tree: nil, copy_article?: false, add_member: false,
  241 + remove_member: false, add_admin: false, remove_admin: false, add_moderator:
  242 + false, display_info_to?: true, update_category_from_region: nil,
  243 + accept_category?: false, custom_header_expanded: '',
  244 + custom_footer_expanded: '', public?: true, themes: [], find_theme: nil,
  245 + blogs: Blog.none, blog: nil, has_blog?: false, forums: Forum.none, forum:
  246 + nil, has_forum?: false, admins: [], settings_field: {}, setting_changed:
  247 + false, public_content: true, enable_contact?: false, folder_types: [],
  248 + folders: Article.none, image_galleries: Article.none, image_valid: true,
  249 + update_header_and_footer: nil, update_theme: nil, update_layout_template:
  250 + nil, recent_actions: ActionTracker::Record.none, recent_notifications:
  251 + ActionTracker::Record.none, more_active_label: _('no activity'),
  252 + more_popular_label: _('no members'), profile_custom_image: nil,
  253 + is_on_homepage?: false, activities: ProfileActivity.none,
  254 + may_display_field_to?: true, may_display_location_to?: true, public_fields:
  255 + {}, followed_by?: false, display_private_info_to?: true, can_view_field?:
  256 + true, remove_from_suggestion_list: nil, layout_template: 'default',
  257 + is_admin?: false, add_friend: false, follows?: false, is_a_friend?: false,
  258 + already_request_friendship?: false
  259 + }
  260 +
  261 + derivated_methods = generate_derivated_methods(methods_and_responses)
  262 + derivated_methods.merge(methods_and_responses)
  263 + end
  264 +
  265 + def method_missing(method, *args, &block)
  266 + if person_instance_methods.keys.include?(method)
  267 + return person_instance_methods[method]
  268 + end
  269 + if profile_instance_methods.keys.include? method
  270 + return profile_instance_methods[method]
  271 + end
  272 + end
  273 +
  274 + def respond_to_missing?(method_name, include_private = false)
  275 + person_instance_methods.keys.include?(method_name) ||
  276 + profile_instance_methods.keys.include?(method_name) ||
  277 + super
  278 + end
  279 +
  280 + private
  281 +
  282 + def generate_derivated_methods(methods)
  283 + derivated_methods = {}
  284 + methods.keys.each do |method|
  285 + derivated_methods[method.to_s.insert(-1, '?').to_sym] = false
  286 + derivated_methods[method.to_s.insert(-1, '=').to_sym] = nil
  287 + end
  288 + derivated_methods
  289 + end
  290 +end
app/models/person.rb
1 # A person is the profile of an user holding all relationships with the rest of the system 1 # A person is the profile of an user holding all relationships with the rest of the system
2 class Person < Profile 2 class Person < Profile
3 3
  4 + include Human
  5 +
4 attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization_website, :following_articles 6 attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization_website, :following_articles
5 7
6 SEARCH_FILTERS = { 8 SEARCH_FILTERS = {
@@ -88,7 +90,6 @@ class Person &lt; Profile @@ -88,7 +90,6 @@ class Person &lt; Profile
88 memberships.where('role_assignments.role_id = ?', role.id) 90 memberships.where('role_assignments.role_id = ?', role.id)
89 end 91 end
90 92
91 - has_many :comments, :foreign_key => :author_id  
92 has_many :article_followers, :dependent => :destroy 93 has_many :article_followers, :dependent => :destroy
93 has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article 94 has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article
94 has_many :friendships, :dependent => :destroy 95 has_many :friendships, :dependent => :destroy
@@ -100,8 +101,6 @@ class Person &lt; Profile @@ -100,8 +101,6 @@ class Person &lt; Profile
100 101
101 has_many :requested_tasks, :class_name => 'Task', :foreign_key => :requestor_id, :dependent => :destroy 102 has_many :requested_tasks, :class_name => 'Task', :foreign_key => :requestor_id, :dependent => :destroy
102 103
103 - has_many :abuse_reports, :foreign_key => 'reporter_id', :dependent => :destroy  
104 -  
105 has_many :mailings 104 has_many :mailings
106 105
107 has_many :scraps_sent, :class_name => 'Scrap', :foreign_key => :sender_id, :dependent => :destroy 106 has_many :scraps_sent, :class_name => 'Scrap', :foreign_key => :sender_id, :dependent => :destroy
@@ -123,15 +122,6 @@ class Person &lt; Profile @@ -123,15 +122,6 @@ class Person &lt; Profile
123 122
124 scope :more_popular, -> { order 'friends_count DESC' } 123 scope :more_popular, -> { order 'friends_count DESC' }
125 124
126 - scope :abusers, -> {  
127 - joins(:abuse_complaints).where('tasks.status = 3').distinct.select('profiles.*')  
128 - }  
129 - scope :non_abusers, -> {  
130 - distinct.select("profiles.*").  
131 - joins("LEFT JOIN tasks ON profiles.id = tasks.requestor_id AND tasks.type='AbuseComplaint'").  
132 - where("tasks.status != 3 OR tasks.id is NULL")  
133 - }  
134 -  
135 scope :admins, -> { joins(:role_assignments => :role).where('roles.key = ?', 'environment_administrator') } 125 scope :admins, -> { joins(:role_assignments => :role).where('roles.key = ?', 'environment_administrator') }
136 scope :activated, -> { joins(:user).where('users.activation_code IS NULL AND users.activated_at IS NOT NULL') } 126 scope :activated, -> { joins(:user).where('users.activation_code IS NULL AND users.activated_at IS NOT NULL') }
137 scope :deactivated, -> { joins(:user).where('NOT (users.activation_code IS NULL AND users.activated_at IS NOT NULL)') } 127 scope :deactivated, -> { joins(:user).where('NOT (users.activation_code IS NULL AND users.activated_at IS NOT NULL)') }
@@ -174,15 +164,6 @@ class Person &lt; Profile @@ -174,15 +164,6 @@ class Person &lt; Profile
174 (self.has_permission?('post_content', profile) || self.has_permission?('publish_content', profile)) 164 (self.has_permission?('post_content', profile) || self.has_permission?('publish_content', profile))
175 end 165 end
176 166
177 - # Sets the identifier for this person. Raises an exception when called on a  
178 - # existing person (since peoples' identifiers cannot be changed)  
179 - def identifier=(value)  
180 - unless self.new_record?  
181 - raise ArgumentError.new(_('An existing person cannot be renamed.'))  
182 - end  
183 - self[:identifier] = value  
184 - end  
185 -  
186 def suggested_friend_groups 167 def suggested_friend_groups
187 (friend_groups.compact + [ _('friends'), _('work'), _('school'), _('family') ]).map {|i| i if !i.empty?}.compact.uniq 168 (friend_groups.compact + [ _('friends'), _('work'), _('school'), _('family') ]).map {|i| i if !i.empty?}.compact.uniq
188 end 169 end
@@ -192,7 +173,7 @@ class Person &lt; Profile @@ -192,7 +173,7 @@ class Person &lt; Profile
192 end 173 end
193 174
194 def add_friend(friend, group = nil) 175 def add_friend(friend, group = nil)
195 - unless self.is_a_friend?(friend) 176 + unless self.is_a_friend?(friend) || friend.is_a?(ExternalPerson)
196 friendship = self.friendships.build 177 friendship = self.friendships.build
197 friendship.friend = friend 178 friendship.friend = friend
198 friendship.group = group 179 friendship.group = group
@@ -517,21 +498,6 @@ class Person &lt; Profile @@ -517,21 +498,6 @@ class Person &lt; Profile
517 leave_hash.to_json 498 leave_hash.to_json
518 end 499 end
519 500
520 - def already_reported?(profile)  
521 - abuse_reports.any? { |report| report.abuse_complaint.reported == profile && report.abuse_complaint.opened? }  
522 - end  
523 -  
524 - def register_report(abuse_report, profile)  
525 - AbuseComplaint.create!(:reported => profile, :target => profile.environment) if !profile.opened_abuse_complaint  
526 - abuse_report.abuse_complaint = profile.opened_abuse_complaint  
527 - abuse_report.reporter = self  
528 - abuse_report.save!  
529 - end  
530 -  
531 - def abuser?  
532 - AbuseComplaint.finished.where(:requestor_id => self).count > 0  
533 - end  
534 -  
535 def control_panel_settings_button 501 def control_panel_settings_button
536 {:title => _('Edit Profile'), :icon => 'edit-profile'} 502 {:title => _('Edit Profile'), :icon => 'edit-profile'}
537 end 503 end
@@ -580,6 +546,34 @@ class Person &lt; Profile @@ -580,6 +546,34 @@ class Person &lt; Profile
580 person.has_permission?(:manage_friends, self) 546 person.has_permission?(:manage_friends, self)
581 end 547 end
582 548
  549 + def data_hash(gravatar_default = nil)
  550 + friends_list = {}
  551 + enterprises = self.enterprises.map { |e| { 'name' => e.short_name, 'identifier' => e.identifier } }
  552 + self.friends.online.map do |person|
  553 + friends_list[person.identifier] = {
  554 + 'avatar' => person.profile_custom_icon(gravatar_default),
  555 + 'name' => person.short_name,
  556 + 'jid' => person.full_jid,
  557 + 'status' => person.user.chat_status,
  558 + }
  559 + end
  560 +
  561 + {
  562 + 'login' => self.identifier,
  563 + 'name' => self.name,
  564 + 'email' => self.email,
  565 + 'avatar' => self.profile_custom_icon(gravatar_default),
  566 + 'is_admin' => self.is_admin?,
  567 + 'since_month' => self.created_at.month,
  568 + 'since_year' => self.created_at.year,
  569 + 'email_domain' => self.user.enable_email ? self.user.email_domain : nil,
  570 + 'friends_list' => friends_list,
  571 + 'enterprises' => enterprises,
  572 + 'amount_of_friends' => friends_list.count,
  573 + 'chat_enabled' => self.environment.enabled?('xmpp_chat')
  574 + }
  575 + end
  576 +
583 protected 577 protected
584 578
585 def followed_by?(profile) 579 def followed_by?(profile)
app/models/profile.rb
@@ -3,9 +3,10 @@ @@ -3,9 +3,10 @@
3 # which by default is the one returned by Environment:default. 3 # which by default is the one returned by Environment:default.
4 class Profile < ApplicationRecord 4 class Profile < ApplicationRecord
5 5
6 - attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time,  
7 - :redirection_after_login, :custom_url_redirection,  
8 - :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification 6 + include ProfileEntity
  7 +
  8 + attr_accessible :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time,
  9 + :custom_url_redirection, :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification, :redirection_after_login
9 10
10 # use for internationalizable human type names in search facets 11 # use for internationalizable human type names in search facets
11 # reimplement on subclasses 12 # reimplement on subclasses
@@ -115,8 +116,6 @@ class Profile &lt; ApplicationRecord @@ -115,8 +116,6 @@ class Profile &lt; ApplicationRecord
115 } 116 }
116 scope :no_templates, -> { where is_template: false } 117 scope :no_templates, -> { where is_template: false }
117 118
118 - scope :recent, -> limit=nil { order('id DESC').limit(limit) }  
119 -  
120 119
121 # 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
122 # distance from the given location center. 121 # distance from the given location center.
@@ -224,8 +223,6 @@ class Profile &lt; ApplicationRecord @@ -224,8 +223,6 @@ class Profile &lt; ApplicationRecord
224 welcome_page && welcome_page.published ? welcome_page.body : nil 223 welcome_page && welcome_page.published ? welcome_page.body : nil
225 end 224 end
226 225
227 - has_many :search_terms, :as => :context  
228 -  
229 def scraps(scrap=nil) 226 def scraps(scrap=nil)
230 scrap = scrap.is_a?(Scrap) ? scrap.id : scrap 227 scrap = scrap.is_a?(Scrap) ? scrap.id : scrap
231 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap) 228 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap)
@@ -278,7 +275,6 @@ class Profile &lt; ApplicationRecord @@ -278,7 +275,6 @@ class Profile &lt; ApplicationRecord
278 275
279 has_many :domains, :as => :owner 276 has_many :domains, :as => :owner
280 belongs_to :preferred_domain, :class_name => 'Domain', :foreign_key => 'preferred_domain_id' 277 belongs_to :preferred_domain, :class_name => 'Domain', :foreign_key => 'preferred_domain_id'
281 - belongs_to :environment  
282 278
283 has_many :articles, :dependent => :destroy 279 has_many :articles, :dependent => :destroy
284 belongs_to :home_page, :class_name => Article.name, :foreign_key => 'home_page_id' 280 belongs_to :home_page, :class_name => Article.name, :foreign_key => 'home_page_id'
@@ -305,8 +301,6 @@ class Profile &lt; ApplicationRecord @@ -305,8 +301,6 @@ class Profile &lt; ApplicationRecord
305 has_many :profile_categorizations_including_virtual, :class_name => 'ProfileCategorization' 301 has_many :profile_categorizations_including_virtual, :class_name => 'ProfileCategorization'
306 has_many :categories_including_virtual, :through => :profile_categorizations_including_virtual, :source => :category 302 has_many :categories_including_virtual, :through => :profile_categorizations_including_virtual, :source => :category
307 303
308 - has_many :abuse_complaints, :foreign_key => 'requestor_id', :dependent => :destroy  
309 -  
310 has_many :profile_suggestions, :foreign_key => :suggestion_id, :dependent => :destroy 304 has_many :profile_suggestions, :foreign_key => :suggestion_id, :dependent => :destroy
311 305
312 def top_level_categorization 306 def top_level_categorization
@@ -401,7 +395,6 @@ class Profile &lt; ApplicationRecord @@ -401,7 +395,6 @@ class Profile &lt; ApplicationRecord
401 self.all 395 self.all
402 end 396 end
403 397
404 - validates_presence_of :identifier, :name  
405 validates_length_of :nickname, :maximum => 16, :allow_nil => true 398 validates_length_of :nickname, :maximum => 16, :allow_nil => true
406 validate :valid_template 399 validate :valid_template
407 validate :valid_identifier 400 validate :valid_identifier
@@ -416,14 +409,6 @@ class Profile &lt; ApplicationRecord @@ -416,14 +409,6 @@ class Profile &lt; ApplicationRecord
416 end 409 end
417 end 410 end
418 411
419 - before_create :set_default_environment  
420 - def set_default_environment  
421 - if self.environment.nil?  
422 - self.environment = Environment.default  
423 - end  
424 - true  
425 - end  
426 -  
427 # registar callback for creating boxes after the object is created. 412 # registar callback for creating boxes after the object is created.
428 after_create :create_default_set_of_boxes 413 after_create :create_default_set_of_boxes
429 414
@@ -496,9 +481,6 @@ class Profile &lt; ApplicationRecord @@ -496,9 +481,6 @@ class Profile &lt; ApplicationRecord
496 self.save(:validate => false) 481 self.save(:validate => false)
497 end 482 end
498 483
499 - def apply_type_specific_template(template)  
500 - end  
501 -  
502 xss_terminate :only => [ :name, :nickname, :address, :contact_phone, :description ], :on => 'validation' 484 xss_terminate :only => [ :name, :nickname, :address, :contact_phone, :description ], :on => 'validation'
503 xss_terminate :only => [ :custom_footer, :custom_header ], :with => 'white_list' 485 xss_terminate :only => [ :custom_footer, :custom_header ], :with => 'white_list'
504 486
@@ -539,10 +521,6 @@ class Profile &lt; ApplicationRecord @@ -539,10 +521,6 @@ class Profile &lt; ApplicationRecord
539 ).order('articles.published_at desc, articles.id desc') 521 ).order('articles.published_at desc, articles.id desc')
540 end 522 end
541 523
542 - def to_liquid  
543 - HashWithIndifferentAccess.new :name => name, :identifier => identifier  
544 - end  
545 -  
546 class << self 524 class << self
547 525
548 # 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
@@ -562,23 +540,6 @@ class Profile &lt; ApplicationRecord @@ -562,23 +540,6 @@ class Profile &lt; ApplicationRecord
562 environment 540 environment
563 end 541 end
564 542
565 - # returns +false+  
566 - def person?  
567 - self.kind_of?(Person)  
568 - end  
569 -  
570 - def enterprise?  
571 - self.kind_of?(Enterprise)  
572 - end  
573 -  
574 - def organization?  
575 - self.kind_of?(Organization)  
576 - end  
577 -  
578 - def community?  
579 - self.kind_of?(Community)  
580 - end  
581 -  
582 # returns false. 543 # returns false.
583 def is_validation_entity? 544 def is_validation_entity?
584 false 545 false
@@ -683,13 +644,6 @@ private :generate_url, :url_options @@ -683,13 +644,6 @@ private :generate_url, :url_options
683 self.articles.tagged_with(tag) 644 self.articles.tagged_with(tag)
684 end 645 end
685 646
686 - # Tells whether a specified profile has members or nor.  
687 - #  
688 - # On this class, returns <tt>false</tt> by default.  
689 - def has_members?  
690 - false  
691 - end  
692 -  
693 after_create :insert_default_article_set 647 after_create :insert_default_article_set
694 def insert_default_article_set 648 def insert_default_article_set
695 if template 649 if template
@@ -704,23 +658,6 @@ private :generate_url, :url_options @@ -704,23 +658,6 @@ private :generate_url, :url_options
704 end 658 end
705 end 659 end
706 660
707 - # Override this method in subclasses of Profile to create a default article  
708 - # set upon creation. Note that this method will be called *only* if there is  
709 - # no template for the type of profile (i.e. if the template was removed or in  
710 - # the creation of the template itself).  
711 - #  
712 - # This method must return an array of pre-populated articles, which will be  
713 - # associated to the profile before being saved. Example:  
714 - #  
715 - # def default_set_of_articles  
716 - # [Blog.new(:name => 'Blog'), Gallery.new(:name => 'Gallery')]  
717 - # end  
718 - #  
719 - # By default, this method returns an empty array.  
720 - def default_set_of_articles  
721 - []  
722 - end  
723 -  
724 def copy_articles_from other 661 def copy_articles_from other
725 return false if other.top_level_articles.empty? 662 return false if other.top_level_articles.empty?
726 other.top_level_articles.each do |a| 663 other.top_level_articles.each do |a|
@@ -816,19 +753,6 @@ private :generate_url, :url_options @@ -816,19 +753,6 @@ private :generate_url, :url_options
816 !forbidden.include?(cat.class) 753 !forbidden.include?(cat.class)
817 end 754 end
818 755
819 - include ActionView::Helpers::TextHelper  
820 - def short_name(chars = 40)  
821 - if self[:nickname].blank?  
822 - if chars  
823 - truncate self.name, length: chars, omission: '...'  
824 - else  
825 - self.name  
826 - end  
827 - else  
828 - self[:nickname]  
829 - end  
830 - end  
831 -  
832 def custom_header 756 def custom_header
833 self[:custom_header] || environment && environment.custom_header 757 self[:custom_header] || environment && environment.custom_header
834 end 758 end
@@ -934,14 +858,6 @@ private :generate_url, :url_options @@ -934,14 +858,6 @@ private :generate_url, :url_options
934 articles.galleries 858 articles.galleries
935 end 859 end
936 860
937 - def blocks_to_expire_cache  
938 - []  
939 - end  
940 -  
941 - def cache_keys(params = {})  
942 - []  
943 - end  
944 -  
945 validate :image_valid 861 validate :image_valid
946 862
947 def image_valid 863 def image_valid
@@ -978,16 +894,6 @@ private :generate_url, :url_options @@ -978,16 +894,6 @@ private :generate_url, :url_options
978 self.update_attribute(:layout_template, template) 894 self.update_attribute(:layout_template, template)
979 end 895 end
980 896
981 - def members_cache_key(params = {})  
982 - page = params[:npage] || '1'  
983 - sort = (params[:sort] == 'desc') ? params[:sort] : 'asc'  
984 - cache_key + '-members-page-' + page + '-' + sort  
985 - end  
986 -  
987 - def more_recent_label  
988 - _("Since: ")  
989 - end  
990 -  
991 def recent_actions 897 def recent_actions
992 tracked_actions.recent 898 tracked_actions.recent
993 end 899 end
@@ -1042,32 +948,6 @@ private :generate_url, :url_options @@ -1042,32 +948,6 @@ private :generate_url, :url_options
1042 end 948 end
1043 end 949 end
1044 950
1045 - def opened_abuse_complaint  
1046 - abuse_complaints.opened.first  
1047 - end  
1048 -  
1049 - def disable  
1050 - self.visible = false  
1051 - self.save  
1052 - end  
1053 -  
1054 - def enable  
1055 - self.visible = true  
1056 - self.save  
1057 - end  
1058 -  
1059 - def control_panel_settings_button  
1060 - {:title => _('Edit Profile'), :icon => 'edit-profile'}  
1061 - end  
1062 -  
1063 - def self.identification  
1064 - name  
1065 - end  
1066 -  
1067 - def exclude_verbs_on_activities  
1068 - %w[]  
1069 - end  
1070 -  
1071 # Customize in subclasses 951 # Customize in subclasses
1072 def activities 952 def activities
1073 self.profile_activities.includes(:activity).order('updated_at DESC') 953 self.profile_activities.includes(:activity).order('updated_at DESC')
@@ -1102,10 +982,6 @@ private :generate_url, :url_options @@ -1102,10 +982,6 @@ private :generate_url, :url_options
1102 self.active_fields 982 self.active_fields
1103 end 983 end
1104 984
1105 - def control_panel_settings_button  
1106 - {:title => _('Profile Info and settings'), :icon => 'edit-profile'}  
1107 - end  
1108 -  
1109 def followed_by?(person) 985 def followed_by?(person)
1110 person.is_member_of?(self) 986 person.is_member_of?(self)
1111 end 987 end
@@ -1133,19 +1009,4 @@ private :generate_url, :url_options @@ -1133,19 +1009,4 @@ private :generate_url, :url_options
1133 suggestion.disable if suggestion 1009 suggestion.disable if suggestion
1134 end 1010 end
1135 1011
1136 - def allow_invitation_from(person)  
1137 - false  
1138 - end  
1139 -  
1140 - def allow_post_content?(person = nil)  
1141 - person.kind_of?(Profile) && person.has_permission?('post_content', self)  
1142 - end  
1143 -  
1144 - def allow_edit?(person = nil)  
1145 - person.kind_of?(Profile) && person.has_permission?('edit_profile', self)  
1146 - end  
1147 -  
1148 - def allow_destroy?(person = nil)  
1149 - person.kind_of?(Profile) && person.has_permission?('destroy_profile', self)  
1150 - end  
1151 end 1012 end
app/models/user.rb
@@ -7,6 +7,8 @@ class User &lt; ApplicationRecord @@ -7,6 +7,8 @@ class User &lt; ApplicationRecord
7 7
8 attr_accessible :login, :email, :password, :password_confirmation, :activated_at 8 attr_accessible :login, :email, :password, :password_confirmation, :activated_at
9 9
  10 + include ExternalUser
  11 +
10 N_('Password') 12 N_('Password')
11 N_('Password confirmation') 13 N_('Password confirmation')
12 N_('Terms accepted') 14 N_('Terms accepted')
@@ -105,6 +107,8 @@ class User &lt; ApplicationRecord @@ -105,6 +107,8 @@ class User &lt; ApplicationRecord
105 has_one :person, dependent: :destroy, autosave: false 107 has_one :person, dependent: :destroy, autosave: false
106 belongs_to :environment 108 belongs_to :environment
107 109
  110 + alias_method_chain :person, :external
  111 +
108 has_many :sessions, dependent: :destroy 112 has_many :sessions, dependent: :destroy
109 # holds the current session, see lib/authenticated_system.rb 113 # holds the current session, see lib/authenticated_system.rb
110 attr_accessor :session 114 attr_accessor :session
@@ -146,7 +150,8 @@ class User &lt; ApplicationRecord @@ -146,7 +150,8 @@ class User &lt; ApplicationRecord
146 u.generate_private_token_if_not_exist 150 u.generate_private_token_if_not_exist
147 return u 151 return u
148 end 152 end
149 - return nil 153 +
  154 + return User.external_authenticate(login, password, environment)
150 end 155 end
151 156
152 def register_login 157 def register_login
@@ -380,31 +385,7 @@ class User &lt; ApplicationRecord @@ -380,31 +385,7 @@ class User &lt; ApplicationRecord
380 end 385 end
381 386
382 def data_hash(gravatar_default = nil) 387 def data_hash(gravatar_default = nil)
383 - friends_list = {}  
384 - enterprises = person.enterprises.map { |e| { 'name' => e.short_name, 'identifier' => e.identifier } }  
385 - self.person.friends.online.map do |person|  
386 - friends_list[person.identifier] = {  
387 - 'avatar' => person.profile_custom_icon(gravatar_default),  
388 - 'name' => person.short_name,  
389 - 'jid' => person.full_jid,  
390 - 'status' => person.user.chat_status,  
391 - }  
392 - end  
393 -  
394 - {  
395 - 'login' => self.login,  
396 - 'name' => self.person.name,  
397 - 'email' => self.email,  
398 - 'avatar' => self.person.profile_custom_icon(gravatar_default),  
399 - 'is_admin' => self.person.is_admin?,  
400 - 'since_month' => self.person.created_at.month,  
401 - 'since_year' => self.person.created_at.year,  
402 - 'email_domain' => self.enable_email ? self.email_domain : nil,  
403 - 'friends_list' => friends_list,  
404 - 'enterprises' => enterprises,  
405 - 'amount_of_friends' => friends_list.count,  
406 - 'chat_enabled' => person.environment.enabled?('xmpp_chat')  
407 - } 388 + self.person.data_hash(gravatar_default)
408 end 389 end
409 390
410 def self.expires_chat_status_every 391 def self.expires_chat_status_every
app/views/account/welcome.html.erb
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <%= _('%s was successfuly activated. Now you may go to your control panel or to the control panel of your enterprise') % @enterprise.name %> 4 <%= _('%s was successfuly activated. Now you may go to your control panel or to the control panel of your enterprise') % @enterprise.name %>
5 5
6 <%= button_bar do %> 6 <%= button_bar do %>
7 - <%= button 'forward', _('Go to my control panel'), :action => 'index', :controller => 'profile_editor', :profile => current_user.person.identifier %> 7 + <%= button 'forward', _('Go to my control panel'), :action => 'index', :controller => 'profile_editor', :profile => current_person.identifier %>
8 <%= button 'forward', _('Go to my enterprise control panel') % @enterprise.name, :action => 'index', :controller => 'profile_editor', :profile => @enterprise.identifier %> 8 <%= button 'forward', _('Go to my enterprise control panel') % @enterprise.name, :action => 'index', :controller => 'profile_editor', :profile => @enterprise.identifier %>
9 <% end %> 9 <% end %>
10 <% end %> 10 <% end %>
app/views/enterprise_registration/basic_information.html.erb
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 </div> 9 </div>
10 10
11 <%= button_bar do %> 11 <%= button_bar do %>
12 - <%= button :back, _('Go back'), { :profile => current_user.person.identifier, :action=>"enterprises", :controller=>"profile" }%> 12 + <%= button :back, _('Go back'), { :profile => current_person.identifier, :action=>"enterprises", :controller=>"profile" }%>
13 <% end %> 13 <% end %>
14 <% else %> 14 <% else %>
15 <div class='atention'> 15 <div class='atention'>
@@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
37 <%= template_options(:enterprises, 'create_enterprise')%> 37 <%= template_options(:enterprises, 'create_enterprise')%>
38 38
39 <%= button_bar do %> 39 <%= button_bar do %>
40 - <%= submit_button('next', _('Next'), :cancel => {:profile => current_user.person.identifier, :action=>"enterprises", :controller=>"profile"}) %> 40 + <%= submit_button('next', _('Next'), :cancel => {:profile => current_person.identifier, :action=>"enterprises", :controller=>"profile"}) %>
41 <% end %> 41 <% end %>
42 <% end %> 42 <% end %>
43 <% end %> 43 <% end %>
app/views/environment_role_manager/affiliate.html.erb
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 2
3 <%= form_tag( {:action => 'give_role'}, {:method => :post}) do %> 3 <%= form_tag( {:action => 'give_role'}, {:method => :post}) do %>
4 <%= select_tag 'role', options_for_select(@roles.map{|r|[r.name,r.id]}) %> 4 <%= select_tag 'role', options_for_select(@roles.map{|r|[r.name,r.id]}) %>
5 - <%= hidden_field_tag 'person', current_user.person.id %> 5 + <%= hidden_field_tag 'person', current_person.id %>
6 <%= button_bar do %> 6 <%= button_bar do %>
7 <%= submit_button('affiliate', _('Affiliate', :cancel => {:action => 'index'}) %> 7 <%= submit_button('affiliate', _('Affiliate', :cancel => {:action => 'index'}) %>
8 <% end %> 8 <% end %>
app/views/layouts/_user.html.erb
1 <div id="user"> 1 <div id="user">
2 <% user = (session[:user] && User.find_by(id: session[:user])) || nil %> 2 <% user = (session[:user] && User.find_by(id: session[:user])) || nil %>
  3 + <% user ||= (session[:external] && User.new(:external_person_id => session[:external])) || nil %>
3 <% if user.present? %> 4 <% if user.present? %>
4 <% user = user.person %> 5 <% user = user.person %>
5 <span class='logged-in'> 6 <span class='logged-in'>
app/views/profile_members/affiliate.html.erb
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 2
3 <%= form_tag( {:action => 'give_role'}, {:method => :post}) do %> 3 <%= form_tag( {:action => 'give_role'}, {:method => :post}) do %>
4 <%= select_tag 'role', options_for_select(@roles.map{|r|[r.name,r.id]}) %> 4 <%= select_tag 'role', options_for_select(@roles.map{|r|[r.name,r.id]}) %>
5 - <%= hidden_field_tag 'person', current_user.person.id %> 5 + <%= hidden_field_tag 'person', current_person.id %>
6 <%= button_bar do %> 6 <%= button_bar do %>
7 <%= submit_button('affiliate', _('Affiliate'), :cancel => {:action => 'index'}) %> 7 <%= submit_button('affiliate', _('Affiliate'), :cancel => {:action => 'index'}) %>
8 <% end %> 8 <% end %>
config/routes.rb
@@ -87,6 +87,9 @@ Noosfero::Application.routes.draw do @@ -87,6 +87,9 @@ Noosfero::Application.routes.draw do
87 # comments 87 # comments
88 match 'profile/:profile/comment/:action/:id', controller: 'comment', profile: /#{Noosfero.identifier_format_in_url}/i, via: :all 88 match 'profile/:profile/comment/:action/:id', controller: 'comment', profile: /#{Noosfero.identifier_format_in_url}/i, via: :all
89 89
  90 + # icon
  91 + match 'profile/:profile/icon(/:size)', controller: 'profile', action: 'icon', size: /(big|minor|thumb|portrait|icon)/, profile: /#{Noosfero.identifier_format_in_url}/i, via: :get
  92 +
90 # public profile information 93 # public profile information
91 match 'profile/:profile(/:action(/:id))', controller: 'profile', action: 'index', id: /[^\/]*/, profile: /#{Noosfero.identifier_format_in_url}/i, as: :profile, via: :all 94 match 'profile/:profile(/:action(/:id))', controller: 'profile', action: 'index', id: /[^\/]*/, profile: /#{Noosfero.identifier_format_in_url}/i, as: :profile, via: :all
92 95
db/migrate/20160420125236_add_type_to_polymorphic_profile_associations.rb 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +class AddTypeToPolymorphicProfileAssociations < ActiveRecord::Migration
  2 + def up
  3 + add_column :tasks, :reported_type, :string
  4 + add_column :abuse_reports, :reporter_type, :string
  5 + add_column :comments, :author_type, :string
  6 +
  7 + update("UPDATE tasks SET reported_type='Profile'")
  8 + update("UPDATE abuse_reports SET reporter_type='Person'")
  9 + update("UPDATE comments SET author_type='Person'")
  10 + end
  11 +
  12 + def down
  13 + remove_column :abuse_complaints, :reported_type
  14 + remove_column :abuse_reports, :reporter_type
  15 + remove_column :comments, :author_type
  16 + end
  17 +end
db/migrate/20160420140141_create_external_person.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class CreateExternalPerson < ActiveRecord::Migration
  2 + def change
  3 + create_table :external_people do |t|
  4 + t.string :name
  5 + t.string :identifier
  6 + t.string :source
  7 + t.string :email
  8 + t.integer :environment_id
  9 + t.boolean :visible, default: true
  10 + t.datetime :created_at
  11 + t.datetime :updated_at
  12 + end
  13 + end
  14 +end
@@ -23,6 +23,7 @@ ActiveRecord::Schema.define(version: 20160422163123) do @@ -23,6 +23,7 @@ ActiveRecord::Schema.define(version: 20160422163123) do
23 t.text "reason" 23 t.text "reason"
24 t.datetime "created_at" 24 t.datetime "created_at"
25 t.datetime "updated_at" 25 t.datetime "updated_at"
  26 + t.string "reporter_type"
26 end 27 end
27 28
28 create_table "action_tracker", force: :cascade do |t| 29 create_table "action_tracker", force: :cascade do |t|
@@ -289,6 +290,7 @@ ActiveRecord::Schema.define(version: 20160422163123) do @@ -289,6 +290,7 @@ ActiveRecord::Schema.define(version: 20160422163123) do
289 t.string "user_agent" 290 t.string "user_agent"
290 t.string "referrer" 291 t.string "referrer"
291 t.text "settings" 292 t.text "settings"
  293 + t.string "author_type"
292 end 294 end
293 295
294 add_index "comments", ["source_id", "spam"], name: "index_comments_on_source_id_and_spam", using: :btree 296 add_index "comments", ["source_id", "spam"], name: "index_comments_on_source_id_and_spam", using: :btree
@@ -404,6 +406,14 @@ ActiveRecord::Schema.define(version: 20160422163123) do @@ -404,6 +406,14 @@ ActiveRecord::Schema.define(version: 20160422163123) do
404 t.boolean "disable_feed_ssl", default: false 406 t.boolean "disable_feed_ssl", default: false
405 end 407 end
406 408
  409 + create_table "external_environments", force: :cascade do |t|
  410 + t.string "name"
  411 + t.string "url"
  412 + t.string "identifier"
  413 + t.string "screenshot"
  414 + t.string "thumbnail"
  415 + end
  416 +
407 create_table "external_feeds", force: :cascade do |t| 417 create_table "external_feeds", force: :cascade do |t|
408 t.string "feed_title" 418 t.string "feed_title"
409 t.datetime "fetched_at" 419 t.datetime "fetched_at"
@@ -421,6 +431,17 @@ ActiveRecord::Schema.define(version: 20160422163123) do @@ -421,6 +431,17 @@ ActiveRecord::Schema.define(version: 20160422163123) do
421 add_index "external_feeds", ["enabled"], name: "index_external_feeds_on_enabled", using: :btree 431 add_index "external_feeds", ["enabled"], name: "index_external_feeds_on_enabled", using: :btree
422 add_index "external_feeds", ["fetched_at"], name: "index_external_feeds_on_fetched_at", using: :btree 432 add_index "external_feeds", ["fetched_at"], name: "index_external_feeds_on_fetched_at", using: :btree
423 433
  434 + create_table "external_people", force: :cascade do |t|
  435 + t.string "name"
  436 + t.string "identifier"
  437 + t.string "source"
  438 + t.string "email"
  439 + t.integer "environment_id"
  440 + t.boolean "visible", default: true
  441 + t.datetime "created_at"
  442 + t.datetime "updated_at"
  443 + end
  444 +
424 create_table "favorite_enterprise_people", force: :cascade do |t| 445 create_table "favorite_enterprise_people", force: :cascade do |t|
425 t.integer "person_id" 446 t.integer "person_id"
426 t.integer "enterprise_id" 447 t.integer "enterprise_id"
@@ -432,14 +453,6 @@ ActiveRecord::Schema.define(version: 20160422163123) do @@ -432,14 +453,6 @@ ActiveRecord::Schema.define(version: 20160422163123) do
432 add_index "favorite_enterprise_people", ["person_id", "enterprise_id"], name: "index_favorite_enterprise_people_on_person_id_and_enterprise_id", using: :btree 453 add_index "favorite_enterprise_people", ["person_id", "enterprise_id"], name: "index_favorite_enterprise_people_on_person_id_and_enterprise_id", using: :btree
433 add_index "favorite_enterprise_people", ["person_id"], name: "index_favorite_enterprise_people_on_person_id", using: :btree 454 add_index "favorite_enterprise_people", ["person_id"], name: "index_favorite_enterprise_people_on_person_id", using: :btree
434 455
435 - create_table "external_environments", force: :cascade do |t|  
436 - t.string "name"  
437 - t.string "url"  
438 - t.string "identifier"  
439 - t.string "screenshot"  
440 - t.string "thumbnail"  
441 - end  
442 -  
443 create_table "friendships", force: :cascade do |t| 456 create_table "friendships", force: :cascade do |t|
444 t.integer "person_id" 457 t.integer "person_id"
445 t.integer "friend_id" 458 t.integer "friend_id"
@@ -797,6 +810,7 @@ ActiveRecord::Schema.define(version: 20160422163123) do @@ -797,6 +810,7 @@ ActiveRecord::Schema.define(version: 20160422163123) do
797 t.boolean "spam", default: false 810 t.boolean "spam", default: false
798 t.integer "responsible_id" 811 t.integer "responsible_id"
799 t.integer "closed_by_id" 812 t.integer "closed_by_id"
  813 + t.string "reported_type"
800 end 814 end
801 815
802 add_index "tasks", ["requestor_id"], name: "index_tasks_on_requestor_id", using: :btree 816 add_index "tasks", ["requestor_id"], name: "index_tasks_on_requestor_id", using: :btree
features/external_login.feature 0 → 100644
@@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
  1 +Feature: external login
  2 + As a user
  3 + I want to login using an account from a federated network
  4 + In order to view pages logged in
  5 +
  6 + @selenium
  7 + Scenario: login from portal homepage
  8 + Given feature "allow_change_of_redirection_after_login" is disabled on environment
  9 + And the following external environments
  10 + | identifier | name | url |
  11 + | test | Test | http://federated.noosfero.org |
  12 + And the following external users
  13 + | login |
  14 + | joaosilva@federated.noosfero.org |
  15 + And I am not logged in
  16 + And I go to the homepage
  17 + And I follow "Login"
  18 + And I fill in the following:
  19 + | Username / Email | joaosilva@federated.noosfero.org |
  20 + | Password | 123456 |
  21 + When I press "Log in"
  22 + Then I should be on the homepage
  23 + And I should be externally logged in as "joaosilva@federated.noosfero.org"
  24 +
  25 + @selenium
  26 + Scenario: not login from portal homepage
  27 + Given feature "allow_change_of_redirection_after_login" is disabled on environment
  28 + And the following external environments
  29 + | identifier | name | url |
  30 + | test | Test | http://federated.noosfero.org |
  31 + And I am not logged in
  32 + And I go to the homepage
  33 + And I follow "Login"
  34 + And I fill in the following:
  35 + | Username / Email | joaosilva@federated.noosfero.org |
  36 + | Password | 123456 |
  37 + When I press "Log in"
  38 + Then I should be on /account/login
  39 + And I should not be externally logged in as "joaosilva@federated.noosfero.org"
  40 +
  41 + @selenium
  42 + Scenario: not login if network is not whitelisted
  43 + Given feature "allow_change_of_redirection_after_login" is disabled on environment
  44 + And the following external users
  45 + | login |
  46 + | joaosilva@federated.noosfero.org |
  47 + And I am not logged in
  48 + And I go to the homepage
  49 + And I follow "Login"
  50 + And I fill in the following:
  51 + | Username / Email | joaosilva@federated.noosfero.org |
  52 + | Password | 123456 |
  53 + When I press "Log in"
  54 + Then I should be on /account/login
  55 + And I should not be externally logged in as "joaosilva@federated.noosfero.org"
features/step_definitions/noosfero_steps.rb
@@ -679,3 +679,36 @@ Given /^the field (.*) is public for all users$/ do |field| @@ -679,3 +679,36 @@ Given /^the field (.*) is public for all users$/ do |field|
679 person.save! 679 person.save!
680 end 680 end
681 end 681 end
  682 +
  683 +Given /^the following external users?$/ do |table|
  684 + table.hashes.each do |item|
  685 + person_data = item.dup
  686 + username, domain = person_data['login'].split('@')
  687 + response = OpenStruct.new(
  688 + code: '200',
  689 + body: {
  690 + user: {
  691 + email: username + '@ema.il',
  692 + person: {
  693 + identifier: username,
  694 + name: username,
  695 + created_at: Time.now,
  696 + }
  697 + }
  698 + }.to_json
  699 + )
  700 + Net::HTTP.stub(:post_form).and_return(response)
  701 + end
  702 +end
  703 +
  704 +Then /^I should be externally logged in as "([^@]+)@(.+)"$/ do |username, domain|
  705 + visit '/'
  706 + url = 'http://' + domain + '/' + username
  707 + page.should have_xpath("//a[@href=\"#{url}\"]")
  708 +end
  709 +
  710 +Then /^I should not be externally logged in as "([^@]+)@(.+)"$/ do |username, domain|
  711 + visit '/'
  712 + url = 'http://' + domain + '/' + username
  713 + page.should_not have_xpath("//a[@href=\"#{url}\"]")
  714 +end
features/support/env.rb
@@ -8,6 +8,7 @@ ENV[&quot;RAILS_ENV&quot;] ||= &quot;cucumber&quot; @@ -8,6 +8,7 @@ ENV[&quot;RAILS_ENV&quot;] ||= &quot;cucumber&quot;
8 8
9 require File.expand_path(File.dirname(__FILE__) + '/../../config/environment') 9 require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
10 require 'cucumber/rails' 10 require 'cucumber/rails'
  11 +require 'cucumber/rspec/doubles'
11 12
12 Capybara.ignore_hidden_elements = true 13 Capybara.ignore_hidden_elements = true
13 14
plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb
@@ -38,7 +38,7 @@ class OrganizationRatingsPluginProfileController &lt; ProfileController @@ -38,7 +38,7 @@ class OrganizationRatingsPluginProfileController &lt; ProfileController
38 38
39 def create_new_rate 39 def create_new_rate
40 @rating = OrganizationRating.new(params[:organization_rating]) 40 @rating = OrganizationRating.new(params[:organization_rating])
41 - @rating.person = current_user.person 41 + @rating.person = current_person
42 @rating.organization = profile 42 @rating.organization = profile
43 @rating.value = params[:organization_rating_value] if params[:organization_rating_value] 43 @rating.value = params[:organization_rating_value] if params[:organization_rating_value]
44 44
plugins/organization_ratings/test/functional/organization_ratings_plugin_profile_controller_test.rb
@@ -173,6 +173,7 @@ class OrganizationRatingsPluginProfileControllerTest &lt; ActionController::TestCas @@ -173,6 +173,7 @@ class OrganizationRatingsPluginProfileControllerTest &lt; ActionController::TestCas
173 173
174 logout 174 logout
175 @controller.stubs(:logged_in?).returns(false) 175 @controller.stubs(:logged_in?).returns(false)
  176 + @controller.stubs(:current_user).returns(nil)
176 177
177 get :new_rating, profile: @community.identifier 178 get :new_rating, profile: @community.identifier
178 assert_no_tag :tag => 'p', :content => /Report waiting for approval/, :attributes => {:class =>/comment-rejected-msg/} 179 assert_no_tag :tag => 'p', :content => /Report waiting for approval/, :attributes => {:class =>/comment-rejected-msg/}
plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb
@@ -5,11 +5,11 @@ @@ -5,11 +5,11 @@
5 5
6 <div class="star-profile-information"> 6 <div class="star-profile-information">
7 <div class="star-profile-image"> 7 <div class="star-profile-image">
8 - <%= link_to profile_image(current_user.person, :portrait), current_user.person.url %> 8 + <%= link_to profile_image(current_person, :portrait), current_person.url %>
9 </div> 9 </div>
10 10
11 <div class="star-profile-name"> 11 <div class="star-profile-name">
12 - <%= link_to current_user.person.name, current_user.person.url %> 12 + <%= link_to current_person.name, current_person.url %>
13 </div> 13 </div>
14 </div> 14 </div>
15 15
@@ -55,14 +55,14 @@ @@ -55,14 +55,14 @@
55 </div> 55 </div>
56 <% elsif env_organization_ratings_config.vote_once %> 56 <% elsif env_organization_ratings_config.vote_once %>
57 <div class="star-rate-form rating-vote-once"> 57 <div class="star-rate-form rating-vote-once">
58 - <%= _("Hi, %s! The administrators set that you can vote") % current_user.name %> 58 + <%= _("Hi, %s! The administrators set that you can vote") % current_person.name %>
59 <strong><%= _("only once") %></strong> 59 <strong><%= _("only once") %></strong>
60 <%= _("for this %s.") % profile.class.name.downcase %> 60 <%= _("for this %s.") % profile.class.name.downcase %>
61 <%= render :partial => 'shared/rating_button', :locals => { :disabled => true } %> 61 <%= render :partial => 'shared/rating_button', :locals => { :disabled => true } %>
62 </div> 62 </div>
63 <% else %> 63 <% else %>
64 <div class="star-rate-form rating-cooldown"> 64 <div class="star-rate-form rating-cooldown">
65 - <%= _("Hi, %s! The administrators set the minimum time of") % current_user.name %> 65 + <%= _("Hi, %s! The administrators set the minimum time of") % current_person.name %>
66 <strong><%= _("%d hour(s)") % env_organization_ratings_config.cooldown %></strong> 66 <strong><%= _("%d hour(s)") % env_organization_ratings_config.cooldown %></strong>
67 <%= _("between each evaluation.") %> 67 <%= _("between each evaluation.") %>
68 68
plugins/organization_ratings/views/shared/_make_report_block.html.erb
1 -<% logged_in_image = link_to profile_image(current_user.person, :portrait), current_user.person.url if current_user %>  
2 -<% logged_in_name = link_to current_user.person.name, current_user.person.url if current_user %>  
3 -<% logged_out_image = image_tag('plugins/organization_ratings/images/user-not-logged.png') %> 1 +<% logged_in_image = link_to profile_image(current_person, :portrait), current_person.url if current_user %>
  2 +<% logged_in_name = link_to current_person.name, current_person.url if current_user %>
  3 +<% logged_out_image = image_tag('plugins/organization_ratings/public/images/user-not-logged.png') %>
4 4
5 <div class="make-report-block"> 5 <div class="make-report-block">
6 <div class="star-profile-information"> 6 <div class="star-profile-information">
@@ -26,4 +26,4 @@ @@ -26,4 +26,4 @@
26 </div> 26 </div>
27 <% end %> 27 <% end %>
28 </div> 28 </div>
29 -</div>  
30 \ No newline at end of file 29 \ No newline at end of file
  30 +</div>
plugins/responsive/lib/ext/application_helper.rb
@@ -140,7 +140,7 @@ module ApplicationHelper @@ -140,7 +140,7 @@ module ApplicationHelper
140 [s_('contents|Most commented'), {host: host, controller: :search, action: :contents, filter: 'more_comments'}], 140 [s_('contents|Most commented'), {host: host, controller: :search, action: :contents, filter: 'more_comments'}],
141 ] 141 ]
142 if logged_in? 142 if logged_in?
143 - links.push [_('New content'), '', modal_options({href: url_for({controller: 'cms', action: 'new', profile: current_user.login, cms: true})})] 143 + links.push [_('New content'), '', modal_options({href: url_for({controller: 'cms', action: 'new', profile: current_person.identifier, cms: true})})]
144 end 144 end
145 145
146 content_tag :li, class: 'dropdown' do 146 content_tag :li, class: 'dropdown' do
@@ -170,8 +170,8 @@ module ApplicationHelper @@ -170,8 +170,8 @@ module ApplicationHelper
170 [s_('people|More popular'), {host: host, controller: :search, action: :people, filter: 'more_popular'}], 170 [s_('people|More popular'), {host: host, controller: :search, action: :people, filter: 'more_popular'}],
171 ] 171 ]
172 if logged_in? 172 if logged_in?
173 - links.push [_('My friends'), {profile: current_user.login, controller: 'friends'}]  
174 - links.push [_('Invite friends'), {profile: current_user.login, controller: 'invite', action: 'friends'}] 173 + links.push [_('My friends'), {profile: current_person.identifier, controller: 'friends'}]
  174 + links.push [_('Invite friends'), {profile: current_person.identifier, controller: 'invite', action: 'friends'}]
175 end 175 end
176 176
177 content_tag :li, class: 'dropdown' do 177 content_tag :li, class: 'dropdown' do
@@ -201,8 +201,8 @@ module ApplicationHelper @@ -201,8 +201,8 @@ module ApplicationHelper
201 [s_('communities|More popular'), {host: host, controller: :search, action: :communities, filter: 'more_popular'}], 201 [s_('communities|More popular'), {host: host, controller: :search, action: :communities, filter: 'more_popular'}],
202 ] 202 ]
203 if logged_in? 203 if logged_in?
204 - links.push [_('My communities'), {profile: current_user.login, controller: 'memberships'}]  
205 - links.push [_('New community'), {profile: current_user.login, controller: 'memberships', action: 'new_community'}] 204 + links.push [_('My communities'), {profile: current_person.identifier, controller: 'memberships'}]
  205 + links.push [_('New community'), {profile: current_person.identifier, controller: 'memberships', action: 'new_community'}]
206 end 206 end
207 207
208 content_tag :li, class: 'dropdown' do 208 content_tag :li, class: 'dropdown' do
plugins/sniffer/views/blocks/interests.html.erb
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 </ul> 8 </ul>
9 9
10 <div> 10 <div>
11 - <% if logged_in? and (current_user.person.is_admin?(environment) or profile.admins.include?(current_user.person)) %> 11 + <% if logged_in? and (current_person.is_admin?(environment) or profile.admins.include?(current_person)) %>
12 <%= _('Edit %{inputs} and %{block.interests}') % { 12 <%= _('Edit %{inputs} and %{block.interests}') % {
13 :inputs => link_to(_("products' inputs"), :controller => :manage_products, :action => :index), 13 :inputs => link_to(_("products' inputs"), :controller => :manage_products, :action => :index),
14 :interests => link_to(_('declared interests'), :controller => :sniffer_plugin_myprofile, :action => :edit), 14 :interests => link_to(_('declared interests'), :controller => :sniffer_plugin_myprofile, :action => :edit),
plugins/solr/lib/solr_plugin/search_helper.rb
@@ -12,9 +12,9 @@ module SolrPlugin::SearchHelper @@ -12,9 +12,9 @@ module SolrPlugin::SearchHelper
12 :products => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')}, 12 :products => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
13 :more_recent, {:label => c_('More recent'), :solr_opts => {:sort => 'updated_at desc, score desc'}}, 13 :more_recent, {:label => c_('More recent'), :solr_opts => {:sort => 'updated_at desc, score desc'}},
14 :name, {:label => _('Name'), :solr_opts => {:sort => 'solr_plugin_name_sortable asc'}}, 14 :name, {:label => _('Name'), :solr_opts => {:sort => 'solr_plugin_name_sortable asc'}},
15 - :closest, {:label => _('Closest to me'), :if => proc{ logged_in? && (profile=current_user.person).lat && profile.lng }, 15 + :closest, {:label => _('Closest to me'), :if => proc{ logged_in? && (profile=current_person).lat && profile.lng },
16 :solr_opts => {:sort => "geodist() asc", 16 :solr_opts => {:sort => "geodist() asc",
17 - :latitude => proc{ current_user.person.lat }, :longitude => proc{ current_user.person.lng }}}, 17 + :latitude => proc{ current_person.lat }, :longitude => proc{ current_person.lng }}},
18 ], 18 ],
19 :events => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')}, 19 :events => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')},
20 :name, {:label => _('Name'), :solr_opts => {:sort => 'solr_plugin_name_sortable asc'}}, 20 :name, {:label => _('Name'), :solr_opts => {:sort => 'solr_plugin_name_sortable asc'}},
test/api/federation/webfinger_test.rb 0 → 100644
@@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
  1 +require_relative '../test_helper'
  2 +
  3 +class WebfingerTest < ActiveSupport::TestCase
  4 + def setup
  5 + Domain.create(name: 'example.com')
  6 + Environment.default.domains << Domain.last
  7 + User.create(login: 'ze', email: 'ze@localdomain.localdomain',
  8 + password: 'zeze', password_confirmation: 'zeze')
  9 + end
  10 +
  11 + should 'return correct user via webfinger url' do
  12 + get '.well-known/webfinger?resource=acct%3Aze%40example.com'
  13 + webfinger = JSON.parse(last_response.body)
  14 + assert_equal 200, last_response.status
  15 + assert_equal webfinger['subject'], 'acct:ze@example.com'
  16 + end
  17 +
  18 + should 'not return json when user not found' do
  19 + invalid_user = 'invalid_user_in_url'
  20 + get ".well-known/webfinger?resource=acct%3A#{invalid_user}%40example.com"
  21 + assert_equal 404, last_response.status
  22 + end
  23 +
  24 + should 'return correct article via webfinger url' do
  25 + a = fast_create(Article, name: 'my article', profile_id: 1)
  26 + get ".well-known/webfinger?resource=http://example.com/article/id/#{a.id}"
  27 + webfinger = JSON.parse(last_response.body)
  28 + assert_equal 200, last_response.status
  29 + assert_equal webfinger['subject'], "http://example.com/article/id/#{a.id}"
  30 + end
  31 +
  32 + should 'not return json when domain is invalid' do
  33 + invalid_domain = 'doest_not_exist.com'
  34 + get ".well-known/webfinger?resource=http://#{invalid_domain}/article/id/1"
  35 + assert_equal 404, last_response.status
  36 + end
  37 +
  38 + should 'not return json when entity is not found' do
  39 + get '.well-known/webfinger?resource=http://example.com/article/id/999999'
  40 + assert_equal 404, last_response.status
  41 + end
  42 +
  43 + should 'not return json when entity does not exist' do
  44 + get '.well-known/webfinger?resource=http://example.com/doest_not_exist/id/1'
  45 + assert_equal 404, last_response.status
  46 + end
  47 +
  48 + should 'not return json when request is not http' do
  49 + not_http_url = 'kkttc://example.com/article/id/1'
  50 + get ".well-known/webfinger?resource=#{not_http_url}"
  51 + assert_equal 404, last_response.status
  52 + end
  53 +end
test/api/people_test.rb
@@ -369,6 +369,44 @@ class PeopleTest &lt; ActiveSupport::TestCase @@ -369,6 +369,44 @@ class PeopleTest &lt; ActiveSupport::TestCase
369 assert_equal "www.blog.org", json['person']['additional_data']['Custom Blog'] 369 assert_equal "www.blog.org", json['person']['additional_data']['Custom Blog']
370 end 370 end
371 371
  372 + should 'return portrait icon if size is not provided and there is a profile image' do
  373 + img = Image.create!(uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'))
  374 + profile = fast_create(Person, image_id: img.id)
  375 +
  376 + get "/api/v1/people/#{profile.id}/icon?#{params.to_query}"
  377 + assert_equal 200, last_response.status
  378 + json = JSON.parse(last_response.body)
  379 + assert_match(/^https?:\/\/.*portrait\.png$/, json['icon'])
  380 + end
  381 +
  382 + should 'return icon in provided size if there is a profile image' do
  383 + img = Image.create!(uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'))
  384 + profile = fast_create(Person, image_id: img.id)
  385 +
  386 + get "/api/v1/people/#{profile.id}/icon?#{params.to_query}&size=big"
  387 + assert_equal 200, last_response.status
  388 + json = JSON.parse(last_response.body)
  389 + assert_match(/^https?:\/\/.*big\.png$/, json['icon'])
  390 + end
  391 +
  392 + should 'return icon from gravatar without size if there is no profile image' do
  393 + profile = create_user('test-user').person
  394 +
  395 + get "/api/v1/people/#{profile.id}/icon?#{params.to_query}"
  396 + assert_equal 200, last_response.status
  397 + json = JSON.parse(last_response.body)
  398 + assert_match(/^https:\/\/www\.gravatar\.com.*size=64/, json['icon'])
  399 + end
  400 +
  401 + should 'return icon from gravatar with size if there is no profile image' do
  402 + profile = create_user('test-user').person
  403 +
  404 + get "/api/v1/people/#{profile.id}/icon?#{params.to_query}&size=big"
  405 + assert_equal 200, last_response.status
  406 + json = JSON.parse(last_response.body)
  407 + assert_match(/^https:\/\/www\.gravatar\.com.*size=150/, json['icon'])
  408 + end
  409 +
372 PERSON_ATTRIBUTES = %w(vote_count comments_count articles_count following_articles_count) 410 PERSON_ATTRIBUTES = %w(vote_count comments_count articles_count following_articles_count)
373 411
374 PERSON_ATTRIBUTES.map do |attribute| 412 PERSON_ATTRIBUTES.map do |attribute|
test/functional/comment_controller_test.rb
@@ -487,7 +487,7 @@ class CommentControllerTest &lt; ActionController::TestCase @@ -487,7 +487,7 @@ class CommentControllerTest &lt; ActionController::TestCase
487 should 'edit comment from a page' do 487 should 'edit comment from a page' do
488 login_as profile.identifier 488 login_as profile.identifier
489 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') 489 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
490 - comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile.id) 490 + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile.id, :author_type => 'Person')
491 491
492 get :edit, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' } 492 get :edit, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
493 assert_tag :tag => 'textarea', :attributes => {:id => 'comment_body'}, :content => /Original comment/ 493 assert_tag :tag => 'textarea', :attributes => {:id => 'comment_body'}, :content => /Original comment/
@@ -522,7 +522,7 @@ class CommentControllerTest &lt; ActionController::TestCase @@ -522,7 +522,7 @@ class CommentControllerTest &lt; ActionController::TestCase
522 should 'be able to update a comment' do 522 should 'be able to update a comment' do
523 login_as profile.identifier 523 login_as profile.identifier
524 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false) 524 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false)
525 - comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile) 525 + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile, :author_type => 'Person')
526 526
527 xhr :post, :update, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' } 527 xhr :post, :update, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' }
528 assert ActiveSupport::JSON.decode(@response.body)["ok"], "attribute ok expected to be true" 528 assert ActiveSupport::JSON.decode(@response.body)["ok"], "attribute ok expected to be true"
test/functional/profile_controller_test.rb
@@ -1932,4 +1932,37 @@ class ProfileControllerTest &lt; ActionController::TestCase @@ -1932,4 +1932,37 @@ class ProfileControllerTest &lt; ActionController::TestCase
1932 assert_redirected_to :controller => 'account', :action => 'login' 1932 assert_redirected_to :controller => 'account', :action => 'login'
1933 end 1933 end
1934 1934
  1935 + should 'return portrait icon if size is not provided and there is a profile image' do
  1936 + img = Image.create!(uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'))
  1937 + profile = fast_create(Person, image_id: img.id)
  1938 +
  1939 + get :icon, profile: profile.identifier, size: nil
  1940 + assert_response :success
  1941 + assert_equal 'image/png', @response.header['Content-Type']
  1942 + assert File.exists?(assigns(:file))
  1943 + end
  1944 +
  1945 + should 'return icon in provided size if there is a profile image' do
  1946 + img = Image.create!(uploaded_data: fixture_file_upload('/files/rails.png', 'image/png'))
  1947 + profile = fast_create(Person, image_id: img.id)
  1948 +
  1949 + get :icon, profile: profile.identifier, size: :big
  1950 + assert_response :success
  1951 + assert_equal 'image/png', @response.header['Content-Type']
  1952 + assert File.exists?(assigns(:file))
  1953 + end
  1954 +
  1955 + should 'return icon from gravatar without size if there is no profile image' do
  1956 + profile = fast_create(Person)
  1957 +
  1958 + get :icon, profile: profile.identifier
  1959 + assert_redirected_to /^https:\/\/www\.gravatar\.com\/.*/
  1960 + end
  1961 +
  1962 + should 'return icon from gravatar with size if there is no profile image' do
  1963 + profile = fast_create(Person)
  1964 +
  1965 + get :icon, profile: profile.identifier, size: :thumb
  1966 + assert_redirected_to /^https:\/\/www\.gravatar\.com\/.*/
  1967 + end
1935 end 1968 end
test/integration/routing_test.rb
@@ -276,4 +276,11 @@ class RoutingTest &lt; ActionDispatch::IntegrationTest @@ -276,4 +276,11 @@ class RoutingTest &lt; ActionDispatch::IntegrationTest
276 assert_routing('/profile/~', :controller => 'profile', :profile => '~', :action => 'index') 276 assert_routing('/profile/~', :controller => 'profile', :profile => '~', :action => 'index')
277 end 277 end
278 278
  279 + should 'have route to profile icon without size' do
  280 + assert_routing('/profile/ze/icon', controller: 'profile', profile: 'ze', action: 'icon')
  281 + end
  282 +
  283 + should 'have route to profile icon with supported size' do
  284 + assert_routing('/profile/ze/icon/big', controller: 'profile', profile: 'ze', action: 'icon', size: 'big')
  285 + end
279 end 286 end
test/unit/comment_test.rb
@@ -23,17 +23,14 @@ class CommentTest &lt; ActiveSupport::TestCase @@ -23,17 +23,14 @@ class CommentTest &lt; ActiveSupport::TestCase
23 end 23 end
24 end 24 end
25 25
26 - should 'record authenticated author' do 26 + should 'record authenticated author polymorphically' do
27 c = Comment.new 27 c = Comment.new
28 - assert_raise ActiveRecord::AssociationTypeMismatch do  
29 - c.author = 1  
30 - end  
31 - assert_raise ActiveRecord::AssociationTypeMismatch do  
32 - c.author = Profile  
33 - end  
34 assert_nothing_raised do 28 assert_nothing_raised do
35 c.author = Person.new 29 c.author = Person.new
36 end 30 end
  31 + assert_nothing_raised do
  32 + c.author = ExternalPerson.new
  33 + end
37 end 34 end
38 35
39 should 'record unauthenticated author' do 36 should 'record unauthenticated author' do
test/unit/external_person_test.rb 0 → 100644
@@ -0,0 +1,137 @@ @@ -0,0 +1,137 @@
  1 +# encoding: UTF-8
  2 +require_relative "../test_helper"
  3 +
  4 +class ExternalPersonTest < ActiveSupport::TestCase
  5 + fixtures :environments
  6 +
  7 + def setup
  8 + @external_person = ExternalPerson.create!(identifier: 'johnlock',
  9 + name: 'John Lock',
  10 + source: 'anerenvironment.org',
  11 + email: 'john@lock.org',
  12 + created_at: Date.yesterday
  13 + )
  14 + end
  15 +
  16 + should 'have no permissions' do
  17 + assert_equivalent [], @external_person.role_assignments
  18 + refute @external_person.has_permission?(:manage_friends)
  19 + end
  20 +
  21 + should 'have no suggested profiles' do
  22 + assert_equivalent [], @external_person.suggested_communities
  23 + assert_equivalent [], @external_person.suggested_people
  24 + assert_equivalent [], @external_person.suggested_profiles
  25 + end
  26 +
  27 + should 'have no friendships' do
  28 + refute @external_person.add_friend(fast_create(Person))
  29 + assert_equivalent [], @external_person.friendships
  30 + end
  31 +
  32 + should 'not be a member of any communities' do
  33 + community = fast_create(Community)
  34 + refute community.add_member(@external_person)
  35 + assert_equivalent [], @external_person.memberships
  36 + end
  37 +
  38 + should 'not have any favorite enterprises' do
  39 + assert_equivalent [], @external_person.favorite_enterprises
  40 + end
  41 +
  42 + should 'be a person' do
  43 + assert @external_person.person?
  44 + end
  45 +
  46 + should 'not be a community, organization or enterprise' do
  47 + refute @external_person.community?
  48 + refute @external_person.enterprise?
  49 + refute @external_person.organization?
  50 + end
  51 +
  52 + should 'never be an admin for environments' do
  53 + refute @external_person.is_admin?
  54 + env = Environment.default
  55 + env.add_admin @external_person
  56 + refute @external_person.is_admin?(env)
  57 + assert_equivalent [], env.admins
  58 + end
  59 +
  60 + should 'redirect after login based on environment settings' do
  61 + assert_respond_to ExternalPerson.new, :preferred_login_redirection
  62 + environment = fast_create(Environment, :redirection_after_login => 'site_homepage')
  63 + profile = fast_create(ExternalPerson, :environment_id => environment.id)
  64 + assert_equal 'site_homepage', profile.preferred_login_redirection
  65 + end
  66 +
  67 + should 'have an avatar from its original environment' do
  68 + assert_match(/http:\/\/#{@external_person.source}\/.*/, @external_person.avatar)
  69 + end
  70 +
  71 + should 'generate a custom profile icon based on its avatar' do
  72 + assert_match(/http:\/\/#{@external_person.source}\/.*/, @external_person.profile_custom_icon)
  73 + end
  74 +
  75 + should 'have an url to its profile on its original environment' do
  76 + assert_match(/http:\/\/#{@external_person.source}\/profile\/.*/, @external_person.url)
  77 + end
  78 +
  79 + should 'have a public profile url' do
  80 + assert_match(/http:\/\/#{@external_person.source}\/profile\/.*/, @external_person.public_profile_url)
  81 + end
  82 +
  83 + should 'have an admin url to its profile on its original environment' do
  84 + assert_match(/http:\/\/#{@external_person.source}\/myprofile\/.*/, @external_person.admin_url)
  85 + end
  86 +
  87 + should 'never be a friend of another person' do
  88 + friend = fast_create(Person)
  89 + friend.add_friend @external_person
  90 + refute @external_person.is_a_friend?(friend)
  91 + refute friend.is_a_friend?(@external_person)
  92 + end
  93 +
  94 + should 'never send a friend request to another person' do
  95 + friend = fast_create(Person)
  96 + friend.add_friend @external_person
  97 + refute friend.already_request_friendship?(@external_person)
  98 + @external_person.add_friend(friend)
  99 + refute @external_person.already_request_friendship?(friend)
  100 + end
  101 +
  102 + should 'not follow another profile' do
  103 + friend = fast_create(Person)
  104 + friend.add_friend @external_person
  105 + refute @external_person.follows?(friend)
  106 + refute friend.follows?(@external_person)
  107 + end
  108 +
  109 + should 'have an image' do
  110 + assert_not_nil @external_person.image
  111 + end
  112 +
  113 + should 'profile image has public filename and mimetype' do
  114 + assert_not_nil @external_person.image.public_filename
  115 + assert_not_nil @external_person.image.content_type
  116 + end
  117 +
  118 + should 'respond to all instance methods in Profile' do
  119 + methods = Profile.public_instance_methods(false)
  120 + methods.each do |method|
  121 + # We test if ExternalPerson responds to same methods as Profile, but we
  122 + # skip methods generated by plugins, libs and validations, which are
  123 + # usually only used internally
  124 + assert_respond_to ExternalPerson.new, method.to_sym unless method =~ /type_name|^autosave_.*|^after_.*|^before_.*|validate_.*|^attribute_.*|.*_?tags?_?.*|^custom_value.*|^custom_context.*|^xss.*|bar/
  125 + end
  126 + end
  127 +
  128 + should 'respond to all instance methods in Person' do
  129 + methods = Person.public_instance_methods(false)
  130 + methods.each do |method|
  131 + # We test if ExternalPerson responds to same methods as Person, but we
  132 + # skip methods generated by plugins, libs and validations, which are
  133 + # usually only used internally
  134 + assert_respond_to ExternalPerson.new, method.to_sym unless method =~ /^autosave_.*|validate_.*|^before_.*|^after_.*|^assignment_.*|^(city|state)_.*/
  135 + end
  136 + end
  137 +end
test/unit/person_test.rb
@@ -1831,9 +1831,9 @@ class PersonTest &lt; ActiveSupport::TestCase @@ -1831,9 +1831,9 @@ class PersonTest &lt; ActiveSupport::TestCase
1831 p1 = fast_create(Person) 1831 p1 = fast_create(Person)
1832 p2 = fast_create(Person) 1832 p2 = fast_create(Person)
1833 article = fast_create(Article) 1833 article = fast_create(Article)
1834 - c1 = fast_create(Comment, :source_id => article.id, :author_id => p1.id)  
1835 - c2 = fast_create(Comment, :source_id => article.id, :author_id => p2.id)  
1836 - c3 = fast_create(Comment, :source_id => article.id, :author_id => p1.id) 1834 + c1 = fast_create(Comment, :source_id => article.id, :author_id => p1.id, :author_type => 'Profile')
  1835 + c2 = fast_create(Comment, :source_id => article.id, :author_id => p2.id, :author_type => 'Profile')
  1836 + c3 = fast_create(Comment, :source_id => article.id, :author_id => p1.id, :author_type => 'Profile')
1837 1837
1838 assert_equivalent [c1,c3], p1.comments 1838 assert_equivalent [c1,c3], p1.comments
1839 end 1839 end
test/unit/user_test.rb
@@ -759,6 +759,14 @@ class UserTest &lt; ActiveSupport::TestCase @@ -759,6 +759,14 @@ class UserTest &lt; ActiveSupport::TestCase
759 end 759 end
760 end 760 end
761 761
  762 + should 'get person or external person' do
  763 + user = create_user('new_user')
  764 + assert_kind_of Person, user.person
  765 + user.external_person_id = ExternalPerson.create!(identifier: 'new_user', name: 'New User', email: 'newuser@usermail.com', source: 'federated.noosfero.com', created_at: Date.today).id
  766 + assert_kind_of ExternalPerson, user.person
  767 + assert_kind_of Person, user.person_without_external
  768 + end
  769 +
762 protected 770 protected
763 def new_user(options = {}) 771 def new_user(options = {})
764 user = User.new({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options)) 772 user = User.new({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
vendor/plugins/noosfero_caching/init.rb
@@ -27,7 +27,7 @@ module NoosferoHttpCaching @@ -27,7 +27,7 @@ module NoosferoHttpCaching
27 end 27 end
28 28
29 def noosfero_session_check 29 def noosfero_session_check
30 - headers["X-Noosfero-Auth"] = (session[:user] != nil).to_s 30 + headers["X-Noosfero-Auth"] = (session[:user] != nil || session[:external] != nil).to_s
31 end 31 end
32 32
33 class Middleware 33 class Middleware