From 43992a30635c625f7b38bfc7d4854ee86a75646f Mon Sep 17 00:00:00 2001 From: Larissa Reis Date: Wed, 13 Apr 2016 03:59:18 -0300 Subject: [PATCH] Creates External Profile model for federated users --- app/api/helpers.rb | 3 +++ app/api/v1/people.rb | 17 +++++++++++++++++ app/concerns/authenticated_system.rb | 23 ++++++++++++++++++++--- app/controllers/application_controller.rb | 5 ++++- app/controllers/my_profile/email_templates_controller.rb | 2 +- app/controllers/public/profile_controller.rb | 17 ++++++++++++++--- app/controllers/public/search_controller.rb | 2 +- app/helpers/application_helper.rb | 16 ++++++++-------- app/helpers/theme_loader_helper.rb | 6 +++--- app/models/abuse_complaint.rb | 2 +- app/models/abuse_report.rb | 2 +- app/models/comment.rb | 2 +- app/models/concerns/external_user.rb | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/models/concerns/human.rb | 42 ++++++++++++++++++++++++++++++++++++++++++ app/models/concerns/profile_entity.rb | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/models/environment.rb | 4 ++++ app/models/external_person.rb | 290 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/models/person.rb | 68 +++++++++++++++++++++++++++++++------------------------------------- app/models/profile.rb | 147 ++++----------------------------------------------------------------------------------------------------------------------------------------------- app/models/user.rb | 33 +++++++-------------------------- app/views/account/welcome.html.erb | 2 +- app/views/enterprise_registration/basic_information.html.erb | 4 ++-- app/views/environment_role_manager/affiliate.html.erb | 2 +- app/views/layouts/_user.html.erb | 1 + app/views/profile_members/affiliate.html.erb | 2 +- config/routes.rb | 3 +++ db/migrate/20160420125236_add_type_to_polymorphic_profile_associations.rb | 17 +++++++++++++++++ db/migrate/20160420140141_create_external_person.rb | 14 ++++++++++++++ db/schema.rb | 30 ++++++++++++++++++++++-------- features/federated_login.feature | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ features/step_definitions/noosfero_steps.rb | 33 +++++++++++++++++++++++++++++++++ features/support/env.rb | 1 + plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb | 2 +- plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb | 8 ++++---- plugins/organization_ratings/views/shared/_make_report_block.html.erb | 6 +++--- plugins/responsive/lib/ext/application_helper.rb | 10 +++++----- plugins/sniffer/views/blocks/interests.html.erb | 2 +- plugins/solr/lib/solr_plugin/search_helper.rb | 4 ++-- test/api/people_test.rb | 38 ++++++++++++++++++++++++++++++++++++++ test/functional/comment_controller_test.rb | 4 ++-- test/functional/profile_controller_test.rb | 33 +++++++++++++++++++++++++++++++++ test/integration/routing_test.rb | 7 +++++++ test/unit/comment_test.rb | 11 ++++------- test/unit/external_person_test.rb | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/unit/person_test.rb | 6 +++--- test/unit/user_test.rb | 8 ++++++++ vendor/plugins/noosfero_caching/init.rb | 2 +- 47 files changed, 1100 insertions(+), 271 deletions(-) create mode 100644 app/models/concerns/external_user.rb create mode 100644 app/models/concerns/human.rb create mode 100644 app/models/concerns/profile_entity.rb create mode 100644 app/models/external_person.rb create mode 100644 db/migrate/20160420125236_add_type_to_polymorphic_profile_associations.rb create mode 100644 db/migrate/20160420140141_create_external_person.rb create mode 100644 features/federated_login.feature create mode 100644 test/unit/external_person_test.rb diff --git a/app/api/helpers.rb b/app/api/helpers.rb index 2a26b38..1e39131 100644 --- a/app/api/helpers.rb +++ b/app/api/helpers.rb @@ -10,6 +10,9 @@ module Api include Noosfero::Plugin::HotSpot include ForgotPasswordHelper include SearchTermHelper + include ProfileImageHelper + include Noosfero::Gravatar + include ThemeLoaderHelper def set_locale I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en') diff --git a/app/api/v1/people.rb b/app/api/v1/people.rb index d12626d..767fd4f 100644 --- a/app/api/v1/people.rb +++ b/app/api/v1/people.rb @@ -108,6 +108,23 @@ module Api end present output end + + desc "Return the person profile picture (you can optionally pass a 'size' parameter)" + get ":id/icon" do + person = environment.people.find(params[:id]) + + size = params[:size] || :portrait + image = profile_icon(person, size.to_sym) + output = {} + + unless image.match(/^\/\/www\.gravatar\.com/).nil? + output[:icon] = 'https:' + image + else + output[:icon] = request.url.gsub(/\/api\/.*/, '') + image + end + + present output + end end resource :profiles do diff --git a/app/concerns/authenticated_system.rb b/app/concerns/authenticated_system.rb index db82d73..cfda584 100644 --- a/app/concerns/authenticated_system.rb +++ b/app/concerns/authenticated_system.rb @@ -25,7 +25,20 @@ module AuthenticatedSystem # Accesses the current user from the session. def current_user user_id = session[:user] @current_user ||= begin - user = User.find_by id: user_id if user_id + user = nil + if session[:external] + user = User.new #FIXME: User needs to have at least email + external_person = ExternalPerson.where(id: session[:external]).last + if external_person + user.external_person_id = external_person.id + user.email = external_person.email + else + session[:external] = nil + end + else + id = session[:user] + user = User.where(id: id).first if id + end user.session = session if user User.current = user user @@ -37,9 +50,13 @@ module AuthenticatedSystem if new_user.nil? session.delete(:user) else - session[:user] = new_user.id + if new_user.id + session[:user] = new_user.id + else + session[:external] = new_user.external_person_id + end new_user.session = session - new_user.register_login + new_user.register_login if new_user.id end @current_user = User.current = new_user end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c4301a9..a1fdebb 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -8,6 +8,7 @@ class ApplicationController < ActionController::Base before_filter :allow_cross_domain_access include AuthenticatedSystem + before_filter :require_login_for_environment, :if => :private_environment? before_filter :verify_members_whitelist, :if => [:private_environment?, :user] @@ -120,7 +121,9 @@ class ApplicationController < ActionController::Base end def user - current_user.person if logged_in? + if logged_in? + current_user.person || current_user.external_person + end end alias :current_person :user diff --git a/app/controllers/my_profile/email_templates_controller.rb b/app/controllers/my_profile/email_templates_controller.rb index e15e4b6..02a68c9 100644 --- a/app/controllers/my_profile/email_templates_controller.rb +++ b/app/controllers/my_profile/email_templates_controller.rb @@ -64,7 +64,7 @@ class EmailTemplatesController < ApplicationController private def template_params - {:profile_name => current_user.name, :environment_name => environment.name } + {:profile_name => current_person.name, :environment_name => environment.name } end def template_params_allowed params diff --git a/app/controllers/public/profile_controller.rb b/app/controllers/public/profile_controller.rb index cccc352..b709b5e 100644 --- a/app/controllers/public/profile_controller.rb +++ b/app/controllers/public/profile_controller.rb @@ -176,7 +176,7 @@ class ProfileController < PublicController end def unblock - if current_user.person.is_admin?(profile.environment) + if current_person.is_admin?(profile.environment) profile.unblock session[:notice] = _("You have unblocked %s successfully. ") % profile.name redirect_to :controller => 'profile', :action => 'index' @@ -187,7 +187,7 @@ class ProfileController < PublicController end def leave_scrap - sender = params[:sender_id].nil? ? current_user.person : Person.find(params[:sender_id]) + sender = params[:sender_id].nil? ? current_person : Person.find(params[:sender_id]) receiver = params[:receiver_id].nil? ? @profile : Person.find(params[:receiver_id]) @scrap = Scrap.new(params[:scrap]) @scrap.sender= sender @@ -270,7 +270,7 @@ class ProfileController < PublicController def remove_scrap begin - scrap = current_user.person.scraps(params[:scrap_id]) + scrap = current_person.scraps(params[:scrap_id]) scrap.destroy finish_successful_removal 'Scrap successfully removed.' rescue @@ -395,6 +395,17 @@ class ProfileController < PublicController end end + def icon + size = params[:size] || :portrait + image, mime = profile_icon(profile, size.to_sym, true) + + unless image.match(/^\/\/www\.gravatar\.com/).nil? + redirect_to 'https:' + image + else + @file = File.join(Rails.root, 'public', image) + send_file @file, type: mime, disposition: 'inline' + end + end protected diff --git a/app/controllers/public/search_controller.rb b/app/controllers/public/search_controller.rb index ed70791..dff3c15 100644 --- a/app/controllers/public/search_controller.rb +++ b/app/controllers/public/search_controller.rb @@ -247,7 +247,7 @@ class SearchController < PublicController def visible_profiles(klass, *extra_relations) relations = [:image, :domains, :environment, :preferred_domain] relations += extra_relations - if current_user && current_user.person.is_admin? + if current_user && current_person.is_admin? @environment.send(klass.name.underscore.pluralize).includes(relations) else @environment.send(klass.name.underscore.pluralize).visible.includes(relations) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1a41fa0..73c091d 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -146,12 +146,12 @@ module ApplicationHelper end def link_to_cms(text, profile = nil, options = {}) - profile ||= current_user.login + profile ||= current_person.identifier link_to text, myprofile_path(:controller => 'cms', :profile => profile), options end def link_to_profile(text, profile = nil, options = {}) - profile ||= current_user.login + profile ||= current_person.identifier link_to text, profile_path(:profile => profile) , options end @@ -160,7 +160,7 @@ module ApplicationHelper end def link_if_permitted(link, permission = nil, target = nil) - if permission.nil? || current_user.person.has_permission?(permission, target) + if permission.nil? || current_person.has_permission?(permission, target) link else nil @@ -814,7 +814,7 @@ module ApplicationHelper {s_('contents|Most commented') => {href: url_for({host: host, controller: 'search', action: 'contents', filter: 'more_comments'})}} ] if logged_in? - links.push(_('New content') => modal_options({:href => url_for({:controller => 'cms', :action => 'new', :profile => current_user.login, :cms => true})})) + links.push(_('New content') => modal_options({:href => url_for({:controller => 'cms', :action => 'new', :profile => current_person.identifier, :cms => true})})) end 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 {s_('people|More popular') => {href: url_for({host: host, controller: 'search', action: 'people', filter: 'more_popular'})}} ] if logged_in? - links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})}) - links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})}) + links.push(_('My friends') => {:href => url_for({:profile => current_person.identifier, :controller => 'friends'})}) + links.push(_('Invite friends') => {:href => url_for({:profile => current_person.identifier, :controller => 'invite', :action => 'friends'})}) end 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 {s_('communities|More popular') => {href: url_for({host: host, controller: 'search', action: 'communities', filter: 'more_popular'})}} ] if logged_in? - links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})}) - links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})}) + links.push(_('My communities') => {:href => url_for({:profile => current_person.identifier, :controller => 'memberships'})}) + links.push(_('New community') => {:href => url_for({:profile => current_person.identifier, :controller => 'memberships', :action => 'new_community'})}) end link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "search", :action => 'communities'}, :id => 'submenu-communities') + diff --git a/app/helpers/theme_loader_helper.rb b/app/helpers/theme_loader_helper.rb index 10d8014..e2e0be2 100644 --- a/app/helpers/theme_loader_helper.rb +++ b/app/helpers/theme_loader_helper.rb @@ -2,7 +2,7 @@ module ThemeLoaderHelper def current_theme @current_theme ||= begin - if session[:user_theme] + if !(defined?(session)).nil? && session[:user_theme] session[:user_theme] else # utility for developers: set the theme to 'random' in development mode and @@ -14,7 +14,7 @@ module ThemeLoaderHelper elsif Rails.env.development? && respond_to?(:params) && params[:user_theme] && File.exists?(Rails.root.join('public/designs/themes', params[:user_theme])) params[:user_theme] else - if profile && !profile.theme.nil? + if defined?(profile) && profile && !profile.theme.nil? profile.theme elsif environment environment.theme @@ -34,7 +34,7 @@ module ThemeLoaderHelper end def theme_path - if session[:user_theme] + if !(defined?(session)).nil? && session[:user_theme] '/user_themes/' + current_theme elsif session[:theme] '/designs/themes/' + session[:theme] diff --git a/app/models/abuse_complaint.rb b/app/models/abuse_complaint.rb index 8c8c3ab..2894880 100644 --- a/app/models/abuse_complaint.rb +++ b/app/models/abuse_complaint.rb @@ -1,6 +1,6 @@ class AbuseComplaint < Task has_many :abuse_reports, :dependent => :destroy - belongs_to :reported, :class_name => "Profile", :foreign_key => "requestor_id" + belongs_to :reported, :polymorphic => true, :foreign_key => "requestor_id" validates_presence_of :reported alias :requestor :reported diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb index 8fbec90..0fa161a 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -2,7 +2,7 @@ class AbuseReport < ApplicationRecord attr_accessible :content, :reason - belongs_to :reporter, :class_name => 'Person' + belongs_to :reporter, :polymorphic => true belongs_to :abuse_complaint has_many :reported_images, :dependent => :destroy diff --git a/app/models/comment.rb b/app/models/comment.rb index b79be4d..5f41b4b 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -15,7 +15,7 @@ class Comment < ApplicationRecord alias :article= :source= attr_accessor :follow_article - belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id' + belongs_to :author, :polymorphic => true, :foreign_key => 'author_id' has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' diff --git a/app/models/concerns/external_user.rb b/app/models/concerns/external_user.rb new file mode 100644 index 0000000..0989232 --- /dev/null +++ b/app/models/concerns/external_user.rb @@ -0,0 +1,93 @@ +require 'ostruct' + +module ExternalUser + extend ActiveSupport::Concern + + included do + attr_accessor :external_person_id + end + + def external_person + ExternalPerson.where(id: self.external_person_id).first + end + + def person_with_external + self.external_person || self.person_without_external + end + + module ClassMethods + def webfinger_lookup(login, domain, environment) + if login && domain && environment.has_federated_network?(domain) + # Ask if network at has user with login + # FIXME: Make an actual request to the federated network, which should return nil if not found + { + login: login + } + else + nil + end + end + + def build_request(uri) + request = Net::HTTP.new(uri.host, uri.port) + if uri.scheme == "https" # enable SSL/TLS + request.use_ssl = true + #TODO There may be self-signed certificates that we would not be able + #to verify, so we'll not verify the ssl certificate for now. Since + #this requests will go only towards trusted federated networks the admin + #configured we consider this not to be a big deal. Nonetheless we may be + #able in the future to require/provide the CA Files on the federation + #process which would allow us to verify the certificate. + request.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + request + end + + def external_login(login, password, domain) + # Call Noosfero /api/login + result = nil + response = nil + redirections_allowed = 3 + location = 'http://' + domain + '/api/v1/login' + request_params = CGI.unescape({ login: login, password: password }.to_query) + begin + while redirections_allowed > 0 && (response.blank? || response.code == '301') + uri = URI.parse(location) + request = build_request(uri) + response = request.post(uri.to_s, request_params) + location = response.header['location'] + redirections_allowed -= 1 + end + result = response.code.to_i / 100 === 2 ? JSON.parse(response.body) : nil + rescue + # Could not make request + end + result + end + + # Authenticates a user from an external social network + def external_authenticate(username, password, environment) + login, domain = username.split('@') + webfinger = User.webfinger_lookup(login, domain, environment) + if webfinger + user = User.external_login(login, password, domain) + if user + u = User.new + u.email = user['user']['email'] + u.login = login + # FIXME: Instead of the struct below, we should use the "webfinger" object returned by the webfinger_lookup method + webfinger = OpenStruct.new( + identifier: user['user']['person']['identifier'], + name: user['user']['person']['name'], + created_at: user['user']['person']['created_at'], + domain: domain, + email: user['user']['email'] + ) + u.external_person_id = ExternalPerson.get_or_create(webfinger).id + return u + end + end + nil + end + end +end diff --git a/app/models/concerns/human.rb b/app/models/concerns/human.rb new file mode 100644 index 0000000..d67c266 --- /dev/null +++ b/app/models/concerns/human.rb @@ -0,0 +1,42 @@ +module Human + extend ActiveSupport::Concern + + included do + has_many :comments, :as => :author, :foreign_key => :author_id + has_many :abuse_reports, :as => :reporter, :foreign_key => 'reporter_id', :dependent => :destroy + + scope :abusers, -> { + joins(:abuse_complaints).where('tasks.status = 3').distinct.select("#{self.table_name}.*") + } + scope :non_abusers, -> { + distinct.select("#{self.table_name}.*"). + joins("LEFT JOIN tasks ON #{self.table_name}.id = tasks.requestor_id AND tasks.type='AbuseComplaint'"). + where("tasks.status != 3 OR tasks.id is NULL") + } + end + + def already_reported?(profile) + abuse_reports.any? { |report| report.abuse_complaint.reported == profile && report.abuse_complaint.opened? } + end + + def register_report(abuse_report, profile) + AbuseComplaint.create!(:reported => profile, :target => profile.environment) if !profile.opened_abuse_complaint + abuse_report.abuse_complaint = profile.opened_abuse_complaint + abuse_report.reporter = self + abuse_report.save! + end + + def abuser? + AbuseComplaint.finished.where(:requestor_id => self).count > 0 + end + + # Sets the identifier for this person. Raises an exception when called on a + # existing person (since peoples' identifiers cannot be changed) + def identifier=(value) + unless self.new_record? + raise ArgumentError.new(_('An existing person cannot be renamed.')) + end + self[:identifier] = value + end + +end diff --git a/app/models/concerns/profile_entity.rb b/app/models/concerns/profile_entity.rb new file mode 100644 index 0000000..41fcded --- /dev/null +++ b/app/models/concerns/profile_entity.rb @@ -0,0 +1,155 @@ +module ProfileEntity + extend ActiveSupport::Concern + + included do + attr_accessible :name, :identifier, :environment + + validates_presence_of :identifier, :name + + belongs_to :environment + has_many :search_terms, :as => :context + has_many :abuse_complaints, :as => :reported, :foreign_key => 'requestor_id', :dependent => :destroy + + before_create :set_default_environment + + scope :recent, -> limit=nil { order('id DESC').limit(limit) } + + end + + def disable + self.visible = false + self.save + end + + def enable + self.visible = true + self.save + end + + def opened_abuse_complaint + abuse_complaints.opened.first + end + + def set_default_environment + if self.environment.nil? + self.environment = Environment.default + end + true + end + + # returns +false+ + def person? + self.kind_of?(Person) + end + + def enterprise? + self.kind_of?(Enterprise) + end + + def organization? + self.kind_of?(Organization) + end + + def community? + self.kind_of?(Community) + end + + include ActionView::Helpers::TextHelper + def short_name(chars = 40) + if self[:nickname].blank? + if chars + truncate self.name, length: chars, omission: '...' + else + self.name + end + else + self[:nickname] + end + end + + def to_liquid + HashWithIndifferentAccess.new :name => name, :identifier => identifier + end + + # Tells whether a specified profile has members or nor. + # + # On this class, returns false by default. + def has_members? + false + end + + def apply_type_specific_template(template) + end + + # Override this method in subclasses of Profile to create a default article + # set upon creation. Note that this method will be called *only* if there is + # no template for the type of profile (i.e. if the template was removed or in + # the creation of the template itself). + # + # This method must return an array of pre-populated articles, which will be + # associated to the profile before being saved. Example: + # + # def default_set_of_articles + # [Blog.new(:name => 'Blog'), Gallery.new(:name => 'Gallery')] + # end + # + # By default, this method returns an empty array. + def default_set_of_articles + [] + end + + def blocks_to_expire_cache + [] + end + + def cache_keys(params = {}) + [] + end + + def members_cache_key(params = {}) + page = params[:npage] || '1' + sort = (params[:sort] == 'desc') ? params[:sort] : 'asc' + cache_key + '-members-page-' + page + '-' + sort + end + + def more_recent_label + _("Since: ") + end + + def control_panel_settings_button + {:title => _('Edit Profile'), :icon => 'edit-profile'} + end + + def control_panel_settings_button + {:title => _('Profile Info and settings'), :icon => 'edit-profile'} + end + + def exclude_verbs_on_activities + %w[] + end + + def allow_invitation_from(person) + false + end + + def allow_post_content?(person = nil) + person.kind_of?(Profile) && person.has_permission?('post_content', self) + end + + def allow_edit?(person = nil) + person.kind_of?(Profile) && person.has_permission?('edit_profile', self) + end + + def allow_destroy?(person = nil) + person.kind_of?(Profile) && person.has_permission?('destroy_profile', self) + end + + module ClassMethods + + def identification + name + end + + end + +end diff --git a/app/models/environment.rb b/app/models/environment.rb index 43b5613..85994f8 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -1009,6 +1009,10 @@ class Environment < ApplicationRecord HashWithIndifferentAccess.new :name => name end + def has_federated_network?(domain) + self.federated_networks.map(&:url).any? { |url| /http[s]?:\/\/#{domain}\/?/ =~ url } + end + private def default_language_available diff --git a/app/models/external_person.rb b/app/models/external_person.rb new file mode 100644 index 0000000..7e9a11e --- /dev/null +++ b/app/models/external_person.rb @@ -0,0 +1,290 @@ +# A pseudo profile is a person from a remote network +class ExternalPerson < ActiveRecord::Base + + include Human + include ProfileEntity + + validates_uniqueness_of :identifier, scope: :source + + validates_presence_of :source, :email, :created_at + + attr_accessible :source, :email, :created_at + + def self.get_or_create(webfinger) + user = ExternalPerson.find_by(identifier: webfinger.identifier, source: webfinger.domain) + if user.nil? + user = ExternalPerson.create!(identifier: webfinger.identifier, + name: webfinger.name, + source: webfinger.domain, + email: webfinger.email, + created_at: webfinger.created_at + ) + end + user + end + + def privacy_setting + _('Public profile') + end + + def avatar + "http://#{self.source}/profile/#{self.identifier}/icon/" + end + + def url + "http://#{self.source}/profile/#{self.identifier}" + end + + alias :public_profile_url :url + + def admin_url + "http://#{self.source}/myprofile/#{self.identifier}" + end + + def wall_url + self.url + end + def tasks_url + self.url + end + def leave_url(reload = false) + self.url + end + def join_url + self.url + end + def join_not_logged_url + self.url + end + def check_membership_url + self.url + end + def add_url + self.url + end + def check_friendship_url + self.url + end + def people_suggestions_url + self.url + end + def communities_suggestions_url + self.url + end + def top_url(scheme = 'http') + "#{scheme}://#{self.source}" + end + + def profile_custom_icon(gravatar_default=nil) + self.avatar + end + + def preferred_login_redirection + environment.redirection_after_login + end + + def location + self.source + end + + def default_hostname + environment.default_hostname + end + + def possible_domains + environment.domains + end + + def person? + true + end + + def contact_email(*args) + self.email + end + + def notification_emails + [self.contact_email] + end + + def email_domain + self.source + end + + def email_addresses + ['%s@%s' % [self.identifier, self.source] ] + end + + def jid(options = {}) + "#{self.identifier}@#{self.source}" + end + def full_jid(options = {}) + "#{jid(options)}/#{self.name}" + end + + class ExternalPerson::Image + def initialize(path) + @path = path + end + + def public_filename(size = nil) + URI.join(@path, size.to_s) + end + + def content_type + # This is not really going to be used anywhere that matters + # so we are hardcodding it here. + 'image/png' + end + end + + def image + ExternalPerson::Image.new(avatar) + end + + def data_hash(gravatar_default = nil) + friends_list = {} + { + 'login' => self.identifier, + 'name' => self.name, + 'email' => self.email, + 'avatar' => self.profile_custom_icon(gravatar_default), + 'is_admin' => self.is_admin?, + 'since_month' => self.created_at.month, + 'since_year' => self.created_at.year, + 'email_domain' => self.source, + 'friends_list' => friends_list, + 'enterprises' => [], + 'amount_of_friends' => friends_list.count, + 'chat_enabled' => false + } + end + + # External Person should respond to all methods in Person and Profile + def person_instance_methods + methods_and_responses = { + enterprises: Enterprise.none, communities: Community.none, friends: + Person.none, memberships: Profile.none, friendships: Person.none, + following_articles: Article.none, article_followers: ArticleFollower.none, + requested_tasks: Task.none, mailings: Mailing.none, scraps_sent: + Scrap.none, favorite_enterprise_people: FavoriteEnterprisePerson.none, + favorite_enterprises: Enterprise.none, acepted_forums: Forum.none, + articles_with_access: Article.none, suggested_profiles: + ProfileSuggestion.none, suggested_people: ProfileSuggestion.none, + suggested_communities: ProfileSuggestion.none, user: nil, + refused_communities: Community.none, has_permission?: false, + has_permission_with_admin?: false, has_permission_without_admin?: false, + has_permission_with_plugins?: false, has_permission_without_plugins?: + false, memberships_by_role: Person.none, can_change_homepage?: false, + can_control_scrap?: false, receives_scrap_notification?: false, + can_control_activity?: false, can_post_content?: false, + suggested_friend_groups: [], friend_groups: [], add_friend: nil, + already_request_friendship?: false, remove_friend: nil, + presence_of_required_fields: nil, active_fields: [], required_fields: [], + signup_fields: [], default_set_of_blocks: [], default_set_of_boxes: [], + default_set_of_articles: [], cell_phone: nil, comercial_phone: nil, + nationality: nil, schooling: nil, contact_information: nil, sex: nil, + birth_date: nil, jabber_id: nil, personal_website: nil, address_reference: + nil, district: nil, schooling_status: nil, formation: nil, + custom_formation: nil, area_of_study: nil, custom_area_of_study: nil, + professional_activity: nil, organization_website: nil, organization: nil, + photo: nil, city: nil, state: nil, country: nil, zip_code: nil, + address_line2: nil, copy_communities_from: nil, + has_organization_pending_tasks?: false, organizations_with_pending_tasks: + Organization.none, pending_tasks_for_organization: Task.none, + build_contact: nil, is_a_friend?: false, ask_to_join?: false, refuse_join: + nil, blocks_to_expire_cache: [], cache_keys: [], communities_cache_key: '', + friends_cache_key: '', manage_friends_cache_key: '', + relationships_cache_key: '', is_member_of?: false, follows?: false, + each_friend: nil, is_last_admin?: false, is_last_admin_leaving?: false, + leave: nil, last_notification: nil, notification_time: 0, notifier: nil, + remove_suggestion: nil, allow_invitation_from?: false + } + + derivated_methods = generate_derivated_methods(methods_and_responses) + derivated_methods.merge(methods_and_responses) + end + + def profile_instance_methods + methods_and_responses = { + role_assignments: RoleAssignment.none, favorite_enterprises: + Enterprise.none, memberships: Profile.none, friendships: Profile.none, + tasks: Task.none, suggested_profiles: ProfileSuggestion.none, + suggested_people: ProfileSuggestion.none, suggested_communities: + ProfileSuggestion.none, public_profile: true, nickname: nil, custom_footer: + '', custom_header: '', address: '', zip_code: '', contact_phone: '', + image_builder: nil, description: '', closed: false, template_id: nil, lat: + nil, lng: nil, is_template: false, fields_privacy: {}, preferred_domain_id: + nil, category_ids: [], country: '', city: '', state: '', + national_region_code: '', redirect_l10n: false, notification_time: 0, + custom_url_redirection: nil, email_suggestions: false, + allow_members_to_invite: false, invite_friends_only: false, secret: false, + profile_admin_mail_notification: false, redirection_after_login: nil, + profile_activities: ProfileActivity.none, action_tracker_notifications: + ActionTrackerNotification.none, tracked_notifications: + ActionTracker::Record.none, scraps_received: Scrap.none, template: + Profile.none, comments_received: Comment.none, email_templates: + EmailTemplate.none, members: Profile.none, members_like: Profile.none, + members_by: Profile.none, members_by_role: Profile.none, scraps: + Scrap.none, welcome_page_content: nil, settings: {}, find_in_all_tasks: + nil, top_level_categorization: {}, interests: Category.none, geolocation: + '', country_name: '', pending_categorizations: [], add_category: false, + create_pending_categorizations: false, top_level_articles: Article.none, + valid_identifier: true, valid_template: false, create_default_set_of_boxes: + true, copy_blocks_from: nil, default_template: nil, + template_without_default: nil, template_with_default: nil, apply_template: + false, iframe_whitelist: [], recent_documents: Article.none, last_articles: + Article.none, is_validation_entity?: false, hostname: nil, own_hostname: + nil, article_tags: {}, tagged_with: Article.none, + insert_default_article_set: false, copy_articles_from: true, + copy_article_tree: nil, copy_article?: false, add_member: false, + remove_member: false, add_admin: false, remove_admin: false, add_moderator: + false, display_info_to?: true, update_category_from_region: nil, + accept_category?: false, custom_header_expanded: '', + custom_footer_expanded: '', public?: true, themes: [], find_theme: nil, + blogs: Blog.none, blog: nil, has_blog?: false, forums: Forum.none, forum: + nil, has_forum?: false, admins: [], settings_field: {}, setting_changed: + false, public_content: true, enable_contact?: false, folder_types: [], + folders: Article.none, image_galleries: Article.none, image_valid: true, + update_header_and_footer: nil, update_theme: nil, update_layout_template: + nil, recent_actions: ActionTracker::Record.none, recent_notifications: + ActionTracker::Record.none, more_active_label: _('no activity'), + more_popular_label: _('no members'), profile_custom_image: nil, + is_on_homepage?: false, activities: ProfileActivity.none, + may_display_field_to?: true, may_display_location_to?: true, public_fields: + {}, followed_by?: false, display_private_info_to?: true, can_view_field?: + true, remove_from_suggestion_list: nil, layout_template: 'default', + is_admin?: false, add_friend: false, follows?: false, is_a_friend?: false, + already_request_friendship?: false + } + + derivated_methods = generate_derivated_methods(methods_and_responses) + derivated_methods.merge(methods_and_responses) + end + + def method_missing(method, *args, &block) + if person_instance_methods.keys.include?(method) + return person_instance_methods[method] + end + if profile_instance_methods.keys.include? method + return profile_instance_methods[method] + end + end + + def respond_to_missing?(method_name, include_private = false) + person_instance_methods.keys.include?(method_name) || + profile_instance_methods.keys.include?(method_name) || + super + end + + private + + def generate_derivated_methods(methods) + derivated_methods = {} + methods.keys.each do |method| + derivated_methods[method.to_s.insert(-1, '?').to_sym] = false + derivated_methods[method.to_s.insert(-1, '=').to_sym] = nil + end + derivated_methods + end +end diff --git a/app/models/person.rb b/app/models/person.rb index 323e5b6..936df2e 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -1,6 +1,8 @@ # A person is the profile of an user holding all relationships with the rest of the system class Person < Profile + include Human + 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 SEARCH_FILTERS = { @@ -88,7 +90,6 @@ class Person < Profile memberships.where('role_assignments.role_id = ?', role.id) end - has_many :comments, :foreign_key => :author_id has_many :article_followers, :dependent => :destroy has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article has_many :friendships, :dependent => :destroy @@ -100,8 +101,6 @@ class Person < Profile has_many :requested_tasks, :class_name => 'Task', :foreign_key => :requestor_id, :dependent => :destroy - has_many :abuse_reports, :foreign_key => 'reporter_id', :dependent => :destroy - has_many :mailings has_many :scraps_sent, :class_name => 'Scrap', :foreign_key => :sender_id, :dependent => :destroy @@ -123,15 +122,6 @@ class Person < Profile scope :more_popular, -> { order 'friends_count DESC' } - scope :abusers, -> { - joins(:abuse_complaints).where('tasks.status = 3').distinct.select('profiles.*') - } - scope :non_abusers, -> { - distinct.select("profiles.*"). - joins("LEFT JOIN tasks ON profiles.id = tasks.requestor_id AND tasks.type='AbuseComplaint'"). - where("tasks.status != 3 OR tasks.id is NULL") - } - scope :admins, -> { joins(:role_assignments => :role).where('roles.key = ?', 'environment_administrator') } scope :activated, -> { joins(:user).where('users.activation_code IS NULL AND users.activated_at IS NOT NULL') } scope :deactivated, -> { joins(:user).where('NOT (users.activation_code IS NULL AND users.activated_at IS NOT NULL)') } @@ -174,15 +164,6 @@ class Person < Profile (self.has_permission?('post_content', profile) || self.has_permission?('publish_content', profile)) end - # Sets the identifier for this person. Raises an exception when called on a - # existing person (since peoples' identifiers cannot be changed) - def identifier=(value) - unless self.new_record? - raise ArgumentError.new(_('An existing person cannot be renamed.')) - end - self[:identifier] = value - end - def suggested_friend_groups (friend_groups.compact + [ _('friends'), _('work'), _('school'), _('family') ]).map {|i| i if !i.empty?}.compact.uniq end @@ -192,7 +173,7 @@ class Person < Profile end def add_friend(friend, group = nil) - unless self.is_a_friend?(friend) + unless self.is_a_friend?(friend) || friend.is_a?(ExternalPerson) friendship = self.friendships.build friendship.friend = friend friendship.group = group @@ -517,21 +498,6 @@ class Person < Profile leave_hash.to_json end - def already_reported?(profile) - abuse_reports.any? { |report| report.abuse_complaint.reported == profile && report.abuse_complaint.opened? } - end - - def register_report(abuse_report, profile) - AbuseComplaint.create!(:reported => profile, :target => profile.environment) if !profile.opened_abuse_complaint - abuse_report.abuse_complaint = profile.opened_abuse_complaint - abuse_report.reporter = self - abuse_report.save! - end - - def abuser? - AbuseComplaint.finished.where(:requestor_id => self).count > 0 - end - def control_panel_settings_button {:title => _('Edit Profile'), :icon => 'edit-profile'} end @@ -580,6 +546,34 @@ class Person < Profile person.has_permission?(:manage_friends, self) end + def data_hash(gravatar_default = nil) + friends_list = {} + enterprises = self.enterprises.map { |e| { 'name' => e.short_name, 'identifier' => e.identifier } } + self.friends.online.map do |person| + friends_list[person.identifier] = { + 'avatar' => person.profile_custom_icon(gravatar_default), + 'name' => person.short_name, + 'jid' => person.full_jid, + 'status' => person.user.chat_status, + } + end + + { + 'login' => self.identifier, + 'name' => self.name, + 'email' => self.email, + 'avatar' => self.profile_custom_icon(gravatar_default), + 'is_admin' => self.is_admin?, + 'since_month' => self.created_at.month, + 'since_year' => self.created_at.year, + 'email_domain' => self.user.enable_email ? self.user.email_domain : nil, + 'friends_list' => friends_list, + 'enterprises' => enterprises, + 'amount_of_friends' => friends_list.count, + 'chat_enabled' => self.environment.enabled?('xmpp_chat') + } + end + protected def followed_by?(profile) diff --git a/app/models/profile.rb b/app/models/profile.rb index 1d9f516..7686f89 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -3,9 +3,10 @@ # which by default is the one returned by Environment:default. class Profile < ApplicationRecord - 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, - :redirection_after_login, :custom_url_redirection, - :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification + include ProfileEntity + + 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, + :custom_url_redirection, :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification, :redirection_after_login # use for internationalizable human type names in search facets # reimplement on subclasses @@ -115,8 +116,6 @@ class Profile < ApplicationRecord } scope :no_templates, -> { where is_template: false } - scope :recent, -> limit=nil { order('id DESC').limit(limit) } - # Returns a scoped object to select profiles in a given location or in a radius # distance from the given location center. @@ -224,8 +223,6 @@ class Profile < ApplicationRecord welcome_page && welcome_page.published ? welcome_page.body : nil end - has_many :search_terms, :as => :context - def scraps(scrap=nil) scrap = scrap.is_a?(Scrap) ? scrap.id : scrap scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap) @@ -278,7 +275,6 @@ class Profile < ApplicationRecord has_many :domains, :as => :owner belongs_to :preferred_domain, :class_name => 'Domain', :foreign_key => 'preferred_domain_id' - belongs_to :environment has_many :articles, :dependent => :destroy belongs_to :home_page, :class_name => Article.name, :foreign_key => 'home_page_id' @@ -305,8 +301,6 @@ class Profile < ApplicationRecord has_many :profile_categorizations_including_virtual, :class_name => 'ProfileCategorization' has_many :categories_including_virtual, :through => :profile_categorizations_including_virtual, :source => :category - has_many :abuse_complaints, :foreign_key => 'requestor_id', :dependent => :destroy - has_many :profile_suggestions, :foreign_key => :suggestion_id, :dependent => :destroy def top_level_categorization @@ -401,7 +395,6 @@ class Profile < ApplicationRecord self.all end - validates_presence_of :identifier, :name validates_length_of :nickname, :maximum => 16, :allow_nil => true validate :valid_template validate :valid_identifier @@ -416,14 +409,6 @@ class Profile < ApplicationRecord end end - before_create :set_default_environment - def set_default_environment - if self.environment.nil? - self.environment = Environment.default - end - true - end - # registar callback for creating boxes after the object is created. after_create :create_default_set_of_boxes @@ -496,9 +481,6 @@ class Profile < ApplicationRecord self.save(:validate => false) end - def apply_type_specific_template(template) - end - xss_terminate :only => [ :name, :nickname, :address, :contact_phone, :description ], :on => 'validation' xss_terminate :only => [ :custom_footer, :custom_header ], :with => 'white_list' @@ -539,10 +521,6 @@ class Profile < ApplicationRecord ).order('articles.published_at desc, articles.id desc') end - def to_liquid - HashWithIndifferentAccess.new :name => name, :identifier => identifier - end - class << self # finds a profile by its identifier. This method is a shortcut to @@ -562,23 +540,6 @@ class Profile < ApplicationRecord environment end - # returns +false+ - def person? - self.kind_of?(Person) - end - - def enterprise? - self.kind_of?(Enterprise) - end - - def organization? - self.kind_of?(Organization) - end - - def community? - self.kind_of?(Community) - end - # returns false. def is_validation_entity? false @@ -683,13 +644,6 @@ private :generate_url, :url_options self.articles.tagged_with(tag) end - # Tells whether a specified profile has members or nor. - # - # On this class, returns false by default. - def has_members? - false - end - after_create :insert_default_article_set def insert_default_article_set if template @@ -704,23 +658,6 @@ private :generate_url, :url_options end end - # Override this method in subclasses of Profile to create a default article - # set upon creation. Note that this method will be called *only* if there is - # no template for the type of profile (i.e. if the template was removed or in - # the creation of the template itself). - # - # This method must return an array of pre-populated articles, which will be - # associated to the profile before being saved. Example: - # - # def default_set_of_articles - # [Blog.new(:name => 'Blog'), Gallery.new(:name => 'Gallery')] - # end - # - # By default, this method returns an empty array. - def default_set_of_articles - [] - end - def copy_articles_from other return false if other.top_level_articles.empty? other.top_level_articles.each do |a| @@ -816,19 +753,6 @@ private :generate_url, :url_options !forbidden.include?(cat.class) end - include ActionView::Helpers::TextHelper - def short_name(chars = 40) - if self[:nickname].blank? - if chars - truncate self.name, length: chars, omission: '...' - else - self.name - end - else - self[:nickname] - end - end - def custom_header self[:custom_header] || environment && environment.custom_header end @@ -934,14 +858,6 @@ private :generate_url, :url_options articles.galleries end - def blocks_to_expire_cache - [] - end - - def cache_keys(params = {}) - [] - end - validate :image_valid def image_valid @@ -978,16 +894,6 @@ private :generate_url, :url_options self.update_attribute(:layout_template, template) end - def members_cache_key(params = {}) - page = params[:npage] || '1' - sort = (params[:sort] == 'desc') ? params[:sort] : 'asc' - cache_key + '-members-page-' + page + '-' + sort - end - - def more_recent_label - _("Since: ") - end - def recent_actions tracked_actions.recent end @@ -1042,32 +948,6 @@ private :generate_url, :url_options end end - def opened_abuse_complaint - abuse_complaints.opened.first - end - - def disable - self.visible = false - self.save - end - - def enable - self.visible = true - self.save - end - - def control_panel_settings_button - {:title => _('Edit Profile'), :icon => 'edit-profile'} - end - - def self.identification - name - end - - def exclude_verbs_on_activities - %w[] - end - # Customize in subclasses def activities self.profile_activities.includes(:activity).order('updated_at DESC') @@ -1102,10 +982,6 @@ private :generate_url, :url_options self.active_fields end - def control_panel_settings_button - {:title => _('Profile Info and settings'), :icon => 'edit-profile'} - end - def followed_by?(person) person.is_member_of?(self) end @@ -1133,19 +1009,4 @@ private :generate_url, :url_options suggestion.disable if suggestion end - def allow_invitation_from(person) - false - end - - def allow_post_content?(person = nil) - person.kind_of?(Profile) && person.has_permission?('post_content', self) - end - - def allow_edit?(person = nil) - person.kind_of?(Profile) && person.has_permission?('edit_profile', self) - end - - def allow_destroy?(person = nil) - person.kind_of?(Profile) && person.has_permission?('destroy_profile', self) - end end diff --git a/app/models/user.rb b/app/models/user.rb index bb4fbef..eacdb88 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,6 +7,8 @@ class User < ApplicationRecord attr_accessible :login, :email, :password, :password_confirmation, :activated_at + include ExternalUser + N_('Password') N_('Password confirmation') N_('Terms accepted') @@ -105,6 +107,8 @@ class User < ApplicationRecord has_one :person, dependent: :destroy, autosave: false belongs_to :environment + alias_method_chain :person, :external + has_many :sessions, dependent: :destroy # holds the current session, see lib/authenticated_system.rb attr_accessor :session @@ -146,7 +150,8 @@ class User < ApplicationRecord u.generate_private_token_if_not_exist return u end - return nil + + return User.external_authenticate(login, password, environment) end def register_login @@ -380,31 +385,7 @@ class User < ApplicationRecord end def data_hash(gravatar_default = nil) - friends_list = {} - enterprises = person.enterprises.map { |e| { 'name' => e.short_name, 'identifier' => e.identifier } } - self.person.friends.online.map do |person| - friends_list[person.identifier] = { - 'avatar' => person.profile_custom_icon(gravatar_default), - 'name' => person.short_name, - 'jid' => person.full_jid, - 'status' => person.user.chat_status, - } - end - - { - 'login' => self.login, - 'name' => self.person.name, - 'email' => self.email, - 'avatar' => self.person.profile_custom_icon(gravatar_default), - 'is_admin' => self.person.is_admin?, - 'since_month' => self.person.created_at.month, - 'since_year' => self.person.created_at.year, - 'email_domain' => self.enable_email ? self.email_domain : nil, - 'friends_list' => friends_list, - 'enterprises' => enterprises, - 'amount_of_friends' => friends_list.count, - 'chat_enabled' => person.environment.enabled?('xmpp_chat') - } + self.person.data_hash(gravatar_default) end def self.expires_chat_status_every diff --git a/app/views/account/welcome.html.erb b/app/views/account/welcome.html.erb index 17c9ed3..6dedc00 100644 --- a/app/views/account/welcome.html.erb +++ b/app/views/account/welcome.html.erb @@ -4,7 +4,7 @@ <%= _('%s was successfuly activated. Now you may go to your control panel or to the control panel of your enterprise') % @enterprise.name %> <%= button_bar do %> - <%= button 'forward', _('Go to my control panel'), :action => 'index', :controller => 'profile_editor', :profile => current_user.person.identifier %> + <%= button 'forward', _('Go to my control panel'), :action => 'index', :controller => 'profile_editor', :profile => current_person.identifier %> <%= button 'forward', _('Go to my enterprise control panel') % @enterprise.name, :action => 'index', :controller => 'profile_editor', :profile => @enterprise.identifier %> <% end %> <% end %> diff --git a/app/views/enterprise_registration/basic_information.html.erb b/app/views/enterprise_registration/basic_information.html.erb index ff8e897..ac28201 100644 --- a/app/views/enterprise_registration/basic_information.html.erb +++ b/app/views/enterprise_registration/basic_information.html.erb @@ -9,7 +9,7 @@ <%= button_bar do %> - <%= button :back, _('Go back'), { :profile => current_user.person.identifier, :action=>"enterprises", :controller=>"profile" }%> + <%= button :back, _('Go back'), { :profile => current_person.identifier, :action=>"enterprises", :controller=>"profile" }%> <% end %> <% else %>
@@ -37,7 +37,7 @@ <%= template_options(:enterprises, 'create_enterprise')%> <%= button_bar do %> - <%= submit_button('next', _('Next'), :cancel => {:profile => current_user.person.identifier, :action=>"enterprises", :controller=>"profile"}) %> + <%= submit_button('next', _('Next'), :cancel => {:profile => current_person.identifier, :action=>"enterprises", :controller=>"profile"}) %> <% end %> <% end %> <% end %> diff --git a/app/views/environment_role_manager/affiliate.html.erb b/app/views/environment_role_manager/affiliate.html.erb index ceb3ba3..937f0e0 100644 --- a/app/views/environment_role_manager/affiliate.html.erb +++ b/app/views/environment_role_manager/affiliate.html.erb @@ -2,7 +2,7 @@ <%= form_tag( {:action => 'give_role'}, {:method => :post}) do %> <%= select_tag 'role', options_for_select(@roles.map{|r|[r.name,r.id]}) %> - <%= hidden_field_tag 'person', current_user.person.id %> + <%= hidden_field_tag 'person', current_person.id %> <%= button_bar do %> <%= submit_button('affiliate', _('Affiliate', :cancel => {:action => 'index'}) %> <% end %> diff --git a/app/views/layouts/_user.html.erb b/app/views/layouts/_user.html.erb index e29674f..439630d 100644 --- a/app/views/layouts/_user.html.erb +++ b/app/views/layouts/_user.html.erb @@ -1,5 +1,6 @@
<% user = (session[:user] && User.find_by(id: session[:user])) || nil %> + <% user ||= (session[:external] && User.new(:external_person_id => session[:external])) || nil %> <% if user.present? %> <% user = user.person %> diff --git a/app/views/profile_members/affiliate.html.erb b/app/views/profile_members/affiliate.html.erb index 2c37284..65ebe3c 100644 --- a/app/views/profile_members/affiliate.html.erb +++ b/app/views/profile_members/affiliate.html.erb @@ -2,7 +2,7 @@ <%= form_tag( {:action => 'give_role'}, {:method => :post}) do %> <%= select_tag 'role', options_for_select(@roles.map{|r|[r.name,r.id]}) %> - <%= hidden_field_tag 'person', current_user.person.id %> + <%= hidden_field_tag 'person', current_person.id %> <%= button_bar do %> <%= submit_button('affiliate', _('Affiliate'), :cancel => {:action => 'index'}) %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index 21c706d..076ed0f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -86,6 +86,9 @@ Noosfero::Application.routes.draw do # comments match 'profile/:profile/comment/:action/:id', controller: 'comment', profile: /#{Noosfero.identifier_format_in_url}/i, via: :all + + # icon + match 'profile/:profile/icon(/:size)', controller: 'profile', action: 'icon', size: /(big|minor|thumb|portrait|icon)/, profile: /#{Noosfero.identifier_format_in_url}/i, via: :get # public profile information match 'profile/:profile(/:action(/:id))', controller: 'profile', action: 'index', id: /[^\/]*/, profile: /#{Noosfero.identifier_format_in_url}/i, as: :profile, via: :all diff --git a/db/migrate/20160420125236_add_type_to_polymorphic_profile_associations.rb b/db/migrate/20160420125236_add_type_to_polymorphic_profile_associations.rb new file mode 100644 index 0000000..26cf03d --- /dev/null +++ b/db/migrate/20160420125236_add_type_to_polymorphic_profile_associations.rb @@ -0,0 +1,17 @@ +class AddTypeToPolymorphicProfileAssociations < ActiveRecord::Migration + def up + add_column :tasks, :reported_type, :string + add_column :abuse_reports, :reporter_type, :string + add_column :comments, :author_type, :string + + update("UPDATE tasks SET reported_type='Profile'") + update("UPDATE abuse_reports SET reporter_type='Person'") + update("UPDATE comments SET author_type='Person'") + end + + def down + remove_column :abuse_complaints, :reported_type + remove_column :abuse_reports, :reporter_type + remove_column :comments, :author_type + end +end diff --git a/db/migrate/20160420140141_create_external_person.rb b/db/migrate/20160420140141_create_external_person.rb new file mode 100644 index 0000000..f1c5c4e --- /dev/null +++ b/db/migrate/20160420140141_create_external_person.rb @@ -0,0 +1,14 @@ +class CreateExternalPerson < ActiveRecord::Migration + def change + create_table :external_people do |t| + t.string :name + t.string :identifier + t.string :source + t.string :email + t.integer :environment_id + t.boolean :visible, default: true + t.datetime :created_at + t.datetime :updated_at + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 9c4895c..db6eb6b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -23,6 +23,7 @@ ActiveRecord::Schema.define(version: 20160422163123) do t.text "reason" t.datetime "created_at" t.datetime "updated_at" + t.string "reporter_type" end create_table "action_tracker", force: :cascade do |t| @@ -289,6 +290,7 @@ ActiveRecord::Schema.define(version: 20160422163123) do t.string "user_agent" t.string "referrer" t.text "settings" + t.string "author_type" end 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 t.boolean "disable_feed_ssl", default: false end + create_table "external_environments", force: :cascade do |t| + t.string "name" + t.string "url" + t.string "identifier" + t.string "screenshot" + t.string "thumbnail" + end + create_table "external_feeds", force: :cascade do |t| t.string "feed_title" t.datetime "fetched_at" @@ -421,6 +431,17 @@ ActiveRecord::Schema.define(version: 20160422163123) do add_index "external_feeds", ["enabled"], name: "index_external_feeds_on_enabled", using: :btree add_index "external_feeds", ["fetched_at"], name: "index_external_feeds_on_fetched_at", using: :btree + create_table "external_people", force: :cascade do |t| + t.string "name" + t.string "identifier" + t.string "source" + t.string "email" + t.integer "environment_id" + t.boolean "visible", default: true + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "favorite_enterprise_people", force: :cascade do |t| t.integer "person_id" t.integer "enterprise_id" @@ -432,14 +453,6 @@ ActiveRecord::Schema.define(version: 20160422163123) do add_index "favorite_enterprise_people", ["person_id", "enterprise_id"], name: "index_favorite_enterprise_people_on_person_id_and_enterprise_id", using: :btree add_index "favorite_enterprise_people", ["person_id"], name: "index_favorite_enterprise_people_on_person_id", using: :btree - create_table "external_environments", force: :cascade do |t| - t.string "name" - t.string "url" - t.string "identifier" - t.string "screenshot" - t.string "thumbnail" - end - create_table "friendships", force: :cascade do |t| t.integer "person_id" t.integer "friend_id" @@ -797,6 +810,7 @@ ActiveRecord::Schema.define(version: 20160422163123) do t.boolean "spam", default: false t.integer "responsible_id" t.integer "closed_by_id" + t.string "reported_type" end add_index "tasks", ["requestor_id"], name: "index_tasks_on_requestor_id", using: :btree diff --git a/features/federated_login.feature b/features/federated_login.feature new file mode 100644 index 0000000..a6fbde3 --- /dev/null +++ b/features/federated_login.feature @@ -0,0 +1,55 @@ +Feature: federated login + As a user + I want to login using an account from a federated network + In order to view pages logged in + + @selenium + Scenario: login from portal homepage + Given feature "allow_change_of_redirection_after_login" is disabled on environment + And the following federated networks + | identifier | name | url | + | test | Test | http://federated.noosfero.org | + And the following external users + | login | + | joaosilva@federated.noosfero.org | + And I am not logged in + And I go to the homepage + And I follow "Login" + And I fill in the following: + | Username / Email | joaosilva@federated.noosfero.org | + | Password | 123456 | + When I press "Log in" + Then I should be on the homepage + And I should be federated logged in as "joaosilva@federated.noosfero.org" + + @selenium + Scenario: not login from portal homepage + Given feature "allow_change_of_redirection_after_login" is disabled on environment + And the following federated networks + | identifier | name | url | + | test | Test | http://federated.noosfero.org | + And I am not logged in + And I go to the homepage + And I follow "Login" + And I fill in the following: + | Username / Email | joaosilva@federated.noosfero.org | + | Password | 123456 | + When I press "Log in" + Then I should be on /account/login + And I should not be federated logged in as "joaosilva@federated.noosfero.org" + + @selenium + Scenario: not login if network is not whitelisted + Given feature "allow_change_of_redirection_after_login" is disabled on environment + And the following external users + | login | + | joaosilva@federated.noosfero.org | + And I am not logged in + And I go to the homepage + And I follow "Login" + And I fill in the following: + | Username / Email | joaosilva@federated.noosfero.org | + | Password | 123456 | + When I press "Log in" + Then I should be on /account/login + And I should not be federated logged in as "joaosilva@federated.noosfero.org" diff --git a/features/step_definitions/noosfero_steps.rb b/features/step_definitions/noosfero_steps.rb index 1686420..d24de1e 100644 --- a/features/step_definitions/noosfero_steps.rb +++ b/features/step_definitions/noosfero_steps.rb @@ -679,3 +679,36 @@ Given /^the field (.*) is public for all users$/ do |field| person.save! end end + +Given /^the following external users?$/ do |table| + table.hashes.each do |item| + person_data = item.dup + username, domain = person_data['login'].split('@') + response = OpenStruct.new( + code: '200', + body: { + user: { + email: username + '@ema.il', + person: { + identifier: username, + name: username, + created_at: Time.now, + } + } + }.to_json + ) + Net::HTTP.stub(:post_form).and_return(response) + end +end + +Then /^I should be federated logged in as "([^@]+)@(.+)"$/ do |username, domain| + visit '/' + url = 'http://' + domain + '/' + username + page.should have_xpath("//a[@href=\"#{url}\"]") +end + +Then /^I should not be federated logged in as "([^@]+)@(.+)"$/ do |username, domain| + visit '/' + url = 'http://' + domain + '/' + username + page.should_not have_xpath("//a[@href=\"#{url}\"]") +end diff --git a/features/support/env.rb b/features/support/env.rb index 0c85ef5..a63786d 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -8,6 +8,7 @@ ENV["RAILS_ENV"] ||= "cucumber" require File.expand_path(File.dirname(__FILE__) + '/../../config/environment') require 'cucumber/rails' +require 'cucumber/rspec/doubles' Capybara.ignore_hidden_elements = true diff --git a/plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb b/plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb index 17db4b1..8f0086b 100644 --- a/plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb +++ b/plugins/organization_ratings/controllers/organization_ratings_plugin_profile_controller.rb @@ -38,7 +38,7 @@ class OrganizationRatingsPluginProfileController < ProfileController def create_new_rate @rating = OrganizationRating.new(params[:organization_rating]) - @rating.person = current_user.person + @rating.person = current_person @rating.organization = profile @rating.value = params[:organization_rating_value] if params[:organization_rating_value] diff --git a/plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb b/plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb index 71fca2f..b4d04c3 100644 --- a/plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb +++ b/plugins/organization_ratings/views/organization_ratings_plugin_profile/_new_rating_fields.html.erb @@ -5,11 +5,11 @@
- <%= link_to profile_image(current_user.person, :portrait), current_user.person.url %> + <%= link_to profile_image(current_person, :portrait), current_person.url %>
- <%= link_to current_user.person.name, current_user.person.url %> + <%= link_to current_person.name, current_person.url %>
@@ -55,14 +55,14 @@
<% elsif env_organization_ratings_config.vote_once %>
- <%= _("Hi, %s! The administrators set that you can vote") % current_user.name %> + <%= _("Hi, %s! The administrators set that you can vote") % current_person.name %> <%= _("only once") %> <%= _("for this %s.") % profile.class.name.downcase %> <%= render :partial => 'shared/rating_button', :locals => { :disabled => true } %>
<% else %>
- <%= _("Hi, %s! The administrators set the minimum time of") % current_user.name %> + <%= _("Hi, %s! The administrators set the minimum time of") % current_person.name %> <%= _("%d hour(s)") % env_organization_ratings_config.cooldown %> <%= _("between each evaluation.") %> diff --git a/plugins/organization_ratings/views/shared/_make_report_block.html.erb b/plugins/organization_ratings/views/shared/_make_report_block.html.erb index 8d50644..bb16b3e 100644 --- a/plugins/organization_ratings/views/shared/_make_report_block.html.erb +++ b/plugins/organization_ratings/views/shared/_make_report_block.html.erb @@ -1,5 +1,5 @@ -<% logged_in_image = link_to profile_image(current_user.person, :portrait), current_user.person.url if current_user %> -<% logged_in_name = link_to current_user.person.name, current_user.person.url if current_user %> +<% logged_in_image = link_to profile_image(current_person, :portrait), current_person.url if current_user %> +<% logged_in_name = link_to current_person.name, current_person.url if current_user %> <% logged_out_image = image_tag('plugins/organization_ratings/images/user-not-logged.png') %>
@@ -26,4 +26,4 @@
<% end %>
-
\ No newline at end of file + diff --git a/plugins/responsive/lib/ext/application_helper.rb b/plugins/responsive/lib/ext/application_helper.rb index c1e6675..d2b40cb 100644 --- a/plugins/responsive/lib/ext/application_helper.rb +++ b/plugins/responsive/lib/ext/application_helper.rb @@ -140,7 +140,7 @@ module ApplicationHelper [s_('contents|Most commented'), {host: host, controller: :search, action: :contents, filter: 'more_comments'}], ] if logged_in? - links.push [_('New content'), '', modal_options({href: url_for({controller: 'cms', action: 'new', profile: current_user.login, cms: true})})] + links.push [_('New content'), '', modal_options({href: url_for({controller: 'cms', action: 'new', profile: current_person.identifier, cms: true})})] end content_tag :li, class: 'dropdown' do @@ -170,8 +170,8 @@ module ApplicationHelper [s_('people|More popular'), {host: host, controller: :search, action: :people, filter: 'more_popular'}], ] if logged_in? - links.push [_('My friends'), {profile: current_user.login, controller: 'friends'}] - links.push [_('Invite friends'), {profile: current_user.login, controller: 'invite', action: 'friends'}] + links.push [_('My friends'), {profile: current_person.identifier, controller: 'friends'}] + links.push [_('Invite friends'), {profile: current_person.identifier, controller: 'invite', action: 'friends'}] end content_tag :li, class: 'dropdown' do @@ -201,8 +201,8 @@ module ApplicationHelper [s_('communities|More popular'), {host: host, controller: :search, action: :communities, filter: 'more_popular'}], ] if logged_in? - links.push [_('My communities'), {profile: current_user.login, controller: 'memberships'}] - links.push [_('New community'), {profile: current_user.login, controller: 'memberships', action: 'new_community'}] + links.push [_('My communities'), {profile: current_person.identifier, controller: 'memberships'}] + links.push [_('New community'), {profile: current_person.identifier, controller: 'memberships', action: 'new_community'}] end content_tag :li, class: 'dropdown' do diff --git a/plugins/sniffer/views/blocks/interests.html.erb b/plugins/sniffer/views/blocks/interests.html.erb index 81268d3..9ece880 100644 --- a/plugins/sniffer/views/blocks/interests.html.erb +++ b/plugins/sniffer/views/blocks/interests.html.erb @@ -8,7 +8,7 @@
- <% if logged_in? and (current_user.person.is_admin?(environment) or profile.admins.include?(current_user.person)) %> + <% if logged_in? and (current_person.is_admin?(environment) or profile.admins.include?(current_person)) %> <%= _('Edit %{inputs} and %{block.interests}') % { :inputs => link_to(_("products' inputs"), :controller => :manage_products, :action => :index), :interests => link_to(_('declared interests'), :controller => :sniffer_plugin_myprofile, :action => :edit), diff --git a/plugins/solr/lib/solr_plugin/search_helper.rb b/plugins/solr/lib/solr_plugin/search_helper.rb index 1d6851a..2e6f9f2 100644 --- a/plugins/solr/lib/solr_plugin/search_helper.rb +++ b/plugins/solr/lib/solr_plugin/search_helper.rb @@ -12,9 +12,9 @@ module SolrPlugin::SearchHelper :products => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')}, :more_recent, {:label => c_('More recent'), :solr_opts => {:sort => 'updated_at desc, score desc'}}, :name, {:label => _('Name'), :solr_opts => {:sort => 'solr_plugin_name_sortable asc'}}, - :closest, {:label => _('Closest to me'), :if => proc{ logged_in? && (profile=current_user.person).lat && profile.lng }, + :closest, {:label => _('Closest to me'), :if => proc{ logged_in? && (profile=current_person).lat && profile.lng }, :solr_opts => {:sort => "geodist() asc", - :latitude => proc{ current_user.person.lat }, :longitude => proc{ current_user.person.lng }}}, + :latitude => proc{ current_person.lat }, :longitude => proc{ current_person.lng }}}, ], :events => ActiveSupport::OrderedHash[ :none, {:label => _('Relevance')}, :name, {:label => _('Name'), :solr_opts => {:sort => 'solr_plugin_name_sortable asc'}}, diff --git a/test/api/people_test.rb b/test/api/people_test.rb index 01d7f08..dad9b7a 100644 --- a/test/api/people_test.rb +++ b/test/api/people_test.rb @@ -369,6 +369,44 @@ class PeopleTest < ActiveSupport::TestCase assert_equal "www.blog.org", json['person']['additional_data']['Custom Blog'] end + should 'return portrait icon if size is not provided and there is a profile image' do + img = Image.create!(uploaded_data: fixture_file_upload('/files/rails.png', 'image/png')) + profile = fast_create(Person, image_id: img.id) + + get "/api/v1/people/#{profile.id}/icon?#{params.to_query}" + assert_equal 200, last_response.status + json = JSON.parse(last_response.body) + assert_match /^https?:\/\/.*portrait\.png$/, json['icon'] + end + + should 'return icon in provided size if there is a profile image' do + img = Image.create!(uploaded_data: fixture_file_upload('/files/rails.png', 'image/png')) + profile = fast_create(Person, image_id: img.id) + + get "/api/v1/people/#{profile.id}/icon?#{params.to_query}&size=big" + assert_equal 200, last_response.status + json = JSON.parse(last_response.body) + assert_match /^https?:\/\/.*big\.png$/, json['icon'] + end + + should 'return icon from gravatar without size if there is no profile image' do + profile = create_user('test-user').person + + get "/api/v1/people/#{profile.id}/icon?#{params.to_query}" + assert_equal 200, last_response.status + json = JSON.parse(last_response.body) + assert_match /^https:\/\/www\.gravatar\.com.*size=64/, json['icon'] + end + + should 'return icon from gravatar with size if there is no profile image' do + profile = create_user('test-user').person + + get "/api/v1/people/#{profile.id}/icon?#{params.to_query}&size=big" + assert_equal 200, last_response.status + json = JSON.parse(last_response.body) + assert_match /^https:\/\/www\.gravatar\.com.*size=150/, json['icon'] + end + PERSON_ATTRIBUTES = %w(vote_count comments_count articles_count following_articles_count) PERSON_ATTRIBUTES.map do |attribute| diff --git a/test/functional/comment_controller_test.rb b/test/functional/comment_controller_test.rb index 08d5365..45545b8 100644 --- a/test/functional/comment_controller_test.rb +++ b/test/functional/comment_controller_test.rb @@ -487,7 +487,7 @@ class CommentControllerTest < ActionController::TestCase should 'edit comment from a page' do login_as profile.identifier page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') - comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile.id) + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile.id, :author_type => 'Person') get :edit, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' } assert_tag :tag => 'textarea', :attributes => {:id => 'comment_body'}, :content => /Original comment/ @@ -522,7 +522,7 @@ class CommentControllerTest < ActionController::TestCase should 'be able to update a comment' do login_as profile.identifier page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text', :accept_comments => false) - comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile) + comment = fast_create(Comment, :body => 'Original comment', :source_id => page.id, :source_type => 'Article', :author_id => profile, :author_type => 'Person') xhr :post, :update, :id => comment.id, :profile => profile.identifier, :comment => { :body => 'Comment edited' } assert ActiveSupport::JSON.decode(@response.body)["ok"], "attribute ok expected to be true" diff --git a/test/functional/profile_controller_test.rb b/test/functional/profile_controller_test.rb index 57da6e0..fee1442 100644 --- a/test/functional/profile_controller_test.rb +++ b/test/functional/profile_controller_test.rb @@ -1932,4 +1932,37 @@ class ProfileControllerTest < ActionController::TestCase assert_redirected_to :controller => 'account', :action => 'login' end + should 'return portrait icon if size is not provided and there is a profile image' do + img = Image.create!(uploaded_data: fixture_file_upload('/files/rails.png', 'image/png')) + profile = fast_create(Person, image_id: img.id) + + get :icon, profile: profile.identifier, size: nil + assert_response :success + assert_equal 'image/png', @response.header['Content-Type'] + assert File.exists?(assigns(:file)) + end + + should 'return icon in provided size if there is a profile image' do + img = Image.create!(uploaded_data: fixture_file_upload('/files/rails.png', 'image/png')) + profile = fast_create(Person, image_id: img.id) + + get :icon, profile: profile.identifier, size: :big + assert_response :success + assert_equal 'image/png', @response.header['Content-Type'] + assert File.exists?(assigns(:file)) + end + + should 'return icon from gravatar without size if there is no profile image' do + profile = fast_create(Person) + + get :icon, profile: profile.identifier + assert_redirected_to /^https:\/\/www\.gravatar\.com\/.*/ + end + + should 'return icon from gravatar with size if there is no profile image' do + profile = fast_create(Person) + + get :icon, profile: profile.identifier, size: :thumb + assert_redirected_to /^https:\/\/www\.gravatar\.com\/.*/ + end end diff --git a/test/integration/routing_test.rb b/test/integration/routing_test.rb index 9a2cd19..c02e35e 100644 --- a/test/integration/routing_test.rb +++ b/test/integration/routing_test.rb @@ -276,4 +276,11 @@ class RoutingTest < ActionDispatch::IntegrationTest assert_routing('/profile/~', :controller => 'profile', :profile => '~', :action => 'index') end + should 'have route to profile icon without size' do + assert_routing('/profile/ze/icon', controller: 'profile', profile: 'ze', action: 'icon') + end + + should 'have route to profile icon with supported size' do + assert_routing('/profile/ze/icon/big', controller: 'profile', profile: 'ze', action: 'icon', size: 'big') + end end diff --git a/test/unit/comment_test.rb b/test/unit/comment_test.rb index eaaff46..2bd4da6 100644 --- a/test/unit/comment_test.rb +++ b/test/unit/comment_test.rb @@ -23,17 +23,14 @@ class CommentTest < ActiveSupport::TestCase end end - should 'record authenticated author' do + should 'record authenticated author polymorphically' do c = Comment.new - assert_raise ActiveRecord::AssociationTypeMismatch do - c.author = 1 - end - assert_raise ActiveRecord::AssociationTypeMismatch do - c.author = Profile - end assert_nothing_raised do c.author = Person.new end + assert_nothing_raised do + c.author = ExternalPerson.new + end end should 'record unauthenticated author' do diff --git a/test/unit/external_person_test.rb b/test/unit/external_person_test.rb new file mode 100644 index 0000000..b181b59 --- /dev/null +++ b/test/unit/external_person_test.rb @@ -0,0 +1,137 @@ +# encoding: UTF-8 +require_relative "../test_helper" + +class ExternalPersonTest < ActiveSupport::TestCase + fixtures :environments + + def setup + @external_person = ExternalPerson.create!(identifier: 'johnlock', + name: 'John Lock', + source: 'anerenvironment.org', + email: 'john@lock.org', + created_at: Date.yesterday + ) + end + + should 'have no permissions' do + assert_equivalent [], @external_person.role_assignments + refute @external_person.has_permission?(:manage_friends) + end + + should 'have no suggested profiles' do + assert_equivalent [], @external_person.suggested_communities + assert_equivalent [], @external_person.suggested_people + assert_equivalent [], @external_person.suggested_profiles + end + + should 'have no friendships' do + refute @external_person.add_friend(fast_create(Person)) + assert_equivalent [], @external_person.friendships + end + + should 'not be a member of any communities' do + community = fast_create(Community) + refute community.add_member(@external_person) + assert_equivalent [], @external_person.memberships + end + + should 'not have any favorite enterprises' do + assert_equivalent [], @external_person.favorite_enterprises + end + + should 'be a person' do + assert @external_person.person? + end + + should 'not be a community, organization or enterprise' do + refute @external_person.community? + refute @external_person.enterprise? + refute @external_person.organization? + end + + should 'never be an admin for environments' do + refute @external_person.is_admin? + env = Environment.default + env.add_admin @external_person + refute @external_person.is_admin?(env) + assert_equivalent [], env.admins + end + + should 'redirect after login based on environment settings' do + assert_respond_to ExternalPerson.new, :preferred_login_redirection + environment = fast_create(Environment, :redirection_after_login => 'site_homepage') + profile = fast_create(ExternalPerson, :environment_id => environment.id) + assert_equal 'site_homepage', profile.preferred_login_redirection + end + + should 'have an avatar from its original environment' do + assert_match(/http:\/\/#{@external_person.source}\/.*/, @external_person.avatar) + end + + should 'generate a custom profile icon based on its avatar' do + assert_match(/http:\/\/#{@external_person.source}\/.*/, @external_person.profile_custom_icon) + end + + should 'have an url to its profile on its original environment' do + assert_match(/http:\/\/#{@external_person.source}\/profile\/.*/, @external_person.url) + end + + should 'have a public profile url' do + assert_match(/http:\/\/#{@external_person.source}\/profile\/.*/, @external_person.public_profile_url) + end + + should 'have an admin url to its profile on its original environment' do + assert_match(/http:\/\/#{@external_person.source}\/myprofile\/.*/, @external_person.admin_url) + end + + should 'never be a friend of another person' do + friend = fast_create(Person) + friend.add_friend @external_person + refute @external_person.is_a_friend?(friend) + refute friend.is_a_friend?(@external_person) + end + + should 'never send a friend request to another person' do + friend = fast_create(Person) + friend.add_friend @external_person + refute friend.already_request_friendship?(@external_person) + @external_person.add_friend(friend) + refute @external_person.already_request_friendship?(friend) + end + + should 'not follow another profile' do + friend = fast_create(Person) + friend.add_friend @external_person + refute @external_person.follows?(friend) + refute friend.follows?(@external_person) + end + + should 'have an image' do + assert_not_nil @external_person.image + end + + should 'profile image has public filename and mimetype' do + assert_not_nil @external_person.image.public_filename + assert_not_nil @external_person.image.content_type + end + + should 'respond to all instance methods in Profile' do + methods = Profile.public_instance_methods(false) + methods.each do |method| + # We test if ExternalPerson responds to same methods as Profile, but we + # skip methods generated by plugins, libs and validations, which are + # usually only used internally + assert_respond_to ExternalPerson.new, method.to_sym unless method =~ /type_name|^autosave_.*|^after_.*|^before_.*|validate_.*|^attribute_.*|.*_?tags?_?.*|^custom_value.*|^custom_context.*|^xss.*|bar/ + end + end + + should 'respond to all instance methods in Person' do + methods = Person.public_instance_methods(false) + methods.each do |method| + # We test if ExternalPerson responds to same methods as Person, but we + # skip methods generated by plugins, libs and validations, which are + # usually only used internally + assert_respond_to ExternalPerson.new, method.to_sym unless method =~ /^autosave_.*|validate_.*|^before_.*|^after_.*|^assignment_.*|^(city|state)_.*/ + end + end +end diff --git a/test/unit/person_test.rb b/test/unit/person_test.rb index 440a8f6..f605fd2 100644 --- a/test/unit/person_test.rb +++ b/test/unit/person_test.rb @@ -1831,9 +1831,9 @@ class PersonTest < ActiveSupport::TestCase p1 = fast_create(Person) p2 = fast_create(Person) article = fast_create(Article) - c1 = fast_create(Comment, :source_id => article.id, :author_id => p1.id) - c2 = fast_create(Comment, :source_id => article.id, :author_id => p2.id) - c3 = fast_create(Comment, :source_id => article.id, :author_id => p1.id) + c1 = fast_create(Comment, :source_id => article.id, :author_id => p1.id, :author_type => 'Profile') + c2 = fast_create(Comment, :source_id => article.id, :author_id => p2.id, :author_type => 'Profile') + c3 = fast_create(Comment, :source_id => article.id, :author_id => p1.id, :author_type => 'Profile') assert_equivalent [c1,c3], p1.comments end diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb index 4cfb176..2cb8501 100644 --- a/test/unit/user_test.rb +++ b/test/unit/user_test.rb @@ -759,6 +759,14 @@ class UserTest < ActiveSupport::TestCase end end + should 'get person or external person' do + user = create_user('new_user') + assert_kind_of Person, user.person + 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 + assert_kind_of ExternalPerson, user.person + assert_kind_of Person, user.person_without_external + end + protected def new_user(options = {}) user = User.new({ :login => 'quire', :email => 'quire@example.com', :password => 'quire', :password_confirmation => 'quire' }.merge(options)) diff --git a/vendor/plugins/noosfero_caching/init.rb b/vendor/plugins/noosfero_caching/init.rb index bd3e6c9..ac20e2b 100644 --- a/vendor/plugins/noosfero_caching/init.rb +++ b/vendor/plugins/noosfero_caching/init.rb @@ -27,7 +27,7 @@ module NoosferoHttpCaching end def noosfero_session_check - headers["X-Noosfero-Auth"] = (session[:user] != nil).to_s + headers["X-Noosfero-Auth"] = (session[:user] != nil || session[:external] != nil).to_s end class Middleware -- libgit2 0.21.2