Compare View

switch
from
...
to
 
Commits (54)
Showing 300 changed files   Show diff stats
app/api/entities.rb
... ... @@ -124,6 +124,7 @@ module Api
124 124 expose :type
125 125 expose :custom_header
126 126 expose :custom_footer
  127 + expose :layout_template
127 128 expose :permissions do |profile, options|
128 129 Entities.permissions_for_entity(profile, options[:current_person],
129 130 :allow_post_content?, :allow_edit?, :allow_destroy?)
... ... @@ -264,6 +265,7 @@ module Api
264 265 expose :name
265 266 expose :id
266 267 expose :description
  268 + expose :layout_template
267 269 expose :settings, if: lambda { |instance, options| options[:is_admin] }
268 270 end
269 271  
... ...
app/api/helpers.rb
... ... @@ -302,12 +302,12 @@ module Api
302 302 end
303 303  
304 304 def cant_be_saved_request!(attribute)
305   - message = _("(Invalid request) %s can't be saved") % attribute
  305 + message = _("(Invalid request) %s can't be saved").html_safe % attribute
306 306 render_api_error!(message, 400)
307 307 end
308 308  
309 309 def bad_request!(attribute)
310   - message = _("(Invalid request) %s not given") % attribute
  310 + message = _("(Invalid request) %s not given").html_safe % attribute
311 311 render_api_error!(message, 400)
312 312 end
313 313  
... ...
app/api/v1/people.rb
... ... @@ -119,6 +119,20 @@ module Api
119 119 members = select_filtered_collection_of(profile, 'members', params)
120 120 present members, :with => Entities::Person, :current_person => current_person
121 121 end
  122 +
  123 + post do
  124 + authenticate!
  125 + profile = environment.profiles.find_by id: params[:profile_id]
  126 + profile.add_member(current_person) rescue forbidden!
  127 + {pending: !current_person.is_member_of?(profile)}
  128 + end
  129 +
  130 + delete do
  131 + authenticate!
  132 + profile = environment.profiles.find_by id: params[:profile_id]
  133 + profile.remove_member(current_person)
  134 + present current_person, :with => Entities::Person, :current_person => current_person
  135 + end
122 136 end
123 137 end
124 138 end
... ...
app/controllers/admin/categories_controller.rb
... ... @@ -44,7 +44,7 @@ class CategoriesController < AdminController
44 44 if request.post?
45 45 @category.update!(params[:category])
46 46 @saved = true
47   - session[:notice] = _("Category %s saved." % @category.name)
  47 + session[:notice] = _("Category %s saved." % @category.name).html_safe
48 48 redirect_to :action => 'index'
49 49 end
50 50 rescue Exception => e
... ...
app/controllers/application_controller.rb
... ... @@ -14,6 +14,20 @@ class ApplicationController < ActionController::Base
14 14 before_filter :redirect_to_current_user
15 15  
16 16 before_filter :set_session_theme
  17 +
  18 + # FIXME: only include necessary methods
  19 + include ApplicationHelper
  20 +
  21 + # concerns
  22 + include PermissionCheck
  23 + include CustomDesign
  24 + include NeedsProfile
  25 +
  26 + # implementations
  27 + include FindByContents
  28 + include Noosfero::Plugin::HotSpot
  29 + include SearchTermHelper
  30 +
17 31 def set_session_theme
18 32 if params[:theme]
19 33 session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil
... ... @@ -48,7 +62,6 @@ class ApplicationController < ActionController::Base
48 62 end
49 63 end
50 64  
51   - include ApplicationHelper
52 65 layout :get_layout
53 66 def get_layout
54 67 return false if request.format == :js or request.xhr?
... ... @@ -74,9 +87,6 @@ class ApplicationController < ActionController::Base
74 87 helper :document
75 88 helper :language
76 89  
77   - include DesignHelper
78   - include PermissionCheck
79   -
80 90 before_filter :set_locale
81 91 def set_locale
82 92 FastGettext.available_locales = environment.available_locales
... ... @@ -89,8 +99,6 @@ class ApplicationController < ActionController::Base
89 99 end
90 100 end
91 101  
92   - include NeedsProfile
93   -
94 102 attr_reader :environment
95 103  
96 104 # declares that the given <tt>actions</tt> cannot be accessed by other HTTP
... ... @@ -107,6 +115,10 @@ class ApplicationController &lt; ActionController::Base
107 115  
108 116 protected
109 117  
  118 + def accept_only_post
  119 + return render_not_found if !request.post?
  120 + end
  121 +
110 122 def verified_request?
111 123 super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
112 124 end
... ... @@ -151,8 +163,6 @@ class ApplicationController &lt; ActionController::Base
151 163 end
152 164 end
153 165  
154   - include Noosfero::Plugin::HotSpot
155   -
156 166 # FIXME this filter just loads @plugins to children controllers and helpers
157 167 def init_noosfero_plugins
158 168 plugins
... ... @@ -184,9 +194,6 @@ class ApplicationController &lt; ActionController::Base
184 194 end
185 195 end
186 196  
187   - include SearchTermHelper
188   - include FindByContents
189   -
190 197 def find_suggestions(query, context, asset, options={})
191 198 plugins.dispatch_first(:find_suggestions, query, context, asset, options)
192 199 end
... ...
app/controllers/box_organizer_controller.rb
... ... @@ -109,7 +109,7 @@ class BoxOrganizerController &lt; ApplicationController
109 109 def show_block_type_info
110 110 type = params[:type]
111 111 if type.blank? || !available_blocks.map(&:name).include?(type)
112   - raise ArgumentError.new("Type %s is not allowed. Go away." % type)
  112 + raise ArgumentError.new("Type %s is not allowed. Go away.".html_safe % type)
113 113 end
114 114 @block = type.constantize.new
115 115 @block.box = Box.new(:owner => boxes_holder)
... ... @@ -122,7 +122,7 @@ class BoxOrganizerController &lt; ApplicationController
122 122  
123 123 def new_block(type, box)
124 124 if !available_blocks.map(&:name).include?(type)
125   - raise ArgumentError.new("Type %s is not allowed. Go away." % type)
  125 + raise ArgumentError.new("Type %s is not allowed. Go away.".html_safe % type)
126 126 end
127 127 block = type.constantize.new
128 128 box.blocks << block
... ...
app/controllers/concerns/authenticated_system.rb 0 → 100644
... ... @@ -0,0 +1,169 @@
  1 +module AuthenticatedSystem
  2 +
  3 + protected
  4 +
  5 + extend ActiveSupport::Concern
  6 +
  7 + included do
  8 + if self < ActionController::Base
  9 + around_filter :user_set_current
  10 + before_filter :override_user
  11 + before_filter :login_from_cookie
  12 + end
  13 +
  14 + # Inclusion hook to make #current_user and #logged_in?
  15 + # available as ActionView helper methods.
  16 + helper_method :current_user, :logged_in?
  17 + end
  18 +
  19 + # Returns true or false if the user is logged in.
  20 + # Preloads @current_user with the user model if they're logged in.
  21 + def logged_in?
  22 + current_user != nil
  23 + end
  24 +
  25 + # Accesses the current user from the session.
  26 + def current_user user_id = session[:user]
  27 + @current_user ||= begin
  28 + user = User.find_by id: user_id if user_id
  29 + user.session = session if user
  30 + User.current = user
  31 + user
  32 + end
  33 + end
  34 +
  35 + # Store the given user in the session.
  36 + def current_user=(new_user)
  37 + if new_user.nil?
  38 + session.delete(:user)
  39 + else
  40 + session[:user] = new_user.id
  41 + new_user.session = session
  42 + new_user.register_login
  43 + end
  44 + @current_user = User.current = new_user
  45 + end
  46 +
  47 + # See impl. from http://stackoverflow.com/a/2513456/670229
  48 + def user_set_current
  49 + User.current = current_user
  50 + yield
  51 + ensure
  52 + # to address the thread variable leak issues in Puma/Thin webserver
  53 + User.current = nil
  54 + end
  55 +
  56 + # Check if the user is authorized.
  57 + #
  58 + # Override this method in your controllers if you want to restrict access
  59 + # to only a few actions or if you want to check if the user
  60 + # has the correct rights.
  61 + #
  62 + # Example:
  63 + #
  64 + # # only allow nonbobs
  65 + # def authorize?
  66 + # current_user.login != "bob"
  67 + # end
  68 + def authorized?
  69 + true
  70 + end
  71 +
  72 + # Filter method to enforce a login requirement.
  73 + #
  74 + # To require logins for all actions, use this in your controllers:
  75 + #
  76 + # before_filter :login_required
  77 + #
  78 + # To require logins for specific actions, use this in your controllers:
  79 + #
  80 + # before_filter :login_required, :only => [ :edit, :update ]
  81 + #
  82 + # To skip this in a subclassed controller:
  83 + #
  84 + # skip_before_filter :login_required
  85 + #
  86 + def login_required
  87 + username, passwd = get_auth_data
  88 + if username && passwd
  89 + self.current_user ||= User.authenticate(username, passwd) || nil
  90 + end
  91 + if logged_in? && authorized?
  92 + true
  93 + else
  94 + if params[:require_login_popup]
  95 + render :json => { :require_login_popup => true }
  96 + else
  97 + access_denied
  98 + end
  99 + end
  100 + end
  101 +
  102 + # Redirect as appropriate when an access request fails.
  103 + #
  104 + # The default action is to redirect to the login screen.
  105 + #
  106 + # Override this method in your controllers if you want to have special
  107 + # behavior in case the user is not authorized
  108 + # to access the requested action. For example, a popup window might
  109 + # simply close itself.
  110 + def access_denied
  111 + respond_to do |accepts|
  112 + accepts.html do
  113 + if request.xhr?
  114 + render :text => _('Access denied'), :status => 401
  115 + else
  116 + store_location
  117 + redirect_to :controller => '/account', :action => 'login'
  118 + end
  119 + end
  120 + accepts.xml do
  121 + headers["Status"] = "Unauthorized"
  122 + headers["WWW-Authenticate"] = %(Basic realm="Web Password")
  123 + render :text => "Could't authenticate you", :status => '401 Unauthorized'
  124 + end
  125 + end
  126 + false
  127 + end
  128 +
  129 + # Store the URI of the current request in the session.
  130 + #
  131 + # We can return to this location by calling #redirect_back_or_default.
  132 + def store_location(location = request.url)
  133 + session[:return_to] = location
  134 + end
  135 +
  136 + # Redirect to the URI stored by the most recent store_location call or
  137 + # to the passed default.
  138 + def redirect_back_or_default(default)
  139 + if session[:return_to]
  140 + redirect_to(session.delete(:return_to))
  141 + else
  142 + redirect_to(default)
  143 + end
  144 + end
  145 +
  146 + def override_user
  147 + return if params[:override_user].blank?
  148 + return unless logged_in? and user.is_admin? environment
  149 + @current_user = nil
  150 + current_user params[:override_user]
  151 + end
  152 +
  153 + # When called with before_filter :login_from_cookie will check for an :auth_token
  154 + # cookie and log the user back in if apropriate
  155 + def login_from_cookie
  156 + return if cookies[:auth_token].blank? or logged_in?
  157 + user = User.where(remember_token: cookies[:auth_token]).first
  158 + self.current_user = user if user and user.remember_token?
  159 + end
  160 +
  161 + private
  162 + @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
  163 + # gets BASIC auth info
  164 + def get_auth_data
  165 + auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
  166 + auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
  167 + return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
  168 + end
  169 +end
... ...
app/controllers/concerns/custom_design.rb 0 → 100644
... ... @@ -0,0 +1,50 @@
  1 +module CustomDesign
  2 +
  3 + extend ActiveSupport::Concern
  4 +
  5 + included do
  6 + extend ClassMethods
  7 + include InstanceMethods
  8 + before_filter :load_custom_design if self.respond_to? :before_filter
  9 + end
  10 +
  11 + module ClassMethods
  12 +
  13 + def no_design_blocks
  14 + @no_design_blocks = true
  15 + end
  16 +
  17 + def use_custom_design options = {}
  18 + @custom_design = options
  19 + end
  20 +
  21 + def custom_design
  22 + @custom_design ||= {}
  23 + end
  24 +
  25 + def uses_design_blocks?
  26 + !@no_design_blocks
  27 + end
  28 +
  29 + end
  30 +
  31 + module InstanceMethods
  32 +
  33 + protected
  34 +
  35 + def uses_design_blocks?
  36 + !@no_design_blocks && self.class.uses_design_blocks?
  37 + end
  38 +
  39 + def load_custom_design
  40 + # see also: LayoutHelper#body_classes
  41 + @layout_template = self.class.custom_design[:layout_template]
  42 + end
  43 +
  44 + def custom_design
  45 + @custom_design || self.class.custom_design
  46 + end
  47 +
  48 + end
  49 +
  50 +end
... ...
app/controllers/concerns/needs_profile.rb 0 → 100644
... ... @@ -0,0 +1,40 @@
  1 +module NeedsProfile
  2 +
  3 + module ClassMethods
  4 + def needs_profile
  5 + before_filter :load_profile
  6 + end
  7 + end
  8 +
  9 + def self.included(including)
  10 + including.send(:extend, NeedsProfile::ClassMethods)
  11 + end
  12 +
  13 + def boxes_holder
  14 + profile || environment # prefers profile, but defaults to environment
  15 + end
  16 +
  17 + def profile
  18 + @profile
  19 + end
  20 +
  21 + protected
  22 +
  23 + def load_profile
  24 + if params[:profile]
  25 + params[:profile].downcase!
  26 + @profile ||= environment.profiles.where(identifier: params[:profile]).first
  27 + end
  28 +
  29 + if @profile
  30 + profile_hostname = @profile.hostname
  31 + if profile_hostname && profile_hostname != request.host
  32 + params.delete(:profile)
  33 + redirect_to(Noosfero.url_options.merge(params).merge(:host => profile_hostname))
  34 + end
  35 + else
  36 + render_not_found
  37 + end
  38 + end
  39 +
  40 +end
... ...
app/controllers/my_profile/circles_controller.rb 0 → 100644
... ... @@ -0,0 +1,58 @@
  1 +class CirclesController < MyProfileController
  2 +
  3 + before_action :accept_only_post, :only => [:create, :update, :destroy]
  4 +
  5 + def index
  6 + @circles = profile.circles
  7 + end
  8 +
  9 + def new
  10 + @circle = Circle.new
  11 + end
  12 +
  13 + def create
  14 + @circle = Circle.new(params[:circle].merge({ :person => profile }))
  15 + if @circle.save
  16 + redirect_to :action => 'index'
  17 + else
  18 + render :action => 'new'
  19 + end
  20 + end
  21 +
  22 + def xhr_create
  23 + if request.xhr?
  24 + circle = Circle.new(params[:circle].merge({:person => profile }))
  25 + if circle.save
  26 + render :partial => "circle_checkbox", :locals => { :circle => circle },
  27 + :status => 201
  28 + else
  29 + render :text => _('The circle could not be saved'), :status => 400
  30 + end
  31 + else
  32 + render_not_found
  33 + end
  34 + end
  35 +
  36 + def edit
  37 + @circle = Circle.find_by_id(params[:id])
  38 + render_not_found if @circle.nil?
  39 + end
  40 +
  41 + def update
  42 + @circle = Circle.find_by_id(params[:id])
  43 + return render_not_found if @circle.nil?
  44 +
  45 + if @circle.update(params[:circle])
  46 + redirect_to :action => 'index'
  47 + else
  48 + render :action => 'edit'
  49 + end
  50 + end
  51 +
  52 + def destroy
  53 + @circle = Circle.find_by_id(params[:id])
  54 + return render_not_found if @circle.nil?
  55 + @circle.destroy
  56 + redirect_to :action => 'index'
  57 + end
  58 +end
... ...
app/controllers/my_profile/followers_controller.rb 0 → 100644
... ... @@ -0,0 +1,43 @@
  1 +class FollowersController < MyProfileController
  2 +
  3 + before_action :only_for_person, :only => :index
  4 + before_action :accept_only_post, :only => [:update_category]
  5 +
  6 + def index
  7 + @followed_people = profile.followed_profiles.order(:type)
  8 + @profile_types = {_('All profiles') => nil}.merge(Circle.profile_types).to_a
  9 +
  10 + if params['filter'].present?
  11 + @followed_people = @followed_people.where(:type => params['filter'])
  12 + @active_filter = params['filter']
  13 + end
  14 +
  15 + @followed_people = @followed_people.paginate(:per_page => 15, :page => params[:npage])
  16 + end
  17 +
  18 + def set_category_modal
  19 + followed_profile = Profile.find(params[:followed_profile_id])
  20 + circles = Circle.where(:person => profile, :profile_type => followed_profile.class.name)
  21 + render :partial => 'followers/edit_circles_modal', :locals => { :circles => circles, :followed_profile => followed_profile }
  22 + end
  23 +
  24 + def update_category
  25 + followed_profile = Profile.find_by(:id => params["followed_profile_id"])
  26 +
  27 + selected_circles = params[:circles].map{ |circle_name, circle_id| Circle.find_by(:id => circle_id) }.select{ |c| c.present? }
  28 +
  29 + if followed_profile
  30 + profile.update_profile_circles(followed_profile, selected_circles)
  31 + render :text => _("Circles of %s updated successfully") % followed_profile.name, :status => 200
  32 + else
  33 + render :text => _("Error: No profile to follow."), :status => 400
  34 + end
  35 + end
  36 +
  37 + protected
  38 +
  39 + def only_for_person
  40 + render_not_found unless profile.person?
  41 + end
  42 +
  43 +end
... ...
app/controllers/public/account_controller.rb
... ... @@ -205,7 +205,7 @@ class AccountController &lt; ApplicationController
205 205 if params[:value].blank?
206 206 @change_password.errors[:base] << _('Can not recover user password with blank value.')
207 207 else
208   - @change_password.errors[:base] << _('Could not find any user with %s equal to "%s".') % [fields_label, params[:value]]
  208 + @change_password.errors[:base] << _('Could not find any user with %s equal to "%s".').html_safe % [fields_label, params[:value]]
209 209 end
210 210 rescue ActiveRecord::RecordInvalid
211 211 @change_password.errors[:base] << _('Could not perform password recovery for the user.')
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -128,9 +128,9 @@ class ContentViewerController &lt; ApplicationController
128 128 end
129 129  
130 130 unless @page.display_to?(user)
131   - if !profile.visible? || profile.secret? || (user && user.follows?(profile)) || user.blank?
  131 + if !profile.visible? || profile.secret? || (user && profile.in_social_circle?(user)) || user.blank?
132 132 render_access_denied
133   - else #!profile.public?
  133 + else
134 134 private_profile_partial_parameters
135 135 render :template => 'profile/_private_profile', :status => 403, :formats => [:html]
136 136 end
... ...
app/controllers/public/profile_controller.rb
... ... @@ -3,7 +3,9 @@ class ProfileController &lt; PublicController
3 3 needs_profile
4 4 before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add]
5 5 before_filter :store_location, :only => [:join, :join_not_logged, :report_abuse, :send_mail]
6   - before_filter :login_required, :only => [:add, :join, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity, :send_mail]
  6 + before_filter :login_required, :only => [:add, :join, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity, :send_mail, :follow, :unfollow]
  7 + before_filter :allow_followers?, :only => [:follow, :unfollow]
  8 + before_filter :accept_only_post, :only => [:follow, :unfollow]
7 9  
8 10 helper TagsHelper
9 11 helper ActionTrackerHelper
... ... @@ -42,8 +44,8 @@ class ProfileController &lt; PublicController
42 44 feed_writer = FeedWriter.new
43 45 data = feed_writer.write(
44 46 tagged,
45   - :title => _("%s's contents tagged with \"%s\"") % [profile.name, @tag],
46   - :description => _("%s's contents tagged with \"%s\"") % [profile.name, @tag],
  47 + :title => _("%s's contents tagged with \"%s\"").html_safe % [profile.name, @tag],
  48 + :description => _("%s's contents tagged with \"%s\"").html_safe % [profile.name, @tag],
47 49 :link => url_for(profile.url)
48 50 )
49 51 render :text => data, :content_type => "text/xml"
... ... @@ -65,6 +67,14 @@ class ProfileController &lt; PublicController
65 67 end
66 68 end
67 69  
  70 + def following
  71 + @followed_people = profile.followed_profiles.paginate(:per_page => per_page, :page => params[:npage], :total_entries => profile.followed_profiles.count)
  72 + end
  73 +
  74 + def followed
  75 + @followed_by = profile.followers.paginate(:per_page => per_page, :page => params[:npage], :total_entries => profile.followers.count)
  76 + end
  77 +
68 78 def members
69 79 if is_cache_expired?(profile.members_cache_key(params))
70 80 sort = (params[:sort] == 'desc') ? params[:sort] : 'asc'
... ... @@ -88,7 +98,7 @@ class ProfileController &lt; PublicController
88 98  
89 99 def join_modal
90 100 profile.add_member(user)
91   - session[:notice] = _('%s administrator still needs to accept you as member.') % profile.name
  101 + session[:notice] = _('%s administrator still needs to accept you as member.').html_safe % profile.name
92 102 redirect_to :action => :index
93 103 end
94 104  
... ... @@ -98,12 +108,12 @@ class ProfileController &lt; PublicController
98 108  
99 109 profile.add_member(user)
100 110 if !profile.members.include?(user)
101   - render :text => {:message => _('%s administrator still needs to accept you as member.') % profile.name}.to_json
  111 + render :text => {:message => _('%s administrator still needs to accept you as member.').html_safe % profile.name}.to_json
102 112 else
103   - render :text => {:message => _('You just became a member of %s.') % profile.name}.to_json
  113 + render :text => {:message => _('You just became a member of %s.').html_safe % profile.name}.to_json
104 114 end
105 115 else
106   - render :text => {:message => _('You are already a member of %s.') % profile.name}.to_json
  116 + render :text => {:message => _('You are already a member of %s.').html_safe % profile.name}.to_json
107 117 end
108 118 end
109 119  
... ... @@ -125,7 +135,7 @@ class ProfileController &lt; PublicController
125 135 render :text => current_person.leave(profile, params[:reload])
126 136 end
127 137 else
128   - render :text => {:message => _('You are not a member of %s.') % profile.name}.to_json
  138 + render :text => {:message => _('You are not a member of %s.').html_safe % profile.name}.to_json
129 139 end
130 140 end
131 141  
... ... @@ -145,12 +155,41 @@ class ProfileController &lt; PublicController
145 155 # FIXME this shouldn't be in Person model?
146 156 if !user.memberships.include?(profile)
147 157 AddFriend.create!(:person => user, :friend => profile)
148   - render :text => _('%s still needs to accept being your friend.') % profile.name
  158 + render :text => _('%s still needs to accept being your friend.').html_safe % profile.name
149 159 else
150   - render :text => _('You are already a friend of %s.') % profile.name
  160 + render :text => _('You are already a friend of %s.').html_safe % profile.name
  161 + end
  162 + end
  163 +
  164 + def follow
  165 + if profile.followed_by?(current_person)
  166 + render :text => _("You are already following %s.") % profile.name, :status => 400
  167 + else
  168 + selected_circles = params[:circles].map{ |circle_name, circle_id| Circle.find_by(:id => circle_id) }.select{ |c| c.present? }
  169 + if selected_circles.present?
  170 + current_person.follow(profile, selected_circles)
  171 + render :text => _("You are now following %s") % profile.name, :status => 200
  172 + else
  173 + render :text => _("Select at least one circle to follow %s.") % profile.name, :status => 400
  174 + end
151 175 end
152 176 end
153 177  
  178 + def find_profile_circles
  179 + circles = Circle.where(:person => current_person, :profile_type => profile.class.name)
  180 + render :partial => 'blocks/profile_info_actions/circles', :locals => { :circles => circles, :profile_types => Circle.profile_types.to_a }
  181 + end
  182 +
  183 + def unfollow
  184 + follower = params[:follower_id].present? ? Person.find_by(id: params[:follower_id]) : current_person
  185 +
  186 + if follower && follower.follows?(profile)
  187 + follower.unfollow(profile)
  188 + end
  189 + redirect_url = params["redirect_to"] ? params["redirect_to"] : profile.url
  190 + redirect_to redirect_url
  191 + end
  192 +
154 193 def check_friendship
155 194 unless logged_in?
156 195 render :text => ''
... ... @@ -178,7 +217,7 @@ class ProfileController &lt; PublicController
178 217 def unblock
179 218 if current_user.person.is_admin?(profile.environment)
180 219 profile.unblock
181   - session[:notice] = _("You have unblocked %s successfully. ") % profile.name
  220 + session[:notice] = _("You have unblocked %s successfully. ").html_safe % profile.name
182 221 redirect_to :controller => 'profile', :action => 'index'
183 222 else
184 223 message = _('You are not allowed to unblock enterprises in this environment.')
... ... @@ -437,4 +476,8 @@ class ProfileController &lt; PublicController
437 476 [:image, :domains, :preferred_domain, :environment]
438 477 end
439 478  
  479 + def allow_followers?
  480 + render_not_found unless profile.allow_followers?
  481 + end
  482 +
440 483 end
... ...
app/helpers/action_tracker_helper.rb
... ... @@ -14,13 +14,23 @@ module ActionTrackerHelper
14 14 }
15 15 end
16 16  
  17 + def new_follower_description ta
  18 + n_('has 1 new follower:<br />%{name}', 'has %{num} new followers:<br />%{name}', ta.get_follower_name.size).html_safe % {
  19 + num: ta.get_follower_name.size,
  20 + name: safe_join(ta.collect_group_with_index(:follower_name) do |n,i|
  21 + link_to image_tag(ta.get_follower_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/person-icon.png")),
  22 + ta.get_follower_url[i], title: n
  23 + end)
  24 + }
  25 + end
  26 +
17 27 def join_community_description ta
18   - n_('has joined 1 community:<br />%{name}'.html_safe, 'has joined %{num} communities:<br />%{name}'.html_safe, ta.get_resource_name.size) % {
  28 + n_('has joined 1 community:<br />%{name}', 'has joined %{num} communities:<br />%{name}', ta.get_resource_name.size).html_safe % {
19 29 num: ta.get_resource_name.size,
20   - name: ta.collect_group_with_index(:resource_name) do |n,i|
  30 + name: safe_join(ta.collect_group_with_index(:resource_name) do |n,i|
21 31 link = link_to image_tag(ta.get_resource_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/community-icon.png")),
22 32 ta.get_resource_url[i], title: n
23   - end.join.html_safe
  33 + end)
24 34 }
25 35 end
26 36  
... ... @@ -68,9 +78,9 @@ module ActionTrackerHelper
68 78 end
69 79  
70 80 def favorite_enterprise_description ta
71   - _('favorited enterprise %{title}') % {
  81 + (_('favorited enterprise %{title}') % {
72 82 title: link_to(truncate(ta.get_enterprise_name), ta.get_enterprise_url),
73   - }
  83 + }).html_safe
74 84 end
75 85  
76 86 end
... ...
app/helpers/application_helper.rb
... ... @@ -880,7 +880,7 @@ module ApplicationHelper
880 880 link_to_all = link_to(content_tag('strong', _('See all')), :controller => 'memberships', :profile => user.identifier)
881 881 end
882 882 link = list.map do |element|
883   - link_to(content_tag('strong', _('<span>Manage</span> %s') % element.short_name(25)), element.admin_url, :class => "icon-menu-"+element.class.identification.underscore, :title => _('Manage %s') % element.short_name)
  883 + link_to(content_tag('strong', _('<span>Manage</span> %s').html_safe % element.short_name(25)), element.admin_url, :class => "icon-menu-"+element.class.identification.underscore, :title => _('Manage %s').html_safe % element.short_name)
884 884 end
885 885 if link_to_all
886 886 link << link_to_all
... ... @@ -921,7 +921,7 @@ module ApplicationHelper
921 921 logout_link = link_to(logout_icon.html_safe, { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
922 922 join_result = safe_join(
923 923 [welcome_span.html_safe, render_environment_features(:usermenu).html_safe, admin_link.html_safe,
924   - manage_enterprises.html_safe, manage_communities.html_safe, ctrl_panel_link.html_safe,
  924 + manage_enterprises, manage_communities, ctrl_panel_link.html_safe,
925 925 pending_tasks_count.html_safe, logout_link.html_safe], "")
926 926 join_result
927 927 end
... ...
app/helpers/forms_helper.rb
... ... @@ -128,14 +128,14 @@ module FormsHelper
128 128 counter += 1
129 129 row << item
130 130 if counter % per_row == 0
131   - rows << content_tag('tr', row.join("\n"))
  131 + rows << content_tag('tr', row.join("\n").html_safe)
132 132 counter = 0
133 133 row = []
134 134 end
135 135 end
136   - rows << content_tag('tr', row.join("\n"))
  136 + rows << content_tag('tr', row.join("\n").html_safe)
137 137  
138   - content_tag('table',rows.join("\n"))
  138 + content_tag('table',rows.join("\n").html_safe)
139 139 end
140 140  
141 141 def date_field(name, value, datepicker_options = {}, html_options = {})
... ...
app/helpers/profile_helper.rb
... ... @@ -11,7 +11,7 @@ module ProfileHelper
11 11 PERSON_CATEGORIES[:location] = [:address, :address_reference, :zip_code, :city, :state, :district, :country, :nationality]
12 12 PERSON_CATEGORIES[:work] = [:organization, :organization_website, :professional_activity]
13 13 PERSON_CATEGORIES[:study] = [:schooling, :formation, :area_of_study]
14   - PERSON_CATEGORIES[:network] = [:friends, :communities, :enterprises]
  14 + PERSON_CATEGORIES[:network] = [:friends, :followers, :followed_profiles, :communities, :enterprises]
15 15 PERSON_CATEGORIES.merge!(COMMON_CATEGORIES)
16 16  
17 17 ORGANIZATION_CATEGORIES = {}
... ... @@ -42,7 +42,8 @@ module ProfileHelper
42 42 :created_at => _('Profile created at'),
43 43 :members_count => _('Members'),
44 44 :privacy_setting => _('Privacy setting'),
45   - :article_tags => _('Tags')
  45 + :article_tags => _('Tags'),
  46 + :followed_profiles => _('Following')
46 47 }
47 48  
48 49 EXCEPTION = {
... ... @@ -144,6 +145,14 @@ module ProfileHelper
144 145 link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url)
145 146 end
146 147  
  148 + def treat_followers(followers)
  149 + link_to(profile.followers.count, {:action=>"followed", :controller=>"profile", :profile=>"#{profile.identifier}"})
  150 + end
  151 +
  152 + def treat_followed_profiles(followed_profiles)
  153 + link_to(profile.followed_profiles.count, {:action=>"following", :controller=>"profile", :profile=>"#{profile.identifier}"})
  154 + end
  155 +
147 156 def treat_events(events)
148 157 link_to events.published.count, :controller => 'events', :action => 'events'
149 158 end
... ...
app/jobs/notify_activity_to_profiles_job.rb
... ... @@ -19,8 +19,8 @@ class NotifyActivityToProfilesJob &lt; Struct.new(:tracked_action_id)
19 19 # Notify the user
20 20 ActionTrackerNotification.create(:profile_id => tracked_action.user.id, :action_tracker_id => tracked_action.id)
21 21  
22   - # Notify all friends
23   - ActionTrackerNotification.connection.execute("insert into action_tracker_notifications(profile_id, action_tracker_id) select f.friend_id, #{tracked_action.id} from friendships as f where person_id=#{tracked_action.user.id} and f.friend_id not in (select atn.profile_id from action_tracker_notifications as atn where atn.action_tracker_id = #{tracked_action.id})")
  22 + # Notify all followers
  23 + ActionTrackerNotification.connection.execute("INSERT INTO action_tracker_notifications(profile_id, action_tracker_id) SELECT DISTINCT c.person_id, #{tracked_action.id} FROM profiles_circles AS p JOIN circles as c ON c.id = p.circle_id WHERE p.profile_id = #{tracked_action.user.id} AND (c.person_id NOT IN (SELECT atn.profile_id FROM action_tracker_notifications AS atn WHERE atn.action_tracker_id = #{tracked_action.id}))")
24 24  
25 25 if tracked_action.user.is_a? Organization
26 26 ActionTrackerNotification.connection.execute "insert into action_tracker_notifications(profile_id, action_tracker_id) " +
... ...
app/mailers/contact.rb
... ... @@ -47,8 +47,8 @@ class Contact
47 47 content_type: 'text/html',
48 48 to: contact.dest.notification_emails,
49 49 reply_to: contact.email,
50   - subject: "[#{contact.dest.short_name(30)}] " + contact.subject,
51   - from: "#{contact.name} <#{contact.dest.environment.noreply_email}>"
  50 + subject: "[#{contact.dest.short_name(30)}] #{contact.subject}".html_safe,
  51 + from: "#{contact.name} <#{contact.dest.environment.noreply_email}>".html_safe
52 52 }
53 53  
54 54 if contact.sender
... ...
app/mailers/environment_mailing.rb
... ... @@ -30,7 +30,7 @@ class EnvironmentMailing &lt; Mailing
30 30 end
31 31  
32 32 def signature_message
33   - _('Sent by %s.') % source.name
  33 + _('Sent by %s.').html_safe % source.name
34 34 end
35 35  
36 36 def url
... ...
app/mailers/mailing.rb
... ... @@ -2,7 +2,8 @@ require_dependency &#39;mailing_job&#39;
2 2  
3 3 class Mailing < ApplicationRecord
4 4  
5   - acts_as_having_settings :field => :data
  5 + extend ActsAsHavingSettings::ClassMethods
  6 + acts_as_having_settings field: :data
6 7  
7 8 attr_accessible :subject, :body, :data
8 9  
... ... @@ -23,11 +24,11 @@ class Mailing &lt; ApplicationRecord
23 24 end
24 25  
25 26 def generate_from
26   - "#{source.name} <#{if source.is_a? Environment then source.noreply_email else source.contact_email end}>"
  27 + "#{source.name} <#{if source.is_a? Environment then source.noreply_email else source.contact_email end}>".html_safe
27 28 end
28 29  
29 30 def generate_subject
30   - '[%s] %s' % [source.name, subject]
  31 + '[%s] %s'.html_safe % [source.name, subject]
31 32 end
32 33  
33 34 def signature_message
... ...
app/mailers/organization_mailing.rb
... ... @@ -30,7 +30,7 @@ class OrganizationMailing &lt; Mailing
30 30 end
31 31  
32 32 def signature_message
33   - _('Sent by community %s.') % source.name
  33 + _('Sent by community %s.').html_safe % source.name
34 34 end
35 35  
36 36 include Rails.application.routes.url_helpers
... ...
app/mailers/pending_task_notifier.rb
... ... @@ -12,8 +12,8 @@ class PendingTaskNotifier &lt; ApplicationMailer
12 12  
13 13 mail(
14 14 to: person.email,
15   - from: "#{person.environment.name} <#{person.environment.noreply_email}>",
16   - subject: _("[%s] Pending tasks") % person.environment.name
  15 + from: "#{person.environment.name} <#{person.environment.noreply_email}>".html_safe,
  16 + subject: _("[%s] Pending tasks").html_safe % person.environment.name
17 17 )
18 18 end
19 19  
... ...
app/mailers/scrap_notifier.rb
... ... @@ -14,8 +14,8 @@ class ScrapNotifier &lt; ApplicationMailer
14 14 @url = sender.environment.top_url
15 15 mail(
16 16 to: receiver.email,
17   - from: "#{sender.environment.name} <#{sender.environment.noreply_email}>",
18   - subject: _("[%s] You received a scrap!") % [sender.environment.name]
  17 + from: "#{sender.environment.name} <#{sender.environment.noreply_email}>".html_safe,
  18 + subject: _("[%s] You received a scrap!").html_safe % [sender.environment.name]
19 19 )
20 20 end
21 21 end
... ...
app/mailers/task_mailer.rb
... ... @@ -14,7 +14,7 @@ class TaskMailer &lt; ApplicationMailer
14 14 mail(
15 15 to: task.target.notification_emails.compact,
16 16 from: self.class.generate_from(task),
17   - subject: "[%s] %s" % [task.environment.name, task.target_notification_description]
  17 + subject: "[%s] %s".html_safe % [task.environment.name, task.target_notification_description]
18 18 )
19 19 end
20 20  
... ... @@ -27,7 +27,7 @@ class TaskMailer &lt; ApplicationMailer
27 27 mail(
28 28 to: task.friend_email,
29 29 from: self.class.generate_from(task),
30   - subject: '[%s] %s' % [ task.requestor.environment.name, task.target_notification_description ]
  30 + subject: '[%s] %s'.html_safe % [ task.requestor.environment.name, task.target_notification_description ]
31 31 )
32 32 end
33 33  
... ... @@ -43,7 +43,7 @@ class TaskMailer &lt; ApplicationMailer
43 43 mail_with_template(
44 44 to: task.requestor.notification_emails,
45 45 from: self.class.generate_from(task),
46   - subject: '[%s] %s' % [task.requestor.environment.name, task.target_notification_description],
  46 + subject: '[%s] %s'.html_safe % [task.requestor.environment.name, task.target_notification_description],
47 47 email_template: task.email_template,
48 48 template_params: {:environment => task.requestor.environment, :task => task, :message => @message, :url => @url, :requestor => task.requestor}
49 49 )
... ...
app/mailers/user_mailer.rb
... ... @@ -13,8 +13,8 @@ class UserMailer &lt; ApplicationMailer
13 13  
14 14 mail(
15 15 to: user_email,
16   - from: "#{user.environment.name} <#{user.environment.contact_email}>",
17   - subject: _("[%{environment}] Welcome to %{environment} mail!") % { :environment => user.environment.name }
  16 + from: "#{user.environment.name} <#{user.environment.contact_email}>".html_safe,
  17 + subject: _("[%{environment}] Welcome to %{environment} mail!").html_safe % { :environment => user.environment.name }
18 18 )
19 19 end
20 20  
... ... @@ -30,7 +30,7 @@ class UserMailer &lt; ApplicationMailer
30 30 mail_with_template(
31 31 from: "#{user.environment.name} <#{user.environment.contact_email}>",
32 32 to: user.email,
33   - subject: _("[%s] Activate your account") % [user.environment.name],
  33 + subject: _("[%s] Activate your account").html_safe % [user.environment.name],
34 34 template_params: {:environment => user.environment, :activation_code => @activation_code, :redirection => @redirection, :join => @join, :person => user.person, :url => @url},
35 35 email_template: user.environment.email_templates.find_by_template_type(:user_activation),
36 36 )
... ... @@ -44,8 +44,8 @@ class UserMailer &lt; ApplicationMailer
44 44 mail(
45 45 content_type: 'text/html',
46 46 to: user.email,
47   - from: "#{user.environment.name} <#{user.environment.contact_email}>",
48   - subject: email_subject.blank? ? _("Welcome to environment %s") % [user.environment.name] : email_subject,
  47 + from: "#{user.environment.name} <#{user.environment.contact_email}>".html_safe,
  48 + subject: email_subject.blank? ? _("Welcome to environment %s").html_safe % [user.environment.name] : email_subject,
49 49 body: @body
50 50 )
51 51 end
... ... @@ -63,8 +63,8 @@ class UserMailer &lt; ApplicationMailer
63 63 mail(
64 64 content_type: 'text/html',
65 65 to: user.email,
66   - from: "#{user.environment.name} <#{user.environment.contact_email}>",
67   - subject: _("[%s] What about grow up your network?") % user.environment.name
  66 + from: "#{user.environment.name} <#{user.environment.contact_email}>".html_safe,
  67 + subject: _("[%s] What about grow up your network?").html_safe % user.environment.name
68 68 )
69 69 end
70 70  
... ...
app/models/abuse_complaint.rb
... ... @@ -25,7 +25,7 @@ class AbuseComplaint &lt; Task
25 25 end
26 26  
27 27 def title
28   - abuse_reports.count > 1 ? (_('Abuse complaint (%s)') % abuse_reports.count) :_('Abuse complaint')
  28 + abuse_reports.count > 1 ? (_('Abuse complaint (%s)').html_safe % abuse_reports.count) :_('Abuse complaint')
29 29 end
30 30  
31 31 def linked_subject
... ... @@ -57,15 +57,15 @@ class AbuseComplaint &lt; Task
57 57 end
58 58  
59 59 def task_activated_message
60   - _('Your profile was reported by the users of %s due to inappropriate behavior. The administrators of the environment are now reviewing the report. To solve this misunderstanding, please contact the administrators.') % environment.name
  60 + _('Your profile was reported by the users of %s due to inappropriate behavior. The administrators of the environment are now reviewing the report. To solve this misunderstanding, please contact the administrators.').html_safe % environment.name
61 61 end
62 62  
63 63 def task_finished_message
64   - _('Your profile was disabled by the administrators of %s due to inappropriate behavior. To solve this misunderstanding please contact them.') % environment.name
  64 + _('Your profile was disabled by the administrators of %s due to inappropriate behavior. To solve this misunderstanding please contact them.').html_safe % environment.name
65 65 end
66 66  
67 67 def target_notification_description
68   - _('%s was reported due to inappropriate behavior.') % reported.name
  68 + _('%s was reported due to inappropriate behavior.').html_safe % reported.name
69 69 end
70 70  
71 71 def target_notification_message
... ...
app/models/add_member.rb
... ... @@ -22,6 +22,7 @@ class AddMember &lt; Task
22 22 self.roles = [Profile::Roles.member(organization.environment.id).id]
23 23 end
24 24 target.affiliate(requestor, self.roles.select{|r| !r.to_i.zero? }.map{|i| Role.find(i)})
  25 + person.follow(organization, Circle.find_or_create_by(:person => person, :name =>_('memberships'), :profile_type => 'Community'))
25 26 end
26 27  
27 28 def title
... ... @@ -56,7 +57,7 @@ class AddMember &lt; Task
56 57 def target_notification_description
57 58 requestor_email = " (#{requestor.email})" if requestor.may_display_field_to?("email")
58 59  
59   - _("%{requestor}%{requestor_email} wants to be a member of '%{organization}'.") % {:requestor => requestor.name, :requestor_email => requestor_email, :organization => organization.name}
  60 + _("%{requestor}%{requestor_email} wants to be a member of '%{organization}'.").html_safe % {:requestor => requestor.name, :requestor_email => requestor_email, :organization => organization.name}
60 61 end
61 62  
62 63 def target_notification_message
... ...
app/models/article.rb
... ... @@ -13,7 +13,9 @@ class Article &lt; ApplicationRecord
13 13 :image_builder, :show_to_followers, :archived,
14 14 :author, :display_preview, :published_at, :person_followers
15 15  
  16 + extend ActsAsHavingImage::ClassMethods
16 17 acts_as_having_image
  18 +
17 19 include Noosfero::Plugin::HotSpot
18 20  
19 21 SEARCHABLE_FIELDS = {
... ... @@ -91,7 +93,8 @@ class Article &lt; ApplicationRecord
91 93 has_many :article_categorizations_including_virtual, :class_name => 'ArticleCategorization'
92 94 has_many :categories_including_virtual, :through => :article_categorizations_including_virtual, :source => :category
93 95  
94   - acts_as_having_settings :field => :setting
  96 + extend ActsAsHavingSettings::ClassMethods
  97 + acts_as_having_settings field: :setting
95 98  
96 99 settings_items :display_hits, :type => :boolean, :default => true
97 100 settings_items :author_name, :type => :string, :default => ""
... ... @@ -242,6 +245,7 @@ class Article &lt; ApplicationRecord
242 245 acts_as_taggable
243 246 N_('Tag list')
244 247  
  248 + extend ActsAsFilesystem::ActsMethods
245 249 acts_as_filesystem
246 250  
247 251 acts_as_versioned
... ... @@ -534,13 +538,13 @@ class Article &lt; ApplicationRecord
534 538  
535 539 scope :display_filter, lambda {|user, profile|
536 540 return published if (user.nil? && profile && profile.public?)
537   - return [] if user.nil? || (profile && !profile.public? && !user.follows?(profile))
  541 + return [] if user.nil? || (profile && !profile.public? && !profile.in_social_circle?(user))
538 542 where(
539 543 [
540 544 "published = ? OR last_changed_by_id = ? OR profile_id = ? OR ?
541 545 OR (show_to_followers = ? AND ? AND profile_id IN (?))", true, user.id, user.id,
542 546 profile.nil? ? false : user.has_permission?(:view_private_content, profile),
543   - true, (profile.nil? ? true : user.follows?(profile)), ( profile.nil? ? (user.friends.select('profiles.id')) : [profile.id])
  547 + true, (profile.nil? ? true : profile.in_social_circle?(user)), ( profile.nil? ? (user.friends.select('profiles.id')) : [profile.id])
544 548 ]
545 549 )
546 550 }
... ...
app/models/block.rb
... ... @@ -17,6 +17,7 @@ class Block &lt; ApplicationRecord
17 17 belongs_to :mirror_block, :class_name => "Block"
18 18 has_many :observers, :class_name => "Block", :foreign_key => "mirror_block_id"
19 19  
  20 + extend ActsAsHavingSettings::ClassMethods
20 21 acts_as_having_settings
21 22  
22 23 scope :enabled, -> { where :enabled => true }
... ... @@ -88,7 +89,7 @@ class Block &lt; ApplicationRecord
88 89 end
89 90  
90 91 def display_to_user?(user)
91   - display_user == 'all' || (user.nil? && display_user == 'not_logged') || (user && display_user == 'logged') || (user && display_user == 'followers' && user.follows?(owner))
  92 + display_user == 'all' || (user.nil? && display_user == 'not_logged') || (user && display_user == 'logged') || (user && display_user == 'followers' && owner.in_social_circle?(user))
92 93 end
93 94  
94 95 def display_always(context)
... ...
app/models/blog.rb
... ... @@ -2,7 +2,9 @@ class Blog &lt; Folder
2 2  
3 3 attr_accessible :visualization_format
4 4  
  5 + extend ActsAsHavingPosts::ClassMethods
5 6 acts_as_having_posts
  7 +
6 8 include PostsLimit
7 9  
8 10 #FIXME This should be used until there is a migration to fix all blogs that
... ...
app/models/category.rb
... ... @@ -21,6 +21,7 @@ class Category &lt; ApplicationRecord
21 21  
22 22 scope :on_level, -> parent { where :parent_id => parent }
23 23  
  24 + extend ActsAsFilesystem::ActsMethods
24 25 acts_as_filesystem
25 26  
26 27 has_many :article_categorizations
... ... @@ -35,6 +36,7 @@ class Category &lt; ApplicationRecord
35 36 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person'
36 37 has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community'
37 38  
  39 + extend ActsAsHavingImage::ClassMethods
38 40 acts_as_having_image
39 41  
40 42 before_save :normalize_display_color
... ...
app/models/circle.rb 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +class Circle < ApplicationRecord
  2 + has_many :profile_followers
  3 + belongs_to :person
  4 +
  5 + attr_accessible :name, :person, :profile_type
  6 +
  7 + validates :name, presence: true
  8 + validates :person_id, presence: true
  9 + validates :profile_type, presence: true
  10 + validates :person_id, :uniqueness => {:scope => :name, :message => "can't add two circles with the same name"}
  11 +
  12 + validate :profile_type_must_be_in_list
  13 +
  14 + scope :by_owner, -> person{
  15 + where(:person => person)
  16 + }
  17 +
  18 + scope :with_name, -> name{
  19 + where(:name => name)
  20 + }
  21 +
  22 + def self.profile_types
  23 + {
  24 + _("Person") => Person.name,
  25 + _("Community") => Community.name,
  26 + _("Enterprise") => Enterprise.name
  27 + }
  28 + end
  29 +
  30 + def profile_type_must_be_in_list
  31 + valid_profile_types = Circle.profile_types.values
  32 + unless self.profile_type.in? valid_profile_types
  33 + self.errors.add(:profile_type, "invalid profile type")
  34 + end
  35 + end
  36 +
  37 +end
... ...
app/models/comment.rb
... ... @@ -38,6 +38,7 @@ class Comment &lt; ApplicationRecord
38 38  
39 39 validate :article_archived?
40 40  
  41 + extend ActsAsHavingSettings::ClassMethods
41 42 acts_as_having_settings
42 43  
43 44 xss_terminate :only => [ :body, :title, :name ], :on => 'validation'
... ...
app/models/community.rb
... ... @@ -2,7 +2,7 @@ class Community &lt; Organization
2 2  
3 3 attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, :resource_type
4 4 attr_accessible :address_reference, :district, :tag_list, :language, :description
5   - attr_accessible :requires_email
  5 +
6 6 after_destroy :check_invite_member_for_destroy
7 7  
8 8 def self.type_name
... ... @@ -13,9 +13,6 @@ class Community &lt; Organization
13 13 N_('Language')
14 14  
15 15 settings_items :language
16   - settings_items :requires_email, :type => :boolean
17   -
18   - alias_method :requires_email?, :requires_email
19 16  
20 17 extend SetProfileRegionFromCityState::ClassMethods
21 18 set_profile_region_from_city_state
... ...
app/models/concerns/acts_as_filesystem.rb 0 → 100644
... ... @@ -0,0 +1,265 @@
  1 +module ActsAsFilesystem
  2 +
  3 + module ActsMethods
  4 +
  5 + # Declares the ActiveRecord model to acts like a filesystem: objects are
  6 + # arranged in a tree (liks acts_as_tree), and . The underlying table must
  7 + # have the following fields:
  8 + #
  9 + # * name (+:string+) - the title of the object
  10 + # * slug (+:string+)- the title turned in a URL-friendly string (downcased,
  11 + # non-ascii chars transliterated into ascii, all sequences of
  12 + # non-alphanumericd characters changed into dashed)
  13 + # * path (+:text+)- stores the full path of the object (the full path of
  14 + # the parent, a "/" and the slug of the object)
  15 + # * children_count - a cache of the number of children elements.
  16 + def acts_as_filesystem
  17 + # a filesystem is a tree
  18 + acts_as_tree :counter_cache => :children_count
  19 +
  20 + extend ClassMethods
  21 + include InstanceMethods
  22 + if self.has_path?
  23 + after_update :update_children_path
  24 + before_create :set_path
  25 + include InstanceMethods::PathMethods
  26 + end
  27 +
  28 + before_save :set_ancestry
  29 + end
  30 +
  31 + end
  32 +
  33 + module ClassMethods
  34 +
  35 + def build_ancestry(parent_id = nil, ancestry = '')
  36 + ActiveRecord::Base.transaction do
  37 + self.base_class.where(parent_id: parent_id).each do |node|
  38 + node.update_column :ancestry, ancestry
  39 +
  40 + build_ancestry node.id, (ancestry.empty? ? "#{node.formatted_ancestry_id}" :
  41 + "#{ancestry}#{node.ancestry_sep}#{node.formatted_ancestry_id}")
  42 + end
  43 + end
  44 +
  45 + #raise "Couldn't reach and set ancestry on every record" if self.base_class.where('ancestry is null').count != 0
  46 + end
  47 +
  48 + def has_path?
  49 + (['name', 'slug', 'path'] - self.column_names).blank?
  50 + end
  51 +
  52 + end
  53 +
  54 + module InstanceMethods
  55 +
  56 + def ancestry_column
  57 + 'ancestry'
  58 + end
  59 + def ancestry_sep
  60 + '.'
  61 + end
  62 + def has_ancestry?
  63 + self.class.column_names.include? self.ancestry_column
  64 + end
  65 +
  66 + def formatted_ancestry_id
  67 + "%010d" % self.id if self.id
  68 + end
  69 +
  70 + def ancestry
  71 + self[ancestry_column]
  72 + end
  73 + def ancestor_ids
  74 + return nil if !has_ancestry? or ancestry.nil?
  75 + @ancestor_ids ||= ancestry.split(ancestry_sep).map{ |id| id.to_i }
  76 + end
  77 +
  78 + def ancestry=(value)
  79 + self[ancestry_column] = value
  80 + end
  81 + def set_ancestry
  82 + return unless self.has_ancestry?
  83 + if self.ancestry.nil? or (new_record? or parent_id_changed?) or recalculate_path
  84 + self.ancestry = self.hierarchy(true)[0...-1].map{ |p| p.formatted_ancestry_id }.join(ancestry_sep)
  85 + end
  86 + end
  87 +
  88 + def descendents_options
  89 + ["#{self.ancestry_column} LIKE ?", "%#{self.formatted_ancestry_id}%"]
  90 + end
  91 + def descendents
  92 + self.class.where descendents_options
  93 + end
  94 +
  95 + # calculates the level of the record in the records hierarchy. Top-level
  96 + # records have level 0; the children of the top-level records have
  97 + # level 1; the children of records with level 1 have level 2, and so on.
  98 + #
  99 + # A level 0
  100 + # / \
  101 + # B C level 1
  102 + # / \ / \
  103 + # E F G H level 2
  104 + # ...
  105 + def level
  106 + self.hierarchy.size - 1
  107 + end
  108 +
  109 + # Is this record a top-level record?
  110 + def top_level?
  111 + self.parent.nil?
  112 + end
  113 +
  114 + # Is this record a leaf in the hierarchy tree of records?
  115 + #
  116 + # Being a leaf means that this record has no subrecord.
  117 + def leaf?
  118 + self.children.empty?
  119 + end
  120 +
  121 + def top_ancestor
  122 + if has_ancestry? and !ancestry.blank?
  123 + self.class.base_class.find_by id: self.top_ancestor_id
  124 + else
  125 + self.hierarchy.first
  126 + end
  127 + end
  128 + def top_ancestor_id
  129 + if has_ancestry? and !ancestry.nil?
  130 + self.ancestor_ids.first
  131 + else
  132 + self.hierarchy.first.id
  133 + end
  134 + end
  135 +
  136 + # returns the full hierarchy from the top-level item to this one. For
  137 + # example, if item1 has a children item2 and item2 has a children item3,
  138 + # then item3's hierarchy would be [item1, item2, item3].
  139 + #
  140 + # If +reload+ is passed as +true+, then the hierarchy is reload (usefull
  141 + # when the ActiveRecord object was modified in some way, or just after
  142 + # changing parent)
  143 + def hierarchy(reload = false)
  144 + @hierarchy = nil if reload or recalculate_path
  145 +
  146 + if @hierarchy.nil?
  147 + @hierarchy = []
  148 +
  149 + if !reload and !recalculate_path and ancestor_ids
  150 + objects = self.class.base_class.where(id: ancestor_ids)
  151 + ancestor_ids.each{ |id| @hierarchy << objects.find{ |t| t.id == id } }
  152 + @hierarchy << self
  153 + else
  154 + item = self
  155 + while item
  156 + @hierarchy.unshift(item)
  157 + item = item.parent
  158 + end
  159 + end
  160 + end
  161 +
  162 + @hierarchy
  163 + end
  164 +
  165 + def map_traversal(&block)
  166 + result = []
  167 + current_level = [self]
  168 +
  169 + while !current_level.empty?
  170 + result += current_level
  171 + ids = current_level.select {|item| item.children_count > 0}.map(&:id)
  172 + break if ids.empty?
  173 + current_level = self.class.base_class.where(parent_id: ids)
  174 + end
  175 + block ||= (lambda { |x| x })
  176 + result.map(&block)
  177 + end
  178 +
  179 + def all_children
  180 + res = map_traversal
  181 + res.shift
  182 + res
  183 + end
  184 +
  185 + #####
  186 + # Path methods
  187 + # These methods are used when _path_, _name_ and _slug_ attributes exist
  188 + # and should be calculated based on the tree
  189 + #####
  190 + module PathMethods
  191 + # used to know when to trigger batch renaming
  192 + attr_accessor :recalculate_path
  193 +
  194 + # calculates the full path to this record using parent's path.
  195 + def calculate_path
  196 + self.hierarchy.map{ |obj| obj.slug }.join('/')
  197 + end
  198 + def set_path
  199 + if self.path == self.slug && !self.top_level?
  200 + self.path = self.calculate_path
  201 + end
  202 + end
  203 + def explode_path
  204 + path.split(/\//)
  205 + end
  206 +
  207 + def update_children_path
  208 + if self.recalculate_path
  209 + self.children.each do |child|
  210 + child.path = child.calculate_path
  211 + child.recalculate_path = true
  212 + child.save!
  213 + end
  214 + end
  215 + self.recalculate_path = false
  216 + end
  217 +
  218 + # calculates the full name of a record by accessing the name of all its
  219 + # ancestors.
  220 + #
  221 + # If you have this record hierarchy:
  222 + # Record "A"
  223 + # Record "B"
  224 + # Record "C"
  225 + #
  226 + # Then Record "C" will have "A/B/C" as its full name.
  227 + def full_name(sep = '/')
  228 + self.hierarchy.map {|item| item.name || '?' }.join(sep)
  229 + end
  230 +
  231 + # gets the name without leading parents. Useful when dividing records
  232 + # in top-level groups and full names must not include the top-level
  233 + # record which is already a emphasized label
  234 + def full_name_without_leading(count, sep = '/')
  235 + parts = self.full_name(sep).split(sep)
  236 + count.times { parts.shift }
  237 + parts.join(sep)
  238 + end
  239 +
  240 + def set_name(value)
  241 + if self.name != value
  242 + self.recalculate_path = true
  243 + end
  244 + self[:name] = value
  245 + end
  246 +
  247 + # sets the name of the record. Also sets #slug accordingly.
  248 + def name=(value)
  249 + self.set_name(value)
  250 + unless self.name.blank?
  251 + self.slug = self.name.to_slug
  252 + end
  253 + end
  254 +
  255 + # sets the slug of the record. Also sets the path with the new slug value.
  256 + def slug=(value)
  257 + self[:slug] = value
  258 + unless self.slug.blank?
  259 + self.path = self.calculate_path
  260 + end
  261 + end
  262 + end
  263 + end
  264 +end
  265 +
... ...
app/models/concerns/acts_as_having_boxes.rb 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +module ActsAsHavingBoxes
  2 +
  3 + module ClassMethods
  4 + def acts_as_having_boxes
  5 + has_many :boxes, -> { order :position }, as: :owner, dependent: :destroy
  6 + self.send(:include, ActsAsHavingBoxes)
  7 + end
  8 + end
  9 +
  10 + module BlockArray
  11 + def find(id)
  12 + select { |item| item.id == id.to_i }.first
  13 + end
  14 + end
  15 +
  16 + def blocks(reload = false)
  17 + if (reload)
  18 + @blocks = nil
  19 + end
  20 + if @blocks.nil?
  21 + @blocks = boxes.includes(:blocks).inject([]) do |acc,obj|
  22 + acc.concat(obj.blocks)
  23 + end
  24 + @blocks.send(:extend, BlockArray)
  25 + end
  26 + @blocks
  27 + end
  28 +
  29 + # returns 3 unless the class table has a boxes_limit column. In that case
  30 + # return the value of the column.
  31 + def boxes_limit layout_template = nil
  32 + layout_template ||= self.layout_template
  33 + @boxes_limit ||= LayoutTemplate.find(layout_template).number_of_boxes || 3
  34 + end
  35 +
  36 +end
  37 +
... ...
app/models/concerns/acts_as_having_image.rb 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +module ActsAsHavingImage
  2 +
  3 + module ClassMethods
  4 + def acts_as_having_image
  5 + belongs_to :image, dependent: :destroy
  6 + scope :with_image, -> { where "#{table_name}.image_id IS NOT NULL" }
  7 + scope :without_image, -> { where "#{table_name}.image_id IS NULL" }
  8 + attr_accessible :image_builder
  9 + include ActsAsHavingImage
  10 + end
  11 + end
  12 +
  13 + def image_builder=(img)
  14 + if image && image.id == img[:id]
  15 + image.attributes = img
  16 + else
  17 + build_image(img)
  18 + end unless img[:uploaded_data].blank?
  19 + if img[:remove_image] == 'true'
  20 + self.image_id = nil
  21 + end
  22 + end
  23 +
  24 +end
  25 +
... ...
app/models/concerns/acts_as_having_posts.rb 0 → 100644
... ... @@ -0,0 +1,49 @@
  1 +module ActsAsHavingPosts
  2 +
  3 + module ClassMethods
  4 + def acts_as_having_posts(scope = nil)
  5 + has_many :posts, -> {
  6 + s = order('published_at DESC, id DESC').where('articles.type != ?', 'RssFeed')
  7 + s = s.instance_exec(&scope) if scope
  8 + s
  9 + }, class_name: 'Article', foreign_key: 'parent_id', source: :children
  10 +
  11 + attr_accessor :feed_attrs
  12 +
  13 + after_create do |blog|
  14 + blog.children << RssFeed.new(:name => 'feed', :profile => blog.profile)
  15 + blog.feed = blog.feed_attrs
  16 + end
  17 +
  18 + settings_items :posts_per_page, :type => :integer, :default => 5
  19 +
  20 + self.send(:include, ActsAsHavingPosts)
  21 + end
  22 + end
  23 +
  24 + def has_posts?
  25 + true
  26 + end
  27 +
  28 + def feed
  29 + children.where(:type => 'RssFeed').first
  30 + end
  31 +
  32 + def feed=(attrs)
  33 + if attrs
  34 + if self.feed
  35 + self.feed.update(attrs)
  36 + else
  37 + self.feed_attrs = attrs
  38 + end
  39 + end
  40 + self.feed
  41 + end
  42 +
  43 + def name=(value)
  44 + self.set_name(value)
  45 + self.slug = self.slug.blank? ? self.name.to_slug : self.slug.to_slug
  46 + end
  47 +
  48 +end
  49 +
... ...
app/models/concerns/acts_as_having_settings.rb 0 → 100644
... ... @@ -0,0 +1,89 @@
  1 +# declare missing types
  2 +module ActiveRecord
  3 + module Type
  4 + class Symbol < Value
  5 + def cast_value value
  6 + value.to_sym
  7 + end
  8 + end
  9 + class Array < Value
  10 + def cast_value value
  11 + ::Array.wrap(value)
  12 + end
  13 + end
  14 + class Hash < Value
  15 + def cast_value value
  16 + h = ::Hash[value]
  17 + h.symbolize_keys!
  18 + h
  19 + end
  20 + end
  21 + end
  22 +end
  23 +
  24 +module ActsAsHavingSettings
  25 +
  26 + def self.type_cast value, type
  27 + # do not cast nil
  28 + return value if value.nil?
  29 + type.send :cast_value, value
  30 + end
  31 +
  32 + module ClassMethods
  33 +
  34 + def acts_as_having_settings(*args)
  35 + options = args.last.is_a?(Hash) ? args.pop : {}
  36 + field = (options[:field] || :settings).to_sym
  37 +
  38 + serialize field, Hash
  39 + class_attribute :settings_field
  40 + self.settings_field = field
  41 +
  42 + class_eval do
  43 + def settings_field
  44 + self[self.class.settings_field] ||= Hash.new
  45 + end
  46 +
  47 + def setting_changed? setting_field
  48 + setting_field = setting_field.to_sym
  49 + changed_settings = self.changes[self.class.settings_field]
  50 + return false if changed_settings.nil?
  51 +
  52 + old_setting_value = changed_settings.first.nil? ? nil : changed_settings.first[setting_field]
  53 + new_setting_value = changed_settings.last[setting_field]
  54 + old_setting_value != new_setting_value
  55 + end
  56 + end
  57 +
  58 + settings_items *args
  59 + end
  60 +
  61 + def settings_items *names
  62 +
  63 + options = names.extract_options!
  64 + default = options[:default]
  65 + type = options[:type]
  66 + type = if type.present? then ActiveRecord::Type.const_get(type.to_s.camelize.to_sym).new else nil end
  67 +
  68 + names.each do |setting|
  69 + # symbolize key
  70 + setting = setting.to_sym
  71 +
  72 + define_method setting do
  73 + h = send self.class.settings_field
  74 + val = h[setting]
  75 + # translate default value if it is used
  76 + if not val.nil? then val elsif default.is_a? String then gettext default else default end
  77 + end
  78 +
  79 + define_method "#{setting}=" do |value|
  80 + h = send self.class.settings_field
  81 + h[setting] = if type then ActsAsHavingSettings.type_cast value, type else value end
  82 + end
  83 + end
  84 + end
  85 +
  86 + end
  87 +
  88 +end
  89 +
... ...
app/models/concerns/code_numbering.rb 0 → 100644
... ... @@ -0,0 +1,57 @@
  1 +module CodeNumbering
  2 + module ClassMethods
  3 + def code_numbering field, options = {}
  4 + class_attribute :code_numbering_field
  5 + class_attribute :code_numbering_options
  6 +
  7 + self.code_numbering_field = field
  8 + self.code_numbering_options = options
  9 +
  10 + before_create :create_code_numbering
  11 +
  12 + include CodeNumbering::InstanceMethods
  13 + end
  14 + end
  15 +
  16 + module InstanceMethods
  17 +
  18 + def code
  19 + self.attributes[self.code_numbering_field.to_s]
  20 + end
  21 +
  22 + def code_scope
  23 + scope = self.code_numbering_options[:scope]
  24 + case scope
  25 + when Symbol
  26 + self.send scope
  27 + when Proc
  28 + instance_exec &scope
  29 + else
  30 + self.class
  31 + end
  32 + end
  33 +
  34 + def code_maximum
  35 + self.code_scope.maximum(self.code_numbering_field) || 0
  36 + end
  37 +
  38 + def create_code_numbering
  39 + max = self.code_numbering_options[:start].to_i - 1 if self.code_numbering_options[:start]
  40 + max = self.code_maximum
  41 + self.send "#{self.code_numbering_field}=", max+1
  42 + end
  43 +
  44 + def reset_scope_code_numbering
  45 + max = self.code_numbering_options[:start].to_i - 1 if self.code_numbering_options[:start]
  46 + max ||= 1
  47 +
  48 + self.code_scope.order(:created_at).each do |record|
  49 + record.update_column self.code_numbering_field, max
  50 + max += 1
  51 + end
  52 + self.reload
  53 + end
  54 +
  55 + end
  56 +end
  57 +
... ...
app/models/concerns/customizable.rb 0 → 100644
... ... @@ -0,0 +1,124 @@
  1 +module Customizable
  2 +
  3 + def self.included(base)
  4 + base.attr_accessible :custom_values
  5 + base.extend ClassMethods
  6 + end
  7 +
  8 + module ClassMethods
  9 + def acts_as_customizable(options = {})
  10 + attr_accessor :custom_values
  11 + has_many :custom_field_values, :dependent => :delete_all, :as => :customized
  12 + send :include, Customizable::InstanceMethods
  13 + after_save :save_custom_values
  14 + validate :valid_custom_values?
  15 + end
  16 +
  17 + def active_custom_fields environment
  18 + environment.custom_fields.select{|cf| customized_ancestors_list.include?(cf.customized_type) && cf.active}
  19 + end
  20 +
  21 + def required_custom_fields environment
  22 + environment.custom_fields.select{|cf| customized_ancestors_list.include?(cf.customized_type) && cf.required}
  23 + end
  24 +
  25 + def signup_custom_fields environment
  26 + environment.custom_fields.select{|cf| customized_ancestors_list.include?(cf.customized_type) && cf.signup}
  27 + end
  28 +
  29 + def custom_fields environment
  30 + environment.custom_fields.select{|cf| customized_ancestors_list.include?(cf.customized_type)}
  31 + end
  32 +
  33 + def customized_ancestors_list
  34 + current=self
  35 + result=[]
  36 + while current.instance_methods.include? :custom_value do
  37 + result << current.name
  38 + current=current.superclass
  39 + end
  40 + result
  41 + end
  42 +
  43 + end
  44 +
  45 + module InstanceMethods
  46 +
  47 + def valid_custom_values?
  48 + is_valid = true
  49 + parse_custom_values.each do |cv|
  50 + unless cv.valid?
  51 + name = cv.custom_field.name
  52 + errors.add(name, cv.errors.messages[name.to_sym].first)
  53 + is_valid = false
  54 + end
  55 + end
  56 + is_valid
  57 + end
  58 +
  59 + def customized_class
  60 + current=self.class
  61 + while current.instance_methods.include? :custom_fields do
  62 + result=current
  63 + current=current.superclass
  64 + end
  65 + result.name
  66 + end
  67 +
  68 + def is_public(field_name)
  69 + cv = self.custom_field_values.detect{|cv| cv.custom_field.name==field_name}
  70 + cv.nil? ? false : cv.public
  71 + end
  72 +
  73 + def public_values
  74 + self.custom_field_values.select{|cv| cv.public}
  75 + end
  76 +
  77 + def custom_value(field_name)
  78 + cv = self.custom_field_values.detect{|cv| cv.custom_field.name==field_name}
  79 + cv.nil? ? default_value_for(field_name) : cv.value
  80 + end
  81 +
  82 + def default_value_for(field_name)
  83 + field=self.class.custom_fields(environment).detect {|c| c.name == field_name}
  84 + field.nil? ? nil : field.default_value
  85 + end
  86 +
  87 + def parse_custom_values
  88 + return_list = []
  89 + return return_list if custom_values.blank?
  90 + custom_values.each_pair do |key, value|
  91 + custom_field = environment.custom_fields.detect{|cf|cf.name==key}
  92 + next if custom_field.blank?
  93 + custom_field_value = self.custom_field_values(true).detect{|cv| cv.custom_field.name==key}
  94 +
  95 + if custom_field_value.nil?
  96 + custom_field_value = CustomFieldValue.new
  97 + custom_field_value.custom_field = custom_field
  98 + custom_field_value.customized = self
  99 + end
  100 +
  101 + if value.is_a?(Hash)
  102 + custom_field_value.value = value['value'].to_s
  103 + if value.has_key?('public')
  104 + is_public = value['public']=="true" || value['public']==true
  105 + custom_field_value.public = is_public
  106 + else
  107 + custom_field_value.public = false
  108 + end
  109 + else
  110 + custom_field_value.value = value.to_s
  111 + custom_field_value.public = false
  112 + end
  113 + return_list << custom_field_value
  114 + end
  115 + return_list
  116 + end
  117 +
  118 + def save_custom_values
  119 + parse_custom_values.each(&:save)
  120 + end
  121 +
  122 + end
  123 +end
  124 +
... ...
app/models/concerns/delayed_attachment_fu.rb 0 → 100644
... ... @@ -0,0 +1,55 @@
  1 +module DelayedAttachmentFu
  2 +
  3 + module ClassMethods
  4 + def delay_attachment_fu_thumbnails
  5 + include DelayedAttachmentFu::InstanceMethods
  6 + after_create do |file|
  7 + if file.thumbnailable?
  8 + Delayed::Job.enqueue CreateThumbnailsJob.new(file.class.name, file.id)
  9 + end
  10 + end
  11 + end
  12 + end
  13 +
  14 + module InstanceMethods
  15 + # skip processing with RMagick
  16 + def process_attachment
  17 + end
  18 +
  19 + def after_process_attachment
  20 + save_to_storage
  21 + @temp_paths.clear
  22 + @saved_attachment = nil
  23 + run_callbacks :after_attachment_saved
  24 + end
  25 +
  26 + def create_thumbnails
  27 + if thumbnailable?
  28 + self.class.with_image(full_filename) do |img|
  29 + self.width = img.columns
  30 + self.height = img.rows
  31 + self.save!
  32 + end
  33 + self.class.attachment_options[:thumbnails].each do |suffix, size|
  34 + self.create_or_update_thumbnail(self.full_filename, suffix, size)
  35 + end
  36 + self.thumbnails_processed = true
  37 + self.save!
  38 + end
  39 + end
  40 +
  41 + def public_filename(size=nil)
  42 + force, size = true, nil if size == :uploaded
  43 + if !self.thumbnailable? || self.thumbnails_processed || force
  44 + super size
  45 + else
  46 + size ||= :thumb
  47 + '/images/icons-app/image-loading-%s.png' % size
  48 + end
  49 + end
  50 +
  51 +
  52 + end
  53 +end
  54 +
  55 +
... ...
app/models/concerns/set_profile_region_from_city_state.rb 0 → 100644
... ... @@ -0,0 +1,44 @@
  1 +module SetProfileRegionFromCityState
  2 +
  3 + module ClassMethods
  4 + def set_profile_region_from_city_state
  5 + before_save :region_from_city_and_state
  6 +
  7 + include InstanceMethods
  8 + alias_method_chain :city=, :region
  9 + alias_method_chain :state=, :region
  10 + end
  11 + end
  12 +
  13 + module InstanceMethods
  14 + include Noosfero::Plugin::HotSpot
  15 +
  16 + def city_with_region=(value)
  17 + self.city_without_region = value
  18 + @change_region = true
  19 + end
  20 +
  21 + def state_with_region=(value)
  22 + self.state_without_region = value
  23 + @change_region = true
  24 + end
  25 +
  26 + def region_from_city_and_state
  27 + if @change_region
  28 + self.region = nil
  29 + state = search_region(State, self.state)
  30 + self.region = search_region(City.where(:parent_id => state.id), self.city) if state
  31 + end
  32 + end
  33 +
  34 + private
  35 +
  36 + def search_region(scope, query)
  37 + return nil if !query
  38 + query = query.downcase.strip
  39 + scope.where(['lower(name)=? OR lower(abbreviation)=? OR lower(acronym)=?', query, query, query]).first
  40 + end
  41 +
  42 + end
  43 +
  44 +end
... ...
app/models/concerns/translatable_content.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +module TranslatableContent
  2 +
  3 + def translatable?
  4 + return false if self.profile && !self.profile.environment.languages.present?
  5 + parent.nil? || !parent.forum?
  6 + end
  7 +
  8 +end
... ...
app/models/concerns/white_list_filter.rb 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +module WhiteListFilter
  2 +
  3 + def check_iframe_on_content(content, trusted_sites)
  4 + if content.blank? || !content.include?('iframe')
  5 + return content
  6 + end
  7 + content.gsub!(/<iframe[^>]*>\s*<\/iframe>/i) do |iframe|
  8 + result = ''
  9 + unless iframe =~ /src=['"].*src=['"]/
  10 + trusted_sites.each do |trusted_site|
  11 + re_dom = trusted_site.gsub('.', '\.')
  12 + if iframe =~ /src=["'](https?:)?\/\/(www\.)?#{re_dom}\//
  13 + result = iframe
  14 + end
  15 + end
  16 + end
  17 + result
  18 + end
  19 + content
  20 + end
  21 +
  22 + module ClassMethods
  23 + def filter_iframes(*opts)
  24 + options = opts.last.is_a?(Hash) && opts.pop || {}
  25 + white_list_method = options[:whitelist] || :iframe_whitelist
  26 + opts.each do |field|
  27 + before_validation do |obj|
  28 + obj.check_iframe_on_content(obj.send(field), obj.send(white_list_method))
  29 + end
  30 + end
  31 + end
  32 + end
  33 +
  34 + def self.included(c)
  35 + c.send(:extend, WhiteListFilter::ClassMethods)
  36 + end
  37 +end
... ...
app/models/create_community.rb
... ... @@ -12,6 +12,7 @@ class CreateCommunity &lt; Task
12 12 attr_accessible :environment, :requestor, :target
13 13 attr_accessible :reject_explanation, :template_id
14 14  
  15 + extend ActsAsHavingImage::ClassMethods
15 16 acts_as_having_image
16 17  
17 18 DATA_FIELDS = Community.fields + ['name', 'closed', 'description']
... ...
app/models/enterprise.rb
... ... @@ -174,8 +174,4 @@ class Enterprise &lt; Organization
174 174 ''
175 175 end
176 176  
177   - def followed_by? person
178   - super or self.fans.where(id: person.id).count > 0
179   - end
180   -
181 177 end
... ...
app/models/environment.rb
... ... @@ -200,6 +200,7 @@ class Environment &lt; ApplicationRecord
200 200 # Relationships and applied behaviour
201 201 # #################################################
202 202  
  203 + extend ActsAsHavingBoxes::ClassMethods
203 204 acts_as_having_boxes
204 205  
205 206 after_create do |env|
... ... @@ -251,7 +252,8 @@ class Environment &lt; ApplicationRecord
251 252 # #################################################
252 253  
253 254 # store the Environment settings as YAML-serialized Hash.
254   - acts_as_having_settings :field => :settings
  255 + extend ActsAsHavingSettings::ClassMethods
  256 + acts_as_having_settings field: :settings
255 257  
256 258 # introduce and explain to users something about the signup
257 259 settings_items :signup_intro, :type => String
... ...
app/models/event.rb
1   -require 'noosfero/translatable_content'
2 1 require 'builder'
3 2  
4 3 class Event < Article
... ... @@ -138,7 +137,7 @@ class Event &lt; Article
138 137 false
139 138 end
140 139  
141   - include Noosfero::TranslatableContent
  140 + include TranslatableContent
142 141 include MaybeAddHttp
143 142  
144 143 end
... ...
app/models/favorite_enterprise_person.rb
... ... @@ -7,6 +7,10 @@ class FavoriteEnterprisePerson &lt; ApplicationRecord
7 7 belongs_to :enterprise
8 8 belongs_to :person
9 9  
  10 + after_create do |favorite|
  11 + favorite.person.follow(favorite.enterprise, Circle.find_or_create_by(:person => favorite.person, :name =>_('favorites'), :profile_type => 'Enterprise'))
  12 + end
  13 +
10 14 protected
11 15  
12 16 def is_trackable?
... ...
app/models/folder.rb
... ... @@ -10,7 +10,8 @@ class Folder &lt; Article
10 10 errors.add(:parent, "A folder should not belong to a blog.") if parent && parent.blog?
11 11 end
12 12  
13   - acts_as_having_settings :field => :setting
  13 + extend ActsAsHavingSettings::ClassMethods
  14 + acts_as_having_settings field: :setting
14 15  
15 16 xss_terminate :only => [ :name, :body ], :with => 'white_list', :on => 'validation'
16 17  
... ...
app/models/forum.rb
1 1 class Forum < Folder
2 2  
  3 + extend ActsAsHavingPosts::ClassMethods
3 4 acts_as_having_posts -> { reorder 'updated_at DESC' }
  5 +
4 6 include PostsLimit
5 7  
6 8 attr_accessible :has_terms_of_use, :terms_of_use, :topic_creation
... ...
app/models/friendship.rb
... ... @@ -9,11 +9,19 @@ class Friendship &lt; ApplicationRecord
9 9 after_create do |friendship|
10 10 Friendship.update_cache_counter(:friends_count, friendship.person, 1)
11 11 Friendship.update_cache_counter(:friends_count, friendship.friend, 1)
  12 +
  13 + circles = friendship.group.blank? ? ['friendships'] : friendship.group.split(',').map(&:strip)
  14 + circles.each do |circle|
  15 + friendship.person.follow(friendship.friend, Circle.find_or_create_by(:person => friendship.person, :name => circle, :profile_type => 'Person'))
  16 + end
12 17 end
13 18  
14 19 after_destroy do |friendship|
15 20 Friendship.update_cache_counter(:friends_count, friendship.person, -1)
16 21 Friendship.update_cache_counter(:friends_count, friendship.friend, -1)
  22 +
  23 + circle = Circle.find_by(:person => friendship.person, :name => (friendship.group.blank? ? 'friendships': friendship.group) )
  24 + friendship.person.remove_profile_from_circle(friendship.friend, circle) if circle
17 25 end
18 26  
19 27 def self.remove_friendship(person1, person2)
... ...
app/models/image.rb
... ... @@ -23,6 +23,7 @@ class Image &lt; ApplicationRecord
23 23  
24 24 validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n
25 25  
  26 + extend DelayedAttachmentFu::ClassMethods
26 27 delay_attachment_fu_thumbnails
27 28  
28 29 postgresql_attachment_fu
... ...
app/models/organization.rb
... ... @@ -2,6 +2,10 @@
2 2 class Organization < Profile
3 3  
4 4 attr_accessible :moderated_articles, :foundation_year, :contact_person, :acronym, :legal_form, :economic_activity, :management_information, :cnpj, :display_name, :enable_contact_us
  5 + attr_accessible :requires_email
  6 +
  7 + settings_items :requires_email, type: :boolean
  8 + alias_method :requires_email?, :requires_email
5 9  
6 10 SEARCH_FILTERS = {
7 11 :order => %w[more_recent more_popular more_active],
... ...
app/models/person.rb
... ... @@ -8,7 +8,6 @@ class Person &lt; Profile
8 8 :display => %w[compact]
9 9 }
10 10  
11   -
12 11 def self.type_name
13 12 _('Person')
14 13 end
... ... @@ -93,6 +92,7 @@ class Person &lt; Profile
93 92 has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article
94 93 has_many :friendships, :dependent => :destroy
95 94 has_many :friends, :class_name => 'Person', :through => :friendships
  95 + has_many :circles
96 96  
97 97 scope :online, -> {
98 98 joins(:user).where("users.chat_status != '' AND users.chat_status_at >= ?", DateTime.now - User.expires_chat_status_every.minutes)
... ... @@ -200,6 +200,33 @@ class Person &lt; Profile
200 200 end
201 201 end
202 202  
  203 + def follow(profile, circles)
  204 + circles = [circles] unless circles.is_a?(Array)
  205 + circles.each do |new_circle|
  206 + ProfileFollower.create(profile: profile, circle: new_circle)
  207 + end
  208 + end
  209 +
  210 + def update_profile_circles(profile, new_circles)
  211 + profile_circles = ProfileFollower.with_profile(profile).with_follower(self).map(&:circle)
  212 + circles_to_add = new_circles - profile_circles
  213 + circles_to_remove = profile_circles - new_circles
  214 + circles_to_add.each do |new_circle|
  215 + ProfileFollower.create(profile: profile, circle: new_circle)
  216 + end
  217 +
  218 + ProfileFollower.where('circle_id IN (?) AND profile_id = ?',
  219 + circles_to_remove.map(&:id), profile.id).destroy_all
  220 + end
  221 +
  222 + def unfollow(profile)
  223 + ProfileFollower.with_follower(self).with_profile(profile).destroy_all
  224 + end
  225 +
  226 + def remove_profile_from_circle(profile, circle)
  227 + ProfileFollower.with_profile(profile).with_circle(circle).destroy_all
  228 + end
  229 +
203 230 def already_request_friendship?(person)
204 231 person.tasks.where(requestor_id: self.id, type: 'AddFriend', status: Task::Status::ACTIVE).first
205 232 end
... ... @@ -580,9 +607,12 @@ class Person &lt; Profile
580 607 person.has_permission?(:manage_friends, self)
581 608 end
582 609  
583   - protected
  610 + def followed_profiles
  611 + Profile.followed_by self
  612 + end
584 613  
585   - def followed_by?(profile)
586   - self == profile || self.is_a_friend?(profile)
  614 + def in_social_circle?(person)
  615 + self.is_a_friend?(person) || super
587 616 end
  617 +
588 618 end
... ...
app/models/profile.rb
... ... @@ -5,7 +5,7 @@ class Profile &lt; ApplicationRecord
5 5  
6 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 7 :redirection_after_login, :custom_url_redirection,
8   - :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification
  8 + :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification, :allow_followers
9 9  
10 10 # use for internationalizable human type names in search facets
11 11 # reimplement on subclasses
... ... @@ -88,6 +88,8 @@ class Profile &lt; ApplicationRecord
88 88 }
89 89  
90 90 acts_as_accessible
  91 +
  92 + include Customizable
91 93 acts_as_customizable
92 94  
93 95 include Noosfero::Plugin::HotSpot
... ... @@ -185,6 +187,21 @@ class Profile &lt; ApplicationRecord
185 187 Person.members_of(self).by_role(roles)
186 188 end
187 189  
  190 + extend ActsAsHavingSettings::ClassMethods
  191 + acts_as_having_settings field: :data
  192 +
  193 + def settings
  194 + data
  195 + end
  196 +
  197 + settings_items :redirect_l10n, :type => :boolean, :default => false
  198 + settings_items :public_content, :type => :boolean, :default => true
  199 + settings_items :description
  200 + settings_items :fields_privacy, :type => :hash, :default => {}
  201 + settings_items :email_suggestions, :type => :boolean, :default => false
  202 + settings_items :profile_admin_mail_notification, :type => :boolean, :default => true
  203 +
  204 + extend ActsAsHavingBoxes::ClassMethods
188 205 acts_as_having_boxes
189 206  
190 207 acts_as_taggable
... ... @@ -203,6 +220,23 @@ class Profile &lt; ApplicationRecord
203 220 scope :more_active, -> { order 'activities_count DESC' }
204 221 scope :more_recent, -> { order "created_at DESC" }
205 222  
  223 + scope :followed_by, -> person{
  224 + distinct.select('profiles.*').
  225 + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id').
  226 + joins('left join circles ON circles.id = profiles_circles.circle_id').
  227 + where('circles.person_id = ?', person.id)
  228 + }
  229 +
  230 + scope :in_circle, -> circle{
  231 + distinct.select('profiles.*').
  232 + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id').
  233 + joins('left join circles ON circles.id = profiles_circles.circle_id').
  234 + where('circles.id = ?', circle.id)
  235 + }
  236 +
  237 + settings_items :allow_followers, :type => :boolean, :default => true
  238 + alias_method :allow_followers?, :allow_followers
  239 +
206 240 acts_as_trackable :dependent => :destroy
207 241  
208 242 has_many :profile_activities
... ... @@ -215,6 +249,9 @@ class Profile &lt; ApplicationRecord
215 249  
216 250 has_many :email_templates, :foreign_key => :owner_id
217 251  
  252 + has_many :profile_followers
  253 + has_many :followers, -> { uniq }, :class_name => 'Person', :through => :profile_followers, :source => :person
  254 +
218 255 # Although this should be a has_one relation, there are no non-silly names for
219 256 # a foreign key on article to reference the template to which it is
220 257 # welcome_page... =P
... ... @@ -231,19 +268,6 @@ class Profile &lt; ApplicationRecord
231 268 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap)
232 269 end
233 270  
234   - acts_as_having_settings :field => :data
235   -
236   - def settings
237   - data
238   - end
239   -
240   - settings_items :redirect_l10n, :type => :boolean, :default => false
241   - settings_items :public_content, :type => :boolean, :default => true
242   - settings_items :description
243   - settings_items :fields_privacy, :type => :hash, :default => {}
244   - settings_items :email_suggestions, :type => :boolean, :default => false
245   - settings_items :profile_admin_mail_notification, :type => :boolean, :default => true
246   -
247 271 validates_length_of :description, :maximum => 550, :allow_nil => true
248 272  
249 273 # Valid identifiers must match this format.
... ... @@ -285,6 +309,7 @@ class Profile &lt; ApplicationRecord
285 309  
286 310 has_many :files, :class_name => 'UploadedFile'
287 311  
  312 + extend ActsAsHavingImage::ClassMethods
288 313 acts_as_having_image
289 314  
290 315 has_many :tasks, :dependent => :destroy, :as => 'target'
... ... @@ -758,12 +783,13 @@ private :generate_url, :url_options
758 783  
759 784 # Adds a person as member of this Profile.
760 785 def add_member(person, attributes={})
761   - if self.has_members?
  786 + if self.has_members? && !self.secret
762 787 if self.closed? && members.count > 0
763 788 AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person)
764 789 else
765 790 self.affiliate(person, Profile::Roles.admin(environment.id), attributes) if members.count == 0
766 791 self.affiliate(person, Profile::Roles.member(environment.id), attributes)
  792 + person.follow(self, Circle.find_or_create_by(:person => person, :name =>_('memberships'), :profile_type => 'Community'))
767 793 end
768 794 person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel }
769 795 remove_from_suggestion_list person
... ... @@ -1107,7 +1133,11 @@ private :generate_url, :url_options
1107 1133 end
1108 1134  
1109 1135 def followed_by?(person)
1110   - person.is_member_of?(self)
  1136 + (person == self) || (person.in? self.followers)
  1137 + end
  1138 +
  1139 + def in_social_circle?(person)
  1140 + (person == self) || (person.is_member_of?(self))
1111 1141 end
1112 1142  
1113 1143 def display_private_info_to?(user)
... ... @@ -1148,4 +1178,8 @@ private :generate_url, :url_options
1148 1178 def allow_destroy?(person = nil)
1149 1179 person.kind_of?(Profile) && person.has_permission?('destroy_profile', self)
1150 1180 end
  1181 +
  1182 + def in_circle?(circle, follower)
  1183 + ProfileFollower.with_follower(follower).with_circle(circle).with_profile(self).present?
  1184 + end
1151 1185 end
... ...
app/models/profile_follower.rb 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +class ProfileFollower < ApplicationRecord
  2 + self.table_name = :profiles_circles
  3 + track_actions :new_follower, :after_create, :keep_params => ["follower.name", "follower.url", "follower.profile_custom_icon"], :custom_user => :profile
  4 +
  5 + attr_accessible :profile, :circle
  6 +
  7 + belongs_to :profile
  8 + belongs_to :circle
  9 +
  10 + has_one :person, through: :circle
  11 + alias follower person
  12 +
  13 + validates_presence_of :profile_id, :circle_id
  14 + validates :profile_id, :uniqueness => {:scope => :circle_id, :message => "can't put a profile in the same circle twice"}
  15 +
  16 + scope :with_follower, -> person{
  17 + joins(:circle).where('circles.person_id = ?', person.id)
  18 + }
  19 +
  20 + scope :with_profile, -> profile{
  21 + where(:profile => profile)
  22 + }
  23 +
  24 + scope :with_circle, -> circle{
  25 + where(:circle => circle)
  26 + }
  27 +
  28 +end
... ...
app/models/profile_suggestion.rb
... ... @@ -17,7 +17,8 @@ class ProfileSuggestion &lt; ApplicationRecord
17 17 self.class.generate_profile_suggestions(profile_suggestion.person)
18 18 end
19 19  
20   - acts_as_having_settings :field => :categories
  20 + extend ActsAsHavingSettings::ClassMethods
  21 + acts_as_having_settings field: :categories
21 22  
22 23 validate :must_be_a_valid_category, :on => :create
23 24 def must_be_a_valid_category
... ...
app/models/task.rb
... ... @@ -11,7 +11,8 @@
11 11 # will need to declare <ttserialize</tt> itself).
12 12 class Task < ApplicationRecord
13 13  
14   - acts_as_having_settings :field => :data
  14 + extend ActsAsHavingSettings::ClassMethods
  15 + acts_as_having_settings field: :data
15 16  
16 17 module Status
17 18 # the status of tasks just created
... ...
app/models/text_article.rb
1   -require 'noosfero/translatable_content'
2   -
3 1 # a base class for all text article types.
4 2 class TextArticle < Article
5 3  
... ... @@ -9,7 +7,7 @@ class TextArticle &lt; Article
9 7 _('Article')
10 8 end
11 9  
12   - include Noosfero::TranslatableContent
  10 + include TranslatableContent
13 11  
14 12 def self.icon_name(article = nil)
15 13 if article && !article.parent.nil? && article.parent.kind_of?(Blog)
... ...
app/models/tiny_mce_article.rb
1   -require 'white_list_filter'
2   -
3 1 class TinyMceArticle < TextArticle
4 2  
5 3 def self.short_description
... ...
app/models/uploaded_file.rb
... ... @@ -84,6 +84,7 @@ class UploadedFile &lt; Article
84 84  
85 85 validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of %{size}").sub('%{size}', self.max_size.to_humanreadable).fix_i18n
86 86  
  87 + extend DelayedAttachmentFu::ClassMethods
87 88 delay_attachment_fu_thumbnails
88 89  
89 90 postgresql_attachment_fu
... ...
app/views/account/activation_question.html.erb
... ... @@ -3,7 +3,7 @@
3 3 var answer = parseInt(form.answer.value);
4 4 var val = form.answer.value;
5 5 if (!answer || (val.length != 4) || val > <%= Time.now.year %> || val < 1900) {
6   - alert(<%= (_('The year must be between %d and %d') % [1900, Time.now.year]).inspect %>);
  6 + alert(<%= (_('The year must be between %d and %d').html_safe % [1900, Time.now.year]).inspect %>);
7 7 return false;
8 8 } else {
9 9 return true;
... ... @@ -28,9 +28,9 @@
28 28  
29 29 <p> <strong><%= _('Pay atention! You have only one chance!') %></strong> </p>
30 30  
31   - <p><%= _("This is a question to know if you really are part of this enterprise. Pay atention because you have only one chance to answer right and activate your enterprise. If you answer wrong you will not be able to activate the enterprise automaticaly and must get in touch with the admins of %s by email or phone.") % environment.name %> </p>
  31 + <p><%= _("This is a question to know if you really are part of this enterprise. Pay atention because you have only one chance to answer right and activate your enterprise. If you answer wrong you will not be able to activate the enterprise automaticaly and must get in touch with the admins of %s by email or phone.").html_safe % environment.name %> </p>
32 32  
33   - <%= ApplicationHelper::NoosferoFormBuilder::output_field(@question == :foundation_year ? (_("What year your enterprise was founded? It must have 4 digits, eg 1990. %s") % environment.tip_message_enterprise_activation_question) : _('What is the CNPJ of your enterprise?'), text_field_tag(:answer, nil, :id => 'enterprise-activation-answer')) %>
  33 + <%= ApplicationHelper::NoosferoFormBuilder::output_field(@question == :foundation_year ? (_("What year your enterprise was founded? It must have 4 digits, eg 1990. %s").html_safe % environment.tip_message_enterprise_activation_question) : _('What is the CNPJ of your enterprise?'), text_field_tag(:answer, nil, :id => 'enterprise-activation-answer')) %>
34 34  
35 35 <%= hidden_field_tag :enterprise_code, params[:enterprise_code] %>
36 36  
... ...
app/views/admin_panel/_signup_welcome_text.html.erb
1 1 <div class='description'>
2 2 <%= _('This text will be sent to new users if the feature "Send welcome e-mail to new users" is enabled on environment.') %><br/><br/>
3   - <%= _('Including %s on body, it will be replaced by the real name of the e-mail recipient.') % content_tag('code', '{user_name}') %>
  3 + <%= _('Including %s on body, it will be replaced by the real name of the e-mail recipient.').html_safe % content_tag('code', '{user_name}') %>
4 4 </div>
5 5  
6 6 <%= labelled_form_field(_('Subject'), text_field(:environment, :signup_welcome_text_subject, :style => 'width:100%')) %>
... ...
app/views/blocks/profile_info_actions/_circles.html.erb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<div class="circles" id='circles-list'>
  2 +
  3 + <%= form_for :circles, :url => {:controller => 'profile', :action => 'follow'}, :html => {:id => "follow-circles-form"} do |f|%>
  4 + <%= render partial: "blocks/profile_info_actions/select_circles", :locals => {:circles => circles, :followed_profile => profile, :follower => current_person } %>
  5 +
  6 + <div id="circle-actions">
  7 + <%= submit_button :ok, _("Follow") %>
  8 + <input type="button" value="<%= _("Cancel") %>" id="cancel-set-circle" class="button with-text icon-cancel"/>
  9 + </div>
  10 + <% end %>
  11 +</div>
... ...
app/views/blocks/profile_info_actions/_common.html.erb
1 1 <li><%= report_abuse(profile, :button) %></li>
  2 +<% if logged_in? && (user != profile) && profile.allow_followers? %>
  3 + <li>
  4 + <% follow = user.follows?(profile) %>
  5 + <%= button(:unfollow, content_tag('span', _('Unfollow')), {:profile => profile.identifier, :controller => 'profile', :action => 'unfollow'}, :method => :post, :id => 'action-unfollow', :title => _("Unfollow"), :style => follow ? "" : "display: none;") %>
  6 + <%= button(:ok, content_tag('span', _('Follow')), {:profile => profile.identifier, :controller => 'profile', :action => 'find_profile_circles'}, :id => 'action-follow', :title => _("Follow"), :style => follow ? "display: none;" : "") %>
  7 + <div id="circles-container" style="display: none;">
  8 + </div>
  9 + </li>
  10 +<% end %>
2 11 <%= render_environment_features(:profile_actions) %>
... ...
app/views/blocks/profile_info_actions/_select_circles.html.erb 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +<div class="circles" id='circles-list'>
  2 + <p><%= _("Select the circles for %s") % followed_profile.name %></p>
  3 + <div id="circles-checkboxes">
  4 + <% circles.each do |circle| %>
  5 + <div class="circle">
  6 + <%= labelled_check_box circle.name, "circles[#{circle.name}]", circle.id, followed_profile.in_circle?(circle, follower) %>
  7 + </div>
  8 + <% end %>
  9 + </div>
  10 +
  11 + <%= button(:add, _('New Circle'), '#', :id => "new-circle") %>
  12 +
  13 + <div id="new-circle-form" style="display: none;">
  14 + <%= labelled_text_field _('Circle name') , 'circle[name]', "",:id => 'text-field-name-new-circle'%>
  15 + <%= hidden_field_tag('circle[profile_type]', followed_profile.class.name) %>
  16 +
  17 + <%= button_bar do %>
  18 + <%= button(:save, _('Create'), {:profile => follower.identifier, :controller => 'circles', :action => 'xhr_create'}, :id => "new-circle-submit") %>
  19 + <%= button(:cancel, _('Cancel'), '#', :id => "new-circle-cancel") %>
  20 + <% end %>
  21 + </div>
  22 +</div>
... ...
app/views/circles/_circle_checkbox.html.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<div class="circle">
  2 + <%= labelled_check_box circle.name, "circles[#{circle.name}]", circle.id %>
  3 +</div>
... ...
app/views/circles/_form.html.erb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +<%= error_messages_for :circle %>
  2 +
  3 +<%= labelled_form_for :circle, :url => (mode == :edit) ? {:action => 'update', :id => circle} : {:action => 'create'} do |f| %>
  4 +
  5 + <%= required_fields_message %>
  6 +
  7 + <%= required f.text_field(:name) %>
  8 +
  9 + <%= required labelled_form_field _("Profile type"), f.select(:profile_type, Circle.profile_types.to_a) %>
  10 +
  11 + <%= button_bar do %>
  12 + <%= submit_button('save', (mode == :edit) ? _('Save changes') : _('Create circle'), :cancel => {:action => 'index'} ) %>
  13 + <% end %>
  14 +<% end %>
... ...
app/views/circles/edit.html.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h1><%= _("Edit circle") %></h1>
  2 +
  3 +<%= render :partial => 'form', :locals => { :mode => :edit, :circle => @circle, :profile_types => @profile_types } %>
... ...
app/views/circles/index.html.erb 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +<h1><%= _('Manage circles') %></h1>
  2 +
  3 +<table>
  4 + <tr>
  5 + <th><%= _('Circle name') %></th>
  6 + <th><%= _('Profile type') %></th>
  7 + <th><%= _('Actions') %></th>
  8 + </tr>
  9 + <% @circles.each do |circle| %>
  10 + <tr>
  11 + <td>
  12 + <%= circle.name %>
  13 + </td>
  14 + <td>
  15 + <%= _(circle.profile_type) %>
  16 + </td>
  17 + <td>
  18 + <div style="text-align: center;">
  19 + <%= button_without_text :edit, _('Edit'), :action => 'edit', :id => circle %>
  20 + <%= button_without_text :delete, _('Delete'), { :action => 'destroy', :id => circle }, { "data-method" => "POST" } %>
  21 + </div>
  22 + </td>
  23 + </tr>
  24 + <% end %>
  25 +</table>
  26 +
  27 +<%= button_bar do %>
  28 + <%= button :add, _('Create a new circle'), :action => 'new' %>
  29 + <%= button :back, _('Back to control panel'), :controller => 'profile_editor' %>
  30 +<% end %>
... ...
app/views/circles/new.html.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h1><%= _("New circle") %></h1>
  2 +
  3 +<%= render :partial => 'form', :locals => { :mode => :new, :circle => @circle, :profile_types => @profile_types } %>
... ...
app/views/cms/_blog.html.erb
... ... @@ -58,7 +58,7 @@
58 58 <div id="blog-image-builder">
59 59 <%= f.fields_for :image_builder, @article.image do |i| %>
60 60 <%= file_field_or_thumbnail(_('Cover image:'), @article.image, i)%>
61   - <%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %>
  61 + <%= _("Max size: %s (.jpg, .gif, .png)").html_safe % Image.max_size.to_humanreadable %>
62 62 <% end %>
63 63 </div>
64 64  
... ...
app/views/enterprise_validation/view_processed.html.erb
1   -<h2><%= _('Processed validation request for %s ') % @processed.name %> (<%= status(@processed) %>)</h2>
  1 +<h2><%= _('Processed validation request for %s ').html_safe % @processed.name %> (<%= status(@processed) %>)</h2>
2 2  
3 3 <%= link_to _('Back'), :action => 'index' %>
4 4  
... ...
app/views/favorite_enterprises/add.html.erb
1   -<h1><%= _('Adding %s as a favorite enterprise') % @favorite_enterprise.name %></h1>
  1 +<h1><%= _('Adding %s as a favorite enterprise').html_safe % @favorite_enterprise.name %></h1>
2 2  
3 3 <p>
4   -<%= _('Are you sure you want to add %s as your favorite enterprise?') % @favorite_enterprise.name %>
  4 +<%= _('Are you sure you want to add %s as your favorite enterprise?').html_safe % @favorite_enterprise.name %>
5 5 </p>
6 6  
7 7 <%= form_tag do %>
8 8 <%= hidden_field_tag(:confirmation, 1) %>
9 9  
10   - <%= submit_button(:ok, _("Yes, I am sure"), :title => _("I want to add %s as a favorite enterprise") % @favorite_enterprise.name) %>
  10 + <%= submit_button(:ok, _("Yes, I am sure"), :title => _("I want to add %s as a favorite enterprise").html_safe % @favorite_enterprise.name) %>
11 11 <%= button(:cancel, _("No, I don't want"), :action => 'index') %>
12 12 <% end %>
... ...
app/views/file_presenter/_image.html.erb
... ... @@ -4,15 +4,15 @@
4 4 current_index = images.index(image.encapsulated_file)
5 5 total_of_images = images.count
6 6 link_to_previous = if current_index >= 1
7   - link_to(_('&laquo; Previous'), images[current_index - 1].view_url, :class => 'previous')
  7 + link_to(_('&laquo; Previous').html_safe, images[current_index - 1].view_url, :class => 'previous')
8 8 else
9   - content_tag('span', _('&laquo; Previous'), :class => 'previous')
  9 + content_tag('span', _('&laquo; Previous').html_safe, :class => 'previous')
10 10 end
11 11  
12 12 link_to_next = if current_index < total_of_images - 1
13   - link_to(_('Next &raquo;'), images[current_index + 1].view_url, :class => 'next')
  13 + link_to(_('Next &raquo;').html_safe, images[current_index + 1].view_url, :class => 'next')
14 14 else
15   - content_tag('span', _('Next &raquo;'), :class => 'next')
  15 + content_tag('span', _('Next &raquo;').html_safe, :class => 'next')
16 16 end
17 17 %>
18 18  
... ...
app/views/followers/_edit_circles_modal.html.erb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +<div class="circles" id='circles-list'>
  2 + <%= form_for :circles, :url => {:controller => 'followers', :action => 'update_category'}, :html => {:id => "follow-circles-form"} do |f|%>
  3 + <%= render partial: "blocks/profile_info_actions/select_circles", :locals => {:circles => circles, :followed_profile => followed_profile, :follower => profile } %>
  4 +
  5 + <%= hidden_field_tag('followed_profile_id', followed_profile.id) %>
  6 +
  7 + <div id="circle-actions">
  8 + <div id="actions-container">
  9 + <%= submit_button('save', _('Save')) %>
  10 + <%= modal_close_button _("Cancel") %>
  11 + </div>
  12 + </div>
  13 + <% end %>
  14 +</div>
... ...
app/views/followers/_profile_list.html.erb 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +<ul class="profile-list">
  2 + <% profiles.each do |followed_profile| %>
  3 + <li>
  4 + <%= link_to_profile profile_image(followed_profile) + tag('br') + followed_profile.short_name,
  5 + followed_profile.identifier, :class => 'profile-link' %>
  6 + <div class="controll">
  7 + <%= button_without_text :remove, content_tag('span',_('unfollow')),
  8 + { :controller => "profile", :profile => followed_profile.identifier, :follower_id => profile.id,
  9 + :action => 'unfollow', :redirect_to => url_for({:controller => "followers", :profile => profile.identifier}) },
  10 + :method => :post, :title => _('remove') %>
  11 + <%= modal_icon_button :edit, _('change category'),
  12 + url_for(:controller => 'followers', :action => 'set_category_modal',
  13 + :followed_profile_id => followed_profile.id) %>
  14 + </div><!-- end class="controll" -->
  15 + </li>
  16 + <% end %>
  17 +</ul>
... ...
app/views/followers/index.html.erb 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +<div id="manage_followed people">
  2 +
  3 +<h1><%= _("%s is following") % profile.name %></h1>
  4 +
  5 +<% cache_timeout(profile.manage_friends_cache_key(params), 4.hours) do %>
  6 + <% if @followed_people.empty? %>
  7 + <p>
  8 + <em>
  9 + <%= _("You don't follow anybody yet.") %>
  10 + </em>
  11 + </p>
  12 + <% end %>
  13 +
  14 + <%= button_bar do %>
  15 + <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %>
  16 + <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %>
  17 + <% end %>
  18 +
  19 + <%= labelled_select(_('Profile type')+': ', :filter_profile_type, :last, :first, @active_filter, @profile_types, :id => "profile-type-filter") %>
  20 +
  21 + <%= render :partial => 'profile_list', :locals => { :profiles => @followed_people } %>
  22 +
  23 + <br style="clear:both" />
  24 + <%= pagination_links @followed_people, :param_name => 'npage' %>
  25 +<% end %>
  26 +
  27 +</div>
... ...
app/views/friends/remove.html.erb
1 1 <div id="remove_friend">
2 2  
3   -<h1><%= _('Removing friend: %s') % @friend.name %></h1>
  3 +<h1><%= _('Removing friend: %s').html_safe % @friend.name %></h1>
4 4  
5 5 <%= profile_image @friend, :thumb, :class => 'friend_picture' %>
6 6  
7 7 <p>
8   -<%= _('Are you sure you want to remove %s from your friends list?') % @friend.name %>
  8 +<%= _('Are you sure you want to remove %s from your friends list?').html_safe % @friend.name %>
9 9 </p>
10 10  
11 11 <p>
12 12 <em>
13   -<%= _('Note that %s will still have you as a friend, unless he/she also wants to remove you from his/her friend list.') % @friend.name %>
  13 +<%= _('Note that %s will still have you as a friend, unless he/she also wants to remove you from his/her friend list.').html_safe % @friend.name %>
14 14 </em>
15 15 </p>
16 16  
... ...
app/views/home/welcome.html.erb
1 1 <% default_message = defined?(default_message) ? default_message : false %>
2 2  
3 3 <div id='thanks-for-signing'>
4   - <h1><%= _("Welcome to %s!") % environment.name %></h1>
  4 + <h1><%= _("Welcome to %s!").html_safe % environment.name %></h1>
5 5 <% if environment.has_custom_welcome_screen? && !default_message %>
6 6 <%= environment.settings[:signup_welcome_screen_body].html_safe %>
7 7 <% else %>
... ... @@ -10,21 +10,21 @@
10 10 <p><%= _("Firstly, some tips for getting started:") %></p>
11 11 <h4><%= _("Confirm your account!") %></h4>
12 12 <p><%= _("You should receive a welcome email from us shortly. Please take a second to follow the link within to confirm your account.") %></p>
13   - <p><%= _("You won't appear as %s until your account is confirmed.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>
  13 + <p><%= _("You won't appear as %s until your account is confirmed.").html_safe % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>
14 14 <% else %>
15 15 <h4><%= _("Wait for admin approvement!") %></h4>
16 16 <p><%= _("The administrators will evaluate your signup request for approvement.") %></p>
17   - <p><%= _("You won't appear as %s until your account is approved.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>
  17 + <p><%= _("You won't appear as %s until your account is approved.").html_safe % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>
18 18 <% end %>
19 19 <h4><%= _("What to do next?") %></h4>
20   - <p><%= _("Access your %s and see your face on the network!") %
  20 + <p><%= _("Access your %s and see your face on the network!").html_safe %
21 21 (user.present? ? link_to(_('Profile'), {:controller => 'profile', :profile => user.identifier}, :target => '_blank') : 'Profile') %>
22   - <%= _("You can also explore your %s to customize your profile. Here are some %s on what you can do there.") %
  22 + <%= _("You can also explore your %s to customize your profile. Here are some %s on what you can do there.").html_safe %
23 23 [user.present? ? link_to(_('Control Panel'), {:controller => 'profile_editor', :profile => user.identifier}, :target => '_blank') : 'Control Panel',
24 24 link_to(_('tips'), {:controller => 'doc', :action => 'topic', :section => 'user', :topic => 'editing-person-info'}, :target => '_blank')] %></p>
25   - <p><%= _("%s your Gmail, Yahoo and Hotmail contacts!") % link_to(_('Invite and find'), {:controller => 'doc', :action => 'topic', :section => 'user', :topic => 'invite-contacts'}, :target => '_blank') %></p>
26   - <p><%= _("Learn the guidelines. Read the %s for more details on how to use this social network!") % link_to(_('Documentation'), {:controller => 'doc'}, :target => '_blank') %></p>
  25 + <p><%= _("%s your Gmail, Yahoo and Hotmail contacts!").html_safe % link_to(_('Invite and find'), {:controller => 'doc', :action => 'topic', :section => 'user', :topic => 'invite-contacts'}, :target => '_blank') %></p>
  26 + <p><%= _("Learn the guidelines. Read the %s for more details on how to use this social network!").html_safe % link_to(_('Documentation'), {:controller => 'doc'}, :target => '_blank') %></p>
27 27 <p><%= _("Start exploring and have fun!") %></p>
28 28 <% end %>
29   - <%= render :partial => 'shared/template_welcome_page', :locals => {:template => @person_template, :header => _("What can I do as a %s?")} %>
  29 + <%= render :partial => 'shared/template_welcome_page', :locals => {:template => @person_template, :header => _("What can I do as a %s?").html_safe} %>
30 30 </div>
... ...
app/views/person_notifier/mailer/_create_article.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <td>
6 6 <p>
7 7 <span style="font-size: 14px;"><%= link_to activity.user.short_name(20), activity.user.url %></span>
8   - <span style="font-size: 14px;"><%= _("has published on community %s") % link_to(activity.target.profile.short_name(20), activity.target.profile.url, :style => "color: #333; font-weight: bold; text-decoration: none;") if activity.target.profile.is_a?(Community) %></span>
  8 + <span style="font-size: 14px;"><%= _("has published on community %s").html_safe % link_to(activity.target.profile.short_name(20), activity.target.profile.url, :style => "color: #333; font-weight: bold; text-decoration: none;") if activity.target.profile.is_a?(Community) %></span>
9 9 <span style="font-size: 10px; color: #929292; float:right;"><%= time_ago_in_words(activity.created_at) %></span>
10 10 </p>
11 11 <p>
... ...
app/views/person_notifier/mailer/_new_follower.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= render :partial => 'default_activity', :locals => { :activity => activity } %>
... ...
app/views/person_notifier/mailer/_profile_comments.html.erb
1 1 <% if activity.comments_count > 2 %>
2 2 <div style="font-size: 10px;">
3 3 <% if activity.params['url'].blank? %>
4   - <%= _("%s comments") % activity.comments_count %>
  4 + <%= _("%s comments").html_safe % activity.comments_count %>
5 5 <% else %>
6   - <%= link_to(_("View all %s comments") % activity.comments_count, activity.params['url']) %>
  6 + <%= link_to(_("View all %s comments").html_safe % activity.comments_count, activity.params['url']) %>
7 7 <% end %>
8 8 </div>
9 9 <% else %>
... ...
app/views/person_notifier/mailer/content_summary.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <%= link_to @url, :style => "text-decoration: none;" do %>
6 6 <span style="font-weight:bold;font-size: 28px;margin: 0;color: white;background-color: #AAAAAA;padding: 5px;"><%= @environment.name %></span>
7 7 <% end %>
8   - <span style="font-weight:bold;color: #333;font-size:19px;margin-left: 8px;"><%= _("%s's Notifications") % @profile.name %></h3>
  8 + <span style="font-weight:bold;color: #333;font-size:19px;margin-left: 8px;"><%= _("%s's Notifications").html_safe % @profile.name %></h3>
9 9 </div>
10 10 <div style="margin: 0 20px 20px 20px;border-top:1px solid #e2e2e2;">
11 11 <% if @tasks.present? %>
... ... @@ -34,7 +34,7 @@
34 34  
35 35 <div style="color:#444444;font-size:11px;margin-bottom: 20px;">
36 36 <p style="margin:0"><%= _("Greetings,") %></p>
37   - <p style="margin:0"><%= _('%s team.') % @environment.name %></p>
  37 + <p style="margin:0"><%= _('%s team.').html_safe % @environment.name %></p>
38 38 <p style="margin:0"><%= link_to @url, url_for(@url) %></p>
39 39 </div>
40 40 </div>
... ...
app/views/profile/_follow.html.erb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +<% cache_timeout(profile.friends_cache_key(params), 4.hours) do %>
  2 + <ul class='profile-list'>
  3 + <% follow.each do |follower| %>
  4 + <%= profile_image_link(follower) %>
  5 + <% end%>
  6 + </ul>
  7 +
  8 + <div id='pagination-profiles'>
  9 + <%= pagination_links follow, :param_name => 'npage' %>
  10 + </div>
  11 +<% end %>
  12 +
  13 +<%= button_bar do %>
  14 + <%= button :back, _('Go back'), { :controller => 'profile' } %>
  15 + <% if user == profile %>
  16 + <%= button :edit, _('Manage followed people'), :controller => 'friends', :action => 'index', :profile => profile.identifier %>
  17 + <% end %>
  18 +<% end %>
... ...
app/views/profile/_new_follower.html.erb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= render :partial => 'default_activity', :locals => { :activity => activity, :tab_action => tab_action } %>
... ...
app/views/profile/_private_profile.html.erb
... ... @@ -13,5 +13,5 @@
13 13 <%= button(:add, content_tag('span', _('Add friend')), profile.add_url, :class => 'add-friend', :title => _("Add friend"), :style => 'position: relative;') %>
14 14 <% end %>
15 15 <%= button :back, _('Go back'), :back %>
16   - <%= button :home, _("Go to %s home page") % environment.name, :controller => 'home' %>
  16 + <%= button :home, _("Go to %s home page").html_safe % environment.name, :controller => 'home' %>
17 17 <% end %>
... ...
app/views/profile/_profile_comments.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <% if activity.comments_count > 0 %>
6 6 <div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments" >
7 7 <div class='view-all-comments icon-chat'>
8   - <%= link_to(n_('View comment', "View all %s comments", activity.comments_count) % activity.comments_count, :profile => profile.identifier, :controller => 'profile', :action => 'more_comments', :activity => activity, :comment_page => (1)) %>
  8 + <%= link_to(n_('View comment', "View all %s comments".html_safe, activity.comments_count) % activity.comments_count, :profile => profile.identifier, :controller => 'profile', :action => 'more_comments', :activity => activity, :comment_page => (1)) %>
9 9 </div>
10 10 </div>
11 11 <% end %>
... ...
app/views/profile/_profile_scraps.html.erb
... ... @@ -23,7 +23,7 @@
23 23 <% if scrap.replies.count > 0 %>
24 24 <div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments">
25 25 <div class='view-all-comments icon-chat'>
26   - <%= link_to(n_('View comment', "View all %s comments", scrap.replies.count) % scrap.replies.count, :profile => profile.identifier, :controller => 'profile', :action => 'more_replies', :activity => activity, :comment_page => (1)) %>
  26 + <%= link_to(n_('View comment', "View all %s comments".html_safe, scrap.replies.count) % scrap.replies.count, :profile => profile.identifier, :controller => 'profile', :action => 'more_replies', :activity => activity, :comment_page => (1)) %>
27 27 </div>
28 28 </div>
29 29 <% end %>
... ...
app/views/profile/followed.html.erb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +<div class="common-profile-list-block">
  2 +
  3 +<h1><%= _("%s is followed by") % profile.name %></h1>
  4 +
  5 +<%= render :partial => 'follow', :locals => {:follow => @followed_by} %>
  6 +
  7 +</div>
... ...
app/views/profile/following.html.erb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +<div class="common-profile-list-block">
  2 +
  3 +<h1><%= _("%s is following") % profile.name %></h1>
  4 +
  5 +<%= render :partial => 'follow', :locals => {:follow => @followed_people} %>
  6 +
  7 +</div>
... ...
app/views/profile_editor/edit.html.erb
1   -<h1><%= _('Profile settings for %s') % profile.name %></h1>
  1 +<h1><%= _('Profile settings for %s').html_safe % profile.name %></h1>
2 2  
3 3 <%= error_messages_for :profile_data %>
4 4  
... ... @@ -18,14 +18,16 @@
18 18 </div>
19 19 <div id="profile_change_picture">
20 20 <%= f.fields_for :image_builder, @profile.image do |i| %>
21   - <%= file_field_or_thumbnail(_('Image:'), @profile.image, i) %><%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %>
  21 + <%= file_field_or_thumbnail(_('Image:'), @profile.image, i) %><%= _("Max size: %s (.jpg, .gif, .png)").html_safe % Image.max_size.to_humanreadable %>
22 22 <% end %>
23 23 </div>
24 24  
25 25 <h2><%= _('Privacy options') %></h2>
26   -
  26 + <div>
  27 + <%= labelled_check_box _("Allow other users to follow me"), 'profile_data[allow_followers]', true, @profile.allow_followers?, :class => "person-can-be-followed" %>
  28 + </div>
27 29 <% if profile.person? %>
28   - <div>
  30 + <div id="profile_allow_follows">
29 31 <%= labelled_radio_button _('Public &mdash; show my contents to all internet users').html_safe, 'profile_data[public_profile]', true, @profile.public_profile? %>
30 32 </div>
31 33 <div>
... ...
app/views/profile_editor/index.html.erb
... ... @@ -72,6 +72,11 @@
72 72  
73 73 <%= control_panel_button(_('Email Templates'), 'email-templates', :controller => :profile_email_templates) if profile.organization? %>
74 74  
  75 + <% if profile.person? %>
  76 + <%= control_panel_button(_('Manage followed profiles'), 'edit-profile', :controller => :followers) %>
  77 + <%= control_panel_button(_('Manage circles'), 'edit-profile-group', :controller => :circles) %>
  78 + <% end %>
  79 +
75 80 <% @plugins.dispatch(:control_panel_buttons).each do |button| %>
76 81 <%= control_panel_button(button[:title], button[:icon], button[:url], button[:html_options]) %>
77 82 <% end %>
... ...
app/views/search/_search_content.html.erb
1 1 <div id='search-content'>
2 2 <div class='total'>
3   - <%= n_('Total of 1 result', 'Total of %s results', @searches[@asset][:results].total_entries) % @searches[@asset][:results].total_entries.inspect %>
  3 + <%= n_('Total of 1 result', 'Total of %s results'.html_safe, @searches[@asset][:results].total_entries) % @searches[@asset][:results].total_entries.inspect %>
4 4 </div>
5 5  
6 6 <%= display_results(@searches, @asset) %>
... ...
app/views/search/search_page.html.erb
1   -<%= render :partial => 'search_form', :locals => { :hint => _("Type words about the %s you're looking for") % _(@asset.to_s.singularize) } %>
  1 +<%= render :partial => 'search_form', :locals => { :hint => _("Type words about the %s you're looking for").html_safe % _(@asset.to_s.singularize) } %>
2 2 <%= render :partial => 'search_content' %>
3 3  
4 4 <div style="clear: both"></div>
... ...
app/views/shared/_remove_suggestion.html.erb
1 1 <%= profile_image suggestion, :thumb, :class => 'suggestion_picture' %>
2 2  
3 3 <p>
4   -<%= _('Are you sure you want to remove %s from your suggestions list?') % suggestion.name %>
  4 +<%= _('Are you sure you want to remove %s from your suggestions list?').html_safe % suggestion.name %>
5 5 </p>
6 6  
7 7 <%= form_tag do %>
8   - <%= submit_button(:ok, _("Yes, I want to remove %s") % suggestion.name) %>
  8 + <%= submit_button(:ok, _("Yes, I want to remove %s").html_safe % suggestion.name) %>
9 9 <%= button(:cancel, _("No"), :action => 'suggest') %>
10 10 <% end %>
... ...
app/views/tasks/ticket_details.html.erb
1   -<h2><%= _('Ticket: %s') % @ticket.name %></h2>
  1 +<h2><%= _('Ticket: %s').html_safe % @ticket.name %></h2>
2 2 <p><small>
3   - <%= _('Created at %s by %s') % [@ticket.created_at.to_date, link_to(@ticket.requestor.name, @ticket.requestor.url)] %><br/>
4   - <%= _('Owner: %s') % link_to(@ticket.target.name, @ticket.target.url) %>
  3 + <%= _('Created at %s by %s').html_safe % [@ticket.created_at.to_date, link_to(@ticket.requestor.name, @ticket.requestor.url)] %><br/>
  4 + <%= _('Owner: %s').html_safe % link_to(@ticket.target.name, @ticket.target.url) %>
5 5 </small></p>
6 6  
7   -<p><%= _('Status: %s') % gettext(Task::Status.names[@ticket.status]) %></p>
  7 +<p><%= _('Status: %s').html_safe % gettext(Task::Status.names[@ticket.status]) %></p>
8 8  
9 9 <div>
10   - <p><%= _('Description: %s') % @ticket.description %></p>
  10 + <p><%= _('Description: %s').html_safe % @ticket.description %></p>
11 11 </div>
12 12  
13 13 <% if @ticket.closing_statment %>
14   - <p><%= _('Closing statement: %s') % @ticket.closing_statment %></p>
  14 + <p><%= _('Closing statement: %s').html_safe % @ticket.closing_statment %></p>
15 15 <% end %>
16 16  
17 17 <%= button_bar do %>
... ...
app/views/templates/index.html.erb
... ... @@ -10,7 +10,7 @@
10 10 <% list_of_templates.each do |title, templates, kind|%>
11 11 <div class='template-kind'>
12 12 <h2><%= title %></h2>
13   - <%= button :add, _('New...'), {:action => "create_#{kind}_template"}, :title => _("Create a new template for %s") % title.downcase %>
  13 + <%= button :add, _('New...'), {:action => "create_#{kind}_template"}, :title => _("Create a new template for %s").html_safe % title.downcase %>
14 14 <table class='actions'>
15 15 <tr>
16 16 <th><%= _('Template') %></th>
... ... @@ -20,11 +20,11 @@
20 20 <tr>
21 21 <td>
22 22 <%#= image_tag "icons-app/#{kind}-icon.png" %>
23   - <%= link_to(template.name, {:controller => 'profile_editor', :profile => template.identifier}, :title => _('Edit template "%s"') % template.name ) %>
  23 + <%= link_to(template.name, {:controller => 'profile_editor', :profile => template.identifier}, :title => _('Edit template "%s"').html_safe % template.name ) %>
24 24 <% if environment.is_default_template?(template) %>
25 25 <%= _('is the default template') %>
26 26 <% else %>
27   - <%= link_to(_('Set as default'), {:action => "set_#{kind}_as_default", :template_id => template.id}, :title => _('Set %s template as default') % template.name ) %>
  27 + <%= link_to(_('Set as default'), {:action => "set_#{kind}_as_default", :template_id => template.id}, :title => _('Set %s template as default').html_safe % template.name ) %>
28 28 <% end %>
29 29 </td>
30 30 <td>
... ...
app/views/user_mailer/activation_email_notify.text.erb
1   -<%= _('Hello %s,') % @name %>
  1 +<%= _('Hello %s,').html_safe % @name %>
2 2  
3   -<%= _('Your email %s was just activated.') % [@email] %>
  3 +<%= _('Your email %s was just activated.').html_safe % [@email] %>
4 4  
5 5 <%= _('You can access your e-mail from anywhere, using the following address:') %>
6 6 <%= @webmail %>
... ... @@ -8,5 +8,5 @@
8 8 <%= _('Greetings,') %>
9 9  
10 10 --
11   -<%= _('%s team.') % @environment.name %>
  11 +<%= _('%s team.').html_safe % @environment.name %>
12 12 <%= @url %>
... ...
config/initializers/00_dependencies.rb
... ... @@ -16,15 +16,6 @@ end
16 16 require 'extensions'
17 17  
18 18 # locally-developed modules
19   -require 'acts_as_filesystem'
20   -require 'acts_as_having_settings'
21   -require 'acts_as_having_boxes'
22   -require 'acts_as_having_image'
23   -require 'acts_as_having_posts'
24   -require 'acts_as_customizable'
25 19 require 'route_if'
26 20 require 'maybe_add_http'
27   -require 'set_profile_region_from_city_state'
28   -require 'needs_profile'
29   -require 'white_list_filter'
30 21  
... ...
config/initializers/action_tracker.rb
... ... @@ -12,6 +12,10 @@ ActionTrackerConfig.verbs = {
12 12 type: :groupable
13 13 },
14 14  
  15 + new_follower: {
  16 + type: :groupable
  17 + },
  18 +
15 19 join_community: {
16 20 type: :groupable
17 21 },
... ...
db/migrate/20160422163123_enable_products_plugin_on_environments.rb
... ... @@ -7,6 +7,7 @@ class Environment &lt; ApplicationRecord
7 7 has_many :profiles
8 8 has_many :products, through: :profiles
9 9  
  10 + extend ActsAsHavingSettings::ClassMethods
10 11 acts_as_having_settings field: :settings
11 12 settings_items :enabled_plugins, type: Array
12 13 end
... ...
db/migrate/20160608123748_create_profile_followers_table.rb 0 → 100644
... ... @@ -0,0 +1,43 @@
  1 +class CreateProfileFollowersTable < ActiveRecord::Migration
  2 + def up
  3 + create_table :profiles_circles do |t|
  4 + t.column :profile_id, :integer
  5 + t.column :circle_id, :integer
  6 + t.timestamps
  7 + end
  8 +
  9 + create_table :circles do |t|
  10 + t.column :name, :string
  11 + t.belongs_to :person
  12 + t.column :profile_type, :string, :null => false
  13 + end
  14 +
  15 + add_foreign_key :profiles_circles, :circles, :on_delete => :nullify
  16 +
  17 + add_index :profiles_circles, [:profile_id, :circle_id], :name => "profiles_circles_composite_key_index", :unique => true
  18 + add_index :circles, [:person_id, :name], :name => "circles_composite_key_index", :unique => true
  19 +
  20 + #insert one category for each friend group a person has
  21 + execute("INSERT INTO circles(name, person_id, profile_type) SELECT DISTINCT 'friendships', f.person_id, 'Person' FROM friendships as f WHERE f.group IS NULL OR f.group = ''")
  22 + execute("INSERT INTO circles(name, person_id, profile_type) SELECT DISTINCT regexp_split_to_table(f.group, ', '), f.person_id, 'Person' FROM friendships as f WHERE f.group IS NOT NULL AND f.group <> ''")
  23 + #insert 'memberships' category if a person is in a community as any role
  24 + execute("INSERT INTO circles(name, person_id, profile_type) SELECT DISTINCT 'memberships', ra.accessor_id, 'Community' FROM role_assignments as ra JOIN profiles ON ra.resource_id = profiles.id WHERE ra.resource_type = 'Profile' AND profiles.type = 'Community'")
  25 + #insert 'favorites' category if a person has any favorited enterprise
  26 + execute("INSERT INTO circles(name, person_id, profile_type) SELECT DISTINCT 'favorites', person_id, 'Enterprise' FROM favorite_enterprise_people")
  27 +
  28 + #insert a follower entry for each friend, with the category the same as the friendship group or equals 'friendships'
  29 + execute("INSERT INTO profiles_circles(profile_id, circle_id) SELECT DISTINCT f.friend_id, c.id FROM friendships as f JOIN circles as c ON f.person_id = c.person_id WHERE c.name = ANY(regexp_split_to_array(f.group, ', ')) OR c.name = 'friendships'")
  30 + #insert a follower entry for each favorited enterprise, with the category 'favorites'
  31 + execute("INSERT INTO profiles_circles(profile_id, circle_id) SELECT DISTINCT f.enterprise_id, c.id FROM favorite_enterprise_people AS f JOIN circles as c ON f.person_id = c.person_id WHERE c.name = 'favorites' ")
  32 + #insert a follower entry for each community a person participates with any role
  33 + execute("INSERT INTO profiles_circles(profile_id, circle_id) SELECT DISTINCT ra.resource_id, c.id FROM role_assignments as ra JOIN profiles ON ra.accessor_id = profiles.id JOIN circles as c ON ra.accessor_id = c.person_id WHERE ra.resource_type = 'Profile' AND profiles.type = 'Community' AND c.name = 'memberships'")
  34 + end
  35 +
  36 + def down
  37 + remove_foreign_key :profiles_circles, :circles
  38 + remove_index :profiles_circles, :name => "profiles_circles_composite_key_index"
  39 + remove_index :circles, :name => "circles_composite_key_index"
  40 + drop_table :circles
  41 + drop_table :profiles_circles
  42 + end
  43 +end
... ...
db/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 12 # It's strongly recommended that you check this file into your version control system.
13 13  
14   -ActiveRecord::Schema.define(version: 20160422163123) do
  14 +ActiveRecord::Schema.define(version: 20160608123748) do
15 15  
16 16 # These are extensions that must be enabled in order to support this database
17 17 enable_extension "plpgsql"
... ... @@ -274,6 +274,14 @@ ActiveRecord::Schema.define(version: 20160422163123) do
274 274 add_index "chat_messages", ["from_id"], name: "index_chat_messages_on_from_id", using: :btree
275 275 add_index "chat_messages", ["to_id"], name: "index_chat_messages_on_to_id", using: :btree
276 276  
  277 + create_table "circles", force: :cascade do |t|
  278 + t.string "name"
  279 + t.integer "person_id"
  280 + t.string "profile_type", null: false
  281 + end
  282 +
  283 + add_index "circles", ["person_id", "name"], name: "circles_composite_key_index", unique: true, using: :btree
  284 +
277 285 create_table "comments", force: :cascade do |t|
278 286 t.string "title"
279 287 t.text "body"
... ... @@ -639,6 +647,15 @@ ActiveRecord::Schema.define(version: 20160422163123) do
639 647 add_index "profiles", ["user_id", "type"], name: "index_profiles_on_user_id_and_type", using: :btree
640 648 add_index "profiles", ["user_id"], name: "index_profiles_on_user_id", using: :btree
641 649  
  650 + create_table "profiles_circles", force: :cascade do |t|
  651 + t.integer "profile_id"
  652 + t.integer "circle_id"
  653 + t.datetime "created_at"
  654 + t.datetime "updated_at"
  655 + end
  656 +
  657 + add_index "profiles_circles", ["profile_id", "circle_id"], name: "profiles_circles_composite_key_index", unique: true, using: :btree
  658 +
642 659 create_table "qualifier_certifiers", force: :cascade do |t|
643 660 t.integer "qualifier_id"
644 661 t.integer "certifier_id"
... ... @@ -860,4 +877,5 @@ ActiveRecord::Schema.define(version: 20160422163123) do
860 877 add_index "votes", ["voteable_id", "voteable_type"], name: "fk_voteables", using: :btree
861 878 add_index "votes", ["voter_id", "voter_type"], name: "fk_voters", using: :btree
862 879  
  880 + add_foreign_key "profiles_circles", "circles", on_delete: :nullify
863 881 end
... ...
features/follow_profile.feature 0 → 100644
... ... @@ -0,0 +1,114 @@
  1 +Feature: follow profile
  2 + As a noosfero user
  3 + I want to follow a profile
  4 + So I can receive notifications from it
  5 +
  6 + Background:
  7 + Given the following community
  8 + | identifier | name |
  9 + | nightswatch | Nights Watch |
  10 + And the following users
  11 + | login |
  12 + | johnsnow |
  13 + And the user "johnsnow" has the following circles
  14 + | name | profile_type |
  15 + | Family | Person |
  16 + | Work | Community |
  17 + | Favorites | Community |
  18 +
  19 + @selenium
  20 + Scenario: Common noofero user follow a community
  21 + Given I am logged in as "johnsnow"
  22 + When I go to nightswatch's homepage
  23 + When I follow "Follow"
  24 + When I check "Work"
  25 + When I press "Follow"
  26 + And I wait 1 second
  27 + Then "johnsnow" should be a follower of "nightswatch" in circle "Work"
  28 +
  29 + @selenium
  30 + Scenario: Common noofero user follow a community in more than one circle
  31 + Given I am logged in as "johnsnow"
  32 + When I go to nightswatch's homepage
  33 + When I follow "Follow"
  34 + When I check "Work"
  35 + When I check "Favorites"
  36 + When I press "Follow"
  37 + And I wait 1 second
  38 + Then "johnsnow" should be a follower of "nightswatch" in circle "Work"
  39 + And "johnsnow" should be a follower of "nightswatch" in circle "Favorites"
  40 +
  41 + @selenium
  42 + Scenario: No see another profile type circle when following a community
  43 + Given I am logged in as "johnsnow"
  44 + When I go to nightswatch's homepage
  45 + When I follow "Follow"
  46 + Then I should not see "Family"
  47 + And I should see "Favorites"
  48 + And I should see "Work"
  49 +
  50 + @selenium
  51 + Scenario: Common noofero user follow a community then cancel the action
  52 + Given I am logged in as "johnsnow"
  53 + When I go to nightswatch's homepage
  54 + When I follow "Follow"
  55 + When I press "Cancel"
  56 + And I wait 1 second
  57 + Then I should not see "Family"
  58 + And I should not see "Favorites"
  59 + And I should not see "Work"
  60 + And I should not see "New Circle"
  61 + Then "johnsnow" should not be a follower of "nightswatch"
  62 +
  63 + @selenium
  64 + Scenario: Common noofero user cancel the circle creation action
  65 + Given I am logged in as "johnsnow"
  66 + When I go to nightswatch's homepage
  67 + When I follow "Follow"
  68 + When I follow "New Circle"
  69 + When I press "Cancel"
  70 + And I wait 1 second
  71 + Then I should not see "Circle name"
  72 + And I should not see "Create"
  73 +
  74 + @selenium
  75 + Scenario: Noosfero user see new circle option when following a community
  76 + Given I am logged in as "johnsnow"
  77 + When I go to nightswatch's homepage
  78 + When I follow "Follow"
  79 + Then I should see "New Circle"
  80 +
  81 + @selenium
  82 + Scenario: Common noofero user follow a community with a new circle
  83 + Given I am logged in as "johnsnow"
  84 + When I go to nightswatch's homepage
  85 + When I follow "Follow"
  86 + When I follow "New Circle"
  87 + And I fill in "text-field-name-new-circle" with "Winterfell"
  88 + When I follow "Create"
  89 + When I check "Winterfell"
  90 + When I press "Follow"
  91 + And I wait 1 second
  92 + Then "johnsnow" should be a follower of "nightswatch" in circle "Winterfell"
  93 +
  94 + @selenium
  95 + Scenario: Common noofero user create a new circle when following a community
  96 + Given I am logged in as "johnsnow"
  97 + When I go to nightswatch's homepage
  98 + When I follow "Follow"
  99 + When I follow "New Circle"
  100 + And I fill in "text-field-name-new-circle" with "Winterfell"
  101 + When I follow "Create"
  102 + And I wait 1 second
  103 + Then "johnsnow" should have the circle "Winterfell" with profile type "Community"
  104 + Then I should not see "Circle name"
  105 + Then I should not see "Create"
  106 +
  107 + @selenium
  108 + Scenario: Common noofero user unfollow a community
  109 + Given "johnsnow" is a follower of "nightswatch" in circle "Work"
  110 + And I am logged in as "johnsnow"
  111 + When I go to nightswatch's homepage
  112 + When I follow "Unfollow"
  113 + Then "johnsnow" should not be a follower of "nightswatch"
  114 +
... ...
features/step_definitions/followers_steps.rb 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +Given /^the user "(.+)" has the following circles$/ do |user_name,table|
  2 + person = User.find_by(:login => user_name).person
  3 + table.hashes.each do |circle|
  4 + Circle.create!(:person => person, :name => circle[:name], :profile_type => circle[:profile_type])
  5 + end
  6 +end
  7 +
  8 +Then /^"(.+)" should be a follower of "(.+)" in circle "(.+)"$/ do |person, profile, circle|
  9 + profile = Profile.find_by(identifier: profile)
  10 + followers = profile.followers
  11 + person = Person.find_by(identifier: person)
  12 + followers.should include(person)
  13 +
  14 + circle = Circle.find_by(:name => circle, :person => person)
  15 + ProfileFollower.find_by(:circle => circle, :profile => profile).should_not == nil
  16 +end
  17 +
  18 +Then /^"(.+)" should not be a follower of "(.+)"$/ do |person, profile|
  19 + profile = Profile.find_by(identifier: profile)
  20 + followers = profile.followers
  21 + person = Person.find_by(identifier: person)
  22 + followers.should_not include(person)
  23 +end
  24 +
  25 +Given /^"(.+)" is a follower of "(.+)" in circle "(.+)"$/ do |person, profile, circle|
  26 + profile = Profile.find_by(identifier: profile)
  27 + person = Person.find_by(identifier: person)
  28 + circle = Circle.find_by(:name => circle, :person => person)
  29 + ProfileFollower.create!(:circle => circle, :profile => profile)
  30 +end
  31 +
  32 +Then /^"(.+)" should have the circle "(.+)" with profile type "(.+)"$/ do |user_name, circle, profile_type|
  33 + person = User.find_by(:login => user_name).person
  34 + Circle.find_by(:name => circle, :person => person, :profile_type => profile_type).should_not == nil
  35 +end
... ...
plugins/admin_notifications/test/functional/account_controller_test.rb
... ... @@ -8,8 +8,7 @@ end
8 8 class AccountControllerTest < ActionController::TestCase
9 9 def setup
10 10 @controller = AccountController.new
11   - @request = ActionController::TestRequest.new
12   - @response = ActionController::TestResponse.new
  11 +
13 12 @person = create_user('person').person
14 13  
15 14 @environment = Environment.default
... ...
plugins/admin_notifications/test/functional/admin_notifications_plugin_admin_controller_test.rb
... ... @@ -4,8 +4,6 @@ require_relative &#39;../../controllers/admin_notifications_plugin_admin_controller&#39;
4 4 class AdminNotificationsPluginAdminControllerTest < ActionController::TestCase
5 5 def setup
6 6 @controller = AdminNotificationsPluginAdminController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7 @person = create_user('person').person
10 8  
11 9 @environment = Environment.default
... ...
plugins/admin_notifications/test/functional/admin_notifications_plugin_myprofile_controller_test.rb
... ... @@ -7,8 +7,6 @@ require(
7 7 class AdminNotificationsPluginMyprofileControllerTest < ActionController::TestCase
8 8 def setup
9 9 @controller = AdminNotificationsPluginMyprofileController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
12 10 @person = create_user('person').person
13 11 @community = fast_create(Community)
14 12  
... ...
plugins/admin_notifications/test/functional/admin_notifications_plugin_public_controller_test.rb
... ... @@ -7,8 +7,6 @@ require(
7 7 class AdminNotificationsPluginPublicControllerTest < ActionController::TestCase
8 8 def setup
9 9 @controller = AdminNotificationsPluginPublicController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
12 10 @person = create_user('person').person
13 11  
14 12 @environment = Environment.default
... ...
plugins/admin_notifications/test/functional/home_controller_test.rb
... ... @@ -7,8 +7,7 @@ end
7 7 class HomeControllerTest < ActionController::TestCase
8 8 def setup
9 9 @controller = HomeController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
  10 +
12 11 @person = create_user('person').person
13 12  
14 13 @environment = Environment.default
... ...
plugins/analytics/Gemfile 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +gem 'browser', '~> 2.2.0'
  2 +
... ...
plugins/analytics/controllers/myprofile/analytics_plugin/stats_controller.rb
... ... @@ -7,12 +7,39 @@ class AnalyticsPlugin::StatsController &lt; MyProfileController
7 7 def index
8 8 end
9 9  
  10 + def edit
  11 + return render_access_denied unless user.has_permission? 'edit_profile', profile
  12 +
  13 + params[:analytics_settings][:enabled] = params[:analytics_settings][:enabled] == 'true'
  14 + params[:analytics_settings][:anonymous] = params[:analytics_settings][:anonymous] == 'true'
  15 + @settings = profile.analytics_settings params[:analytics_settings] || {}
  16 + @settings.save!
  17 + render nothing: true
  18 + end
  19 +
  20 + def view
  21 + params[:profile_ids] ||= [profile.id]
  22 + ids = params[:profile_ids].map(&:to_i)
  23 + user.adminships # FIXME just to cache #adminship_ids
  24 + ids = ids.select{ |id| id.in? user.adminship_ids } unless @user_is_admin
  25 +
  26 + @profiles = environment.profiles.find ids
  27 + @user = environment.people.find params[:user_id]
  28 + @visits = AnalyticsPlugin::Visit.eager_load(:users_page_views).
  29 + where(profile_id: ids, analytics_plugin_page_views: {user_id: @user.id})
  30 +
  31 + render partial: 'table', locals: {visits: @visits}
  32 +
  33 + end
  34 +
10 35 protected
11 36  
12   - def default_url_options
13   - # avoid rails' use_relative_controller!
14   - {use_route: '/'}
  37 + # inherit routes from core skipping use_relative_controller!
  38 + def url_for options
  39 + options[:controller] = "/#{options[:controller]}" if options.is_a? Hash and options[:controller] and not options[:controller].to_s.starts_with? '/'
  40 + super options
15 41 end
  42 + helper_method :url_for
16 43  
17 44 def skip_page_view
18 45 @analytics_skip_page_view = true
... ...
plugins/analytics/controllers/profile/analytics_plugin/time_on_page_controller.rb
... ... @@ -7,7 +7,10 @@ class AnalyticsPlugin::TimeOnPageController &lt; ProfileController
7 7 Noosfero::Scheduler::Defer.later do
8 8 page_view = profile.page_views.where(request_id: params[:id]).first
9 9 page_view.request = request
10   - page_view.page_load!
  10 + AnalyticsPlugin::PageView.transaction do
  11 + page_view.page_load! Time.at(params[:time].to_i)
  12 + page_view.update_column :title, params[:title] if params[:title].present?
  13 + end
11 14 end
12 15  
13 16 render nothing: true
... ...
plugins/analytics/db/migrate/20151030122634_add_title_and_is_bot_to_analytics_plugin_page_view.rb 0 → 100644
... ... @@ -0,0 +1,40 @@
  1 +class AddTitleAndIsBotToAnalyticsPluginPageView < ActiveRecord::Migration
  2 +
  3 + def up
  4 + add_column :analytics_plugin_page_views, :title, :text
  5 + add_column :analytics_plugin_page_views, :is_bot, :boolean
  6 +
  7 + # missing indexes for performance
  8 + add_index :analytics_plugin_page_views, :type
  9 + add_index :analytics_plugin_page_views, :visit_id
  10 + add_index :analytics_plugin_page_views, :request_started_at
  11 + add_index :analytics_plugin_page_views, :page_loaded_at
  12 + add_index :analytics_plugin_page_views, :is_bot
  13 +
  14 + AnalyticsPlugin::PageView.transaction do
  15 + AnalyticsPlugin::PageView.find_each do |page_view|
  16 + page_view.send :fill_is_bot
  17 + page_view.update_column :is_bot, page_view.is_bot
  18 + end
  19 + end
  20 +
  21 + change_table :analytics_plugin_visits do |t|
  22 + t.timestamps
  23 + end
  24 + AnalyticsPlugin::Visit.transaction do
  25 + AnalyticsPlugin::Visit.find_each do |visit|
  26 + visit.created_at = visit.page_views.first.request_started_at
  27 + visit.updated_at = visit.page_views.last.request_started_at
  28 + visit.save!
  29 + end
  30 + end
  31 +
  32 + # never used
  33 + remove_column :analytics_plugin_page_views, :track_id
  34 + end
  35 +
  36 + def down
  37 + say "this migration can't be reverted"
  38 + end
  39 +
  40 +end
... ...
plugins/analytics/lib/analytics_plugin.rb
... ... @@ -13,4 +13,15 @@ module AnalyticsPlugin
13 13 I18n.t'analytics_plugin.lib.plugin.description'
14 14 end
15 15  
  16 + def self.clear_non_users
  17 + ActiveRecord::Base.transaction do
  18 + AnalyticsPlugin::PageView.bots.delete_all
  19 + AnalyticsPlugin::PageView.not_page_loaded.delete_all
  20 + # delete_all does not work here
  21 + AnalyticsPlugin::Visit.without_page_views.destroy_all
  22 + end
  23 + end
  24 +
16 25 end
  26 +
  27 +Browser::Bot.detect_empty_ua!
... ...
plugins/analytics/lib/analytics_plugin/base.rb
... ... @@ -3,6 +3,7 @@ class AnalyticsPlugin::Base &lt; Noosfero::Plugin
3 3  
4 4 def body_ending
5 5 return unless profile and profile.analytics_enabled?
  6 + return if @analytics_skip_page_view
6 7 lambda do
7 8 render 'analytics_plugin/body_ending'
8 9 end
... ... @@ -12,6 +13,7 @@ class AnalyticsPlugin::Base &lt; Noosfero::Plugin
12 13 ['analytics'].map{ |j| "javascripts/#{j}" }
13 14 end
14 15  
  16 + # FIXME: not reloading on development, need server restart
15 17 def application_controller_filters
16 18 [{
17 19 type: 'around_filter', options: {}, block: -> &block do
... ... @@ -23,15 +25,12 @@ class AnalyticsPlugin::Base &lt; Noosfero::Plugin
23 25 return unless profile and profile.analytics_enabled?
24 26  
25 27 Noosfero::Scheduler::Defer.later 'analytics: register page view' do
26   - page_view = profile.page_views.build request: request, profile_id: profile,
  28 + page_view = profile.page_views.build request: request, profile_id: profile.id,
27 29 request_started_at: request_started_at, request_finished_at: request_finished_at
28   -
29 30 unless profile.analytics_anonymous?
30   - session_id = session.id
31 31 page_view.user = user
32   - page_view.session_id = session_id
  32 + page_view.session_id = session.id
33 33 end
34   -
35 34 page_view.save!
36 35 end
37 36 end,
... ... @@ -39,6 +38,7 @@ class AnalyticsPlugin::Base &lt; Noosfero::Plugin
39 38 end
40 39  
41 40 def control_panel_buttons
  41 + return unless user.is_admin? environment
42 42 {
43 43 title: I18n.t('analytics_plugin.lib.plugin.panel_button'),
44 44 icon: 'analytics-access',
... ...
plugins/analytics/lib/ext/profile.rb
1 1 require_dependency 'profile'
2   -require_dependency 'community'
3 2  
4   -([Profile] + Profile.descendants).each do |subclass|
5   -subclass.class_eval do
  3 +class Profile
6 4  
7   - has_many :visits, foreign_key: :profile_id, class_name: 'AnalyticsPlugin::Visit'
8   - has_many :page_views, foreign_key: :profile_id, class_name: 'AnalyticsPlugin::PageView'
  5 + has_many :users_visits, -> { latest.with_users_page_views }, foreign_key: :profile_id, class_name: 'AnalyticsPlugin::Visit'
9 6  
10   -end
11   -end
  7 + has_many :visits, -> { latest.eager_load :page_views }, foreign_key: :profile_id, class_name: 'AnalyticsPlugin::Visit'
  8 + has_many :page_views, foreign_key: :profile_id, class_name: 'AnalyticsPlugin::PageView'
12 9  
13   -class Profile
  10 + has_many :user_visits, -> { latest.eager_load :page_views }, foreign_key: :user_id, class_name: 'AnalyticsPlugin::PageView'
  11 + has_many :user_page_views, foreign_key: :user_id, class_name: 'AnalyticsPlugin::PageView'
14 12  
15 13 def analytics_settings attrs = {}
16 14 @analytics_settings ||= Noosfero::Plugin::Settings.new self, ::AnalyticsPlugin, attrs
... ...
plugins/analytics/locales/en.yml
... ... @@ -9,10 +9,14 @@ en: &amp;en
9 9  
10 10 views:
11 11 stats:
  12 + enable: "Enable tracking on the profile '%{profile}'"
  13 + anonymous: "Don't associate users' login"
  14 + config_save: "Configuration saved"
12 15 user: 'User'
13 16 initial_time: 'Time'
  17 + ip: 'IP'
14 18 pages: 'Pages'
15 19  
16   -en-US:
  20 +en_US:
17 21 <<: *en
18 22  
... ...
plugins/analytics/locales/pt.yml
... ... @@ -9,9 +9,14 @@ pt: &amp;pt
9 9  
10 10 views:
11 11 stats:
  12 + enable: "Ativar rastreio no perfil '%{profile}'"
  13 + anonymous: "Não associar login de usuários"
  14 + config_save: "Configuração salva"
12 15 user: 'Usuário'
13 16 initial_time: 'Horário'
  17 + ip: 'IP'
14 18 pages: 'Páginas'
15 19  
16   -pt-BR:
  20 +pt_BR:
17 21 <<: *pt
  22 +
... ...
plugins/analytics/models/analytics_plugin/page_view.rb
... ... @@ -8,24 +8,37 @@ class AnalyticsPlugin::PageView &lt; ApplicationRecord
8 8 attr_accessor :request
9 9 attr_accessible :request
10 10  
  11 + extend ActsAsHavingSettings::ClassMethods
11 12 acts_as_having_settings field: :options
12 13  
13   - belongs_to :visit, class_name: 'AnalyticsPlugin::Visit'
14   - belongs_to :referer_page_view, class_name: 'AnalyticsPlugin::PageView'
  14 + belongs_to :profile, validate: true
  15 + belongs_to :visit, class_name: 'AnalyticsPlugin::Visit', touch: true, validate: true
15 16  
16   - belongs_to :user, class_name: 'Person'
17   - belongs_to :session, primary_key: :session_id, foreign_key: :session_id, class_name: 'Session'
18   - belongs_to :profile
  17 + belongs_to :referer_page_view, class_name: 'AnalyticsPlugin::PageView', validate: false
19 18  
20   - validates_presence_of :visit
21   - validates_presence_of :request, on: :create
22   - validates_presence_of :url
  19 + belongs_to :user, class_name: 'Person', validate: false
  20 + belongs_to :session, primary_key: :session_id, foreign_key: :session_id, class_name: 'Session', validate: false
  21 +
  22 + validates :request, presence: true, on: :create
  23 + validates :url, presence: true
23 24  
24 25 before_validation :extract_request_data, on: :create
25 26 before_validation :fill_referer_page_view, on: :create
26 27 before_validation :fill_visit, on: :create
  28 + before_validation :fill_is_bot, on: :create
  29 +
  30 + after_update :destroy_empty_visit
  31 + after_destroy :destroy_empty_visit
  32 +
  33 + scope :in_sequence, -> { order 'analytics_plugin_page_views.request_started_at ASC' }
  34 +
  35 + scope :page_loaded, -> { where 'analytics_plugin_page_views.page_loaded_at IS NOT NULL' }
  36 + scope :not_page_loaded, -> { where 'analytics_plugin_page_views.page_loaded_at IS NULL' }
27 37  
28   - scope :latest, -> { order 'request_started_at DESC' }
  38 + scope :no_bots, -> { where.not is_bot: true }
  39 + scope :bots, -> { where is_bot: true }
  40 +
  41 + scope :loaded_users, -> { in_sequence.page_loaded.no_bots }
29 42  
30 43 def request_duration
31 44 self.request_finished_at - self.request_started_at
... ... @@ -43,8 +56,8 @@ class AnalyticsPlugin::PageView &lt; ApplicationRecord
43 56 Time.now < self.user_last_time_seen + AnalyticsPlugin::TimeOnPageUpdateInterval
44 57 end
45 58  
46   - def page_load!
47   - self.page_loaded_at = Time.now
  59 + def page_load! time
  60 + self.page_loaded_at = time
48 61 self.update_column :page_loaded_at, self.page_loaded_at
49 62 end
50 63  
... ... @@ -56,6 +69,16 @@ class AnalyticsPlugin::PageView &lt; ApplicationRecord
56 69 self.update_column :time_on_page, self.time_on_page
57 70 end
58 71  
  72 + def find_referer_page_view
  73 + return if self.referer_url.blank?
  74 + AnalyticsPlugin::PageView.order('request_started_at DESC').
  75 + where(url: self.referer_url, session_id: self.session_id, user_id: self.user_id, profile_id: self.profile_id).first
  76 + end
  77 +
  78 + def browser
  79 + @browser ||= Browser.new self.user_agent
  80 + end
  81 +
59 82 protected
60 83  
61 84 def extract_request_data
... ... @@ -64,16 +87,29 @@ class AnalyticsPlugin::PageView &lt; ApplicationRecord
64 87 self.user_agent = self.request.headers['User-Agent']
65 88 self.request_id = self.request.env['action_dispatch.request_id']
66 89 self.remote_ip = self.request.remote_ip
  90 + true
67 91 end
68 92  
69 93 def fill_referer_page_view
70   - self.referer_page_view = AnalyticsPlugin::PageView.order('request_started_at DESC').
71   - where(url: self.referer_url, session_id: self.session_id, user_id: self.user_id, profile_id: self.profile_id).first if self.referer_url.present?
  94 + self.referer_page_view = self.find_referer_page_view
  95 + true
72 96 end
73 97  
74 98 def fill_visit
75 99 self.visit = self.referer_page_view.visit if self.referer_page_view and self.referer_page_view.user_on_page?
76 100 self.visit ||= AnalyticsPlugin::Visit.new profile: profile
  101 + true
  102 + end
  103 +
  104 + def fill_is_bot
  105 + self.is_bot = self.browser.bot?
  106 + true
  107 + end
  108 +
  109 + def destroy_empty_visit
  110 + return unless self.visit_id_changed?
  111 + old_visit = AnalyticsPlugin::Visit.find self.visit_id_was
  112 + old_visit.destroy if old_visit.page_views.empty?
77 113 end
78 114  
79 115 end
... ...
plugins/analytics/models/analytics_plugin/visit.rb
... ... @@ -5,10 +5,16 @@ class AnalyticsPlugin::Visit &lt; ApplicationRecord
5 5  
6 6 belongs_to :profile
7 7 has_many :page_views, class_name: 'AnalyticsPlugin::PageView', dependent: :destroy
  8 + has_many :users_page_views, -> { loaded_users }, class_name: 'AnalyticsPlugin::PageView', dependent: :destroy
8 9  
9   - default_scope -> { joins(:page_views).includes :page_views }
  10 + scope :latest, -> { order 'updated_at DESC' }
10 11  
11   - scope :latest, -> { order 'analytics_plugin_page_views.request_started_at DESC' }
  12 + scope :with_users_page_views, -> {
  13 + eager_load(:users_page_views).where.not analytics_plugin_page_views: {visit_id: nil}
  14 + }
  15 + scope :without_page_views, -> {
  16 + eager_load(:page_views).where analytics_plugin_page_views: {visit_id: nil}
  17 + }
12 18  
13 19 def first_page_view
14 20 self.page_views.first
... ...
plugins/analytics/public/javascripts/analytics.js
1 1 analytics = {
  2 +
  3 + t: function (key, options) {
  4 + return I18n.t(key, $.extend(options, {scope: 'analytics_plugin'}))
  5 + },
  6 +
2 7 requestId: '',
3 8  
4 9 timeOnPage: {
... ... @@ -27,7 +32,7 @@ analytics = {
27 32  
28 33 pageLoad: function() {
29 34 $.ajax(analytics.timeOnPage.baseUrl+'/page_load', {
30   - type: 'POST', data: {id: analytics.requestId},
  35 + type: 'POST', data: {id: analytics.requestId, title: document.title, time: Math.floor(Date.now()/1000)},
31 36 success: function(data) {
32 37 },
33 38 });
... ...
plugins/analytics/public/javascripts/views/settings.tag.slim 0 → 100644
... ... @@ -0,0 +1,33 @@
  1 +analytics-settings
  2 + .checkbox
  3 + label name='enabled'
  4 + input type='checkbox' name='enabled' value='1' checked='{settings.enabled}' onchange='{toggleEnabled}'
  5 + |{anl.t('views.stats.enable', {profile: noosfero.profile})}
  6 +
  7 + .checkbox if='{settings.enabled}'
  8 + label name='anonymous'
  9 + input type='checkbox' name='anonymous' value='1' checked='{settings.anonymous}' onchange='{toggleAnonymous}'
  10 + |{anl.t('views.stats.anonymous')}
  11 +
  12 + javascript:
  13 + this.anl = window.analytics
  14 + this.settings = opts.settings
  15 + this.updateUrl = Routes.analytics_plugin_stats_path({profile: noosfero.profile, action: 'edit'})
  16 +
  17 + toggleEnabled (e) {
  18 + this.settings.enabled = !this.settings.enabled
  19 + this.update()
  20 + this.save(e)
  21 + }
  22 + toggleAnonymous (e) {
  23 + this.settings.anonymous = !this.settings.anonymous
  24 + this.save(e)
  25 + }
  26 +
  27 + save (e) {
  28 + var self = this
  29 + $.post(this.updateUrl, {analytics_settings: this.settings}, function() {
  30 + display_notice(self.anl.t('views.stats.config_save'))
  31 + })
  32 + }
  33 +
... ...
plugins/analytics/test/functional/content_viewer_controller_test.rb
... ... @@ -5,8 +5,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = ContentViewerController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @environment = Environment.default
12 10 @environment.enabled_plugins += ['AnalyticsPlugin']
... ... @@ -37,7 +35,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
37 35 @request.env['HTTP_REFERER'] = first_url
38 36 get :view_page, profile: @community.identifier, page: @community.articles.last.path.split('/')
39 37 assert_equal 2, @community.page_views.count
40   - assert_equal 2, @community.visits.count
  38 + assert_equal 1, @community.visits.count
41 39  
42 40 second_page_view = @community.page_views.order(:id).last
43 41 assert_equal first_page_view, second_page_view.referer_page_view
... ... @@ -48,7 +46,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
48 46 future = Time.now + 2*AnalyticsPlugin::TimeOnPageUpdateInterval
49 47 Time.stubs(:now).returns(future)
50 48 get :view_page, profile: @community.identifier, page: @community.articles.last.path.split('/')
51   - assert_equal 3, @community.visits.count
  49 + assert_equal 2, @community.visits.count
52 50 end
53 51  
54 52 end
... ...
plugins/analytics/views/analytics_plugin/stats/_table.html.slim
1 1  
2   -table#analytics-stats.table data-toggle='table' data-striped='true' data-sortable='true' data-icons-prefix='fa'
3   - thead
4   - - unless profile.analytics_anonymous?
  2 +.table-responsive
  3 + table#analytics-stats.table data-toggle='table' data-striped='true' data-sortable='true' data-icons-prefix='fa'
  4 + thead
5 5 th= t'analytics_plugin.views.stats.user'
6   - th= t'analytics_plugin.views.stats.initial_time'
7   - th= t'analytics_plugin.views.stats.pages'
  6 + th= t'analytics_plugin.views.stats.initial_time'
  7 + th= t'analytics_plugin.views.stats.ip'
  8 + th= t'analytics_plugin.views.stats.pages'
8 9  
9   - tbody
10   - - profile.visits.each do |visit|
11   - tr
12   - td= link_to visit.user.name, visit.user.url
13   - td
14   - div data-toggle="tooltip" data-title='#{l visit.initial_time}'
15   - = time_ago_in_words(visit.initial_time)
16   - |&nbsp
17   - = _'ago'
18   - td
19   - - visit.page_views.each do |page_view|
20   - = link_to page_view.url, page_view.url
21   - |&nbsp;
22   - = "(#{distance_of_time_in_words page_view.time_on_page})"
23   - |&nbsp;->&nbsp;
  10 + tbody
  11 + - visits.each do |visit|
  12 + tr data-visit-id='#{visit.id}'
  13 + td= link_to visit.user.name, visit.user.url if visit.user
  14 + td
  15 + div data-toggle="tooltip" data-title='#{l visit.initial_time}'
  16 + = time_ago_in_words visit.initial_time
  17 + |&nbsp
  18 + = _'ago'
  19 + td= visit.users_page_views.first.remote_ip
  20 + td
  21 + ol
  22 + - visit.users_page_views.each do |page_view|
  23 + li
  24 + = link_to (if page_view.title.present? then page_view.title else page_view.url end), page_view.url, target: '_blank'
  25 + |&nbsp;
  26 + = "(#{distance_of_time_in_words page_view.time_on_page})"
24 27  
25 28 javascript:
26 29 $('#analytics-stats').bootstrapTable({
27 30 striped: true,
28   - columns: [
29   - {sortable: true},
30   - {sortable: true},
31   - {sortable: true},
32   - ],
33 31 })
34 32  
35 33 $(document).ready(function() {
... ...
plugins/analytics/views/analytics_plugin/stats/index.html.slim
1   -- content_for :head
2   - = javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table-all.min.js'
3   - = stylesheet_link_tag 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.8.1/bootstrap-table.css'
  1 += render 'shared/bootstrap_table'
  2 +
  3 += button :back, _('Back to control panel'), controller: 'profile_editor'
  4 +
  5 += js_translations_include plugin: :analytics
  6 += javascript_include_tag 'plugins/analytics/javascripts/views/settings'
  7 +analytics-settings data-opts="#{CGI.escapeHTML({settings: {enabled: profile.analytics_settings.enabled, anonymous: profile.analytics_settings.anonymous}}.to_json)}" data-riot=''
  8 +/ needs html_safe to work
  9 +/= riot_component :analytics_settings, settings: {enabled: profile.analytics_settings.enabled, anonymous: profile.analytics_settings.anonymous}
  10 +
  11 += render 'table', visits: profile.users_visits.limit(50)
4 12  
5   -= render 'table'
... ...
plugins/comment_group/test/functional/comment_group_plugin_profile_controller_test.rb
... ... @@ -5,8 +5,6 @@ class CommentGroupPluginProfileControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = CommentGroupPluginProfileController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @profile = create_user('testuser').person
12 10 @article = profile.articles.build(:name => 'test')
... ...
plugins/comment_group/test/functional/comment_group_plugin_public_controller_test.rb
... ... @@ -5,8 +5,6 @@ class CommentGroupPluginPublicControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = CommentGroupPluginPublicController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @profile = create_user('testuser').person
12 10 @article = profile.articles.build(:name => 'test')
... ...
plugins/custom_forms/lib/custom_forms_plugin/helper.rb
... ... @@ -71,7 +71,7 @@ module CustomFormsPlugin::Helper
71 71 def display_custom_field(field, submission, form)
72 72 sanitized_name = ActionView::Base.white_list_sanitizer.sanitize field.name
73 73 answer = submission.answers.select{|answer| answer.field == field}.first
74   - field_tag = send("display_#{type_for_options(field.class)}",field, answer, form)
  74 + field_tag = send("display_#{type_for_options(field.class)}",field, answer, form).html_safe
75 75 if field.mandatory? && submission.id.nil?
76 76 required(labelled_form_field(sanitized_name, field_tag))
77 77 else
... ...
plugins/custom_forms/test/functional/custom_forms_plugin_myprofile_controller_test.rb
... ... @@ -4,8 +4,7 @@ require_relative &#39;../../controllers/custom_forms_plugin_myprofile_controller&#39;
4 4 class CustomFormsPluginMyprofileControllerTest < ActionController::TestCase
5 5 def setup
6 6 @controller = CustomFormsPluginMyprofileController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
  7 +
9 8 @profile = create_user('profile').person
10 9 login_as(@profile.identifier)
11 10 environment = Environment.default
... ...
plugins/custom_forms/test/functional/custom_forms_plugin_profile_controller_test.rb
... ... @@ -4,8 +4,7 @@ require_relative &#39;../../controllers/custom_forms_plugin_profile_controller&#39;
4 4 class CustomFormsPluginProfileControllerTest < ActionController::TestCase
5 5 def setup
6 6 @controller = CustomFormsPluginProfileController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
  7 +
9 8 @profile = create_user('profile').person
10 9 login_as(@profile.identifier)
11 10 environment = Environment.default
... ...
plugins/custom_forms/views/custom_forms_plugin_myprofile/_field.html.erb
... ... @@ -12,7 +12,7 @@
12 12 <%= f.hidden_field(:position) %>
13 13  
14 14 <%= f.hidden_field :_destroy, :class => 'destroy-field' %>
15   - <%= button_to_function :delete, _('Remove field'), "customFormsPlugin.removeFieldBox(this, #{CGI::escapeHTML(_('Are you sure you want to remove this field?').to_json)})" %>
  15 + <%= button_to_function :delete, _('Remove field'), "customFormsPlugin.removeFieldBox(this, #{_('Are you sure you want to remove this field?').to_json})" %>
16 16 <%= yield %>
17 17 </div>
18 18 </fieldset>
... ...
plugins/custom_forms/views/custom_forms_plugin_myprofile/_form.html.erb
... ... @@ -51,8 +51,8 @@
51 51 </ul>
52 52  
53 53 <div class="addition-buttons">
54   - <%= button(:add, _('Add a new text field'), '#', :onclick => "customFormsPlugin.addFields(this, 'fields', #{CGI::escapeHTML(html_for_field(f, :fields, CustomFormsPlugin::TextField).to_json)}); return false")%>
55   - <%= button(:add, _('Add a new select field'), '#', :onclick => "customFormsPlugin.addFields(this, 'fields', #{CGI::escapeHTML(html_for_field(f, :fields, CustomFormsPlugin::SelectField).to_json)}); return false")%>
  54 + <%= button(:add, _('Add a new text field'), '#', :onclick => "customFormsPlugin.addFields(this, 'fields', #{html_for_field(f, :fields, CustomFormsPlugin::TextField).to_json}); return false")%>
  55 + <%= button(:add, _('Add a new select field'), '#', :onclick => "customFormsPlugin.addFields(this, 'fields', #{html_for_field(f, :fields, CustomFormsPlugin::SelectField).to_json}); return false")%>
56 56 </div>
57 57  
58 58 <%= button_bar do %>
... ...
plugins/custom_forms/views/custom_forms_plugin_myprofile/custom_forms_plugin/_alternative.html.erb
... ... @@ -7,6 +7,6 @@
7 7  
8 8 <td>
9 9 <%= f.hidden_field :_destroy, :class => 'destroy-field' %>
10   - <%= button_to_function_without_text :remove, _('Remove alternative'), "customFormsPlugin.removeAlternative(this, #{CGI::escapeHTML(_('Are you sure you want to remove this alternative?').to_json)})", :class => 'remove-field', :title => _('Remove alternative') %>
  10 + <%= button_to_function_without_text :remove, _('Remove alternative'), "customFormsPlugin.removeAlternative(this, #{_('Are you sure you want to remove this alternative?').to_json})", :class => 'remove-field', :title => _('Remove alternative') %>
11 11 </td>
12 12 </tr>
... ...
plugins/custom_forms/views/custom_forms_plugin_myprofile/custom_forms_plugin/_select_field.html.erb
... ... @@ -22,7 +22,7 @@
22 22 <tfoot>
23 23 <tr class="addition-buttons">
24 24 <td colspan="3">
25   - <%= button(:add, _('Add a new alternative'), '#', :onclick => "customFormsPlugin.addFields(this, 'alternatives', #{CGI::escapeHTML(html_for_field(f, :alternatives, CustomFormsPlugin::Alternative).to_json)}); return false") %>
  25 + <%= button(:add, _('Add a new alternative'), '#', :onclick => "customFormsPlugin.addFields(this, 'alternatives', #{html_for_field(f, :alternatives, CustomFormsPlugin::Alternative).to_json}); return false") %>
26 26 </td>
27 27 </tr>
28 28 </tfoot>
... ...
plugins/custom_forms/views/custom_forms_plugin_profile/show.html.erb
1 1 <div id="custom-forms-plugin_submission">
2 2  
3 3 <h1><%= @form.name %></h1>
4   -<p><%= @form.description %></p>
  4 +<p><%= (@form.description || "").html_safe %></p>
5 5  
6 6 <% if @submission.id.nil? %>
7 7 <% if @form.expired? %>
... ...
plugins/custom_forms/views/shared/_form_submission.html.erb
1 1 <% self.extend(CustomFormsPlugin::Helper) %>
2 2  
3 3 <% @form.fields.each do |field| %>
4   - <%= display_custom_field(field, @submission, f.object_name) %>
  4 + <%= display_custom_field(field, @submission, f.object_name).html_safe %>
5 5 <% end %>
... ...
plugins/display_content/test/functional/display_content_plugin_admin_controller_test.rb
... ... @@ -5,8 +5,6 @@ class DisplayContentPluginAdminControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = DisplayContentPluginAdminController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @environment = Environment.default
12 10 user_login = create_admin_user(@environment)
... ...
plugins/display_content/test/functional/display_content_plugin_myprofile_controller_test.rb
... ... @@ -5,8 +5,6 @@ class DisplayContentPluginMyprofileControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = DisplayContentPluginMyprofileController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 user = create_user('testinguser')
12 10 login_as(user.login)
... ...
plugins/driven_signup/test/integration/account_controller_test.rb
... ... @@ -7,8 +7,7 @@ class AccountControllerTest &lt; ActionDispatch::IntegrationTest
7 7  
8 8 def setup
9 9 @controller = AccountController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
  10 + @request = ActionController::TestRequest.new
12 11  
13 12 e = Environment.default
14 13 e.enable 'skip_new_user_email_confirmation', true
... ...
plugins/fb_app/models/fb_app_plugin/page_tab.rb
... ... @@ -9,6 +9,7 @@ class FbAppPlugin::PageTab &lt; ApplicationRecord
9 9  
10 10 belongs_to :owner_profile, foreign_key: :profile_id, class_name: 'Profile'
11 11  
  12 + extend ActsAsHavingSettings::ClassMethods
12 13 acts_as_having_settings field: :config
13 14  
14 15 ConfigTypes = [:profile, :profiles, :query]
... ...
plugins/google_analytics/test/functional/profile_editor_controller_test.rb
... ... @@ -5,8 +5,7 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = ProfileEditorController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
  8 +
10 9 @profile = create_user('default_user').person
11 10 login_as(@profile.identifier)
12 11 Environment.default.enable_plugin(GoogleAnalyticsPlugin.name)
... ...
plugins/google_cse/test/functional/google_cse_plugin_controller_test.rb
... ... @@ -5,8 +5,6 @@ class GoogleCsePluginControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = GoogleCsePluginController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8 end
11 9  
12 10 should 'get results page' do
... ...
plugins/html5_video/test/functional/content_viewer_controler_test.rb
... ... @@ -7,8 +7,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
7 7  
8 8 def setup
9 9 @controller = ContentViewerController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
12 10  
13 11 @profile = create_user('testinguser').person
14 12 @environment = @profile.environment
... ...
plugins/ldap/test/functional/account_controller_plugin_test.rb
... ... @@ -4,8 +4,6 @@ class AccountControllerPluginTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = AccountController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7  
10 8 @environment = Environment.default
11 9 @environment.enabled_plugins = ['LdapPlugin']
... ...
plugins/mark_comment_as_read/test/functional/mark_comment_as_read_plugin_profile_controller_test.rb
... ... @@ -4,8 +4,7 @@ require_relative &#39;../../controllers/mark_comment_as_read_plugin_profile_controll
4 4 class MarkCommentAsReadPluginProfileControllerTest < ActionController::TestCase
5 5 def setup
6 6 @controller = MarkCommentAsReadPluginProfileController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
  7 +
9 8 @profile = create_user('profile').person
10 9 @article = TinyMceArticle.create!(:profile => @profile, :name => 'An article')
11 10 @comment = Comment.new(:source => @article, :author => @profile, :body => 'test')
... ...
plugins/metadata/test/functional/content_viewer_controller_test.rb
... ... @@ -6,8 +6,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
6 6  
7 7 def setup
8 8 @controller = ContentViewerController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
11 9  
12 10 @profile = create_user('testinguser').person
13 11 @environment = @profile.environment
... ...
plugins/metadata/test/functional/home_controller_test.rb
... ... @@ -5,8 +5,6 @@ class HomeControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = HomeController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @environment = Environment.default
12 10 @environment.enabled_plugins += ['MetadataPlugin']
... ...
plugins/metadata/test/functional/products_plugin/page_controller_test.rb
... ... @@ -6,8 +6,6 @@ if defined? ProductsPlugin
6 6  
7 7 def setup
8 8 @controller = PageController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
11 9 @enterprise = fast_create(Enterprise, name: 'test', identifier: 'test_ent')
12 10 @user = create_user_with_permission('test_user', 'manage_products', @enterprise)
13 11 login_as :test_user
... ...
plugins/newsletter/lib/newsletter_plugin/newsletter.rb
... ... @@ -167,6 +167,7 @@ class NewsletterPlugin::Newsletter &lt; ApplicationRecord
167 167 end
168 168 end
169 169  
  170 + extend ActsAsHavingImage::ClassMethods
170 171 acts_as_having_image
171 172  
172 173 def last_send_at
... ...
plugins/newsletter/test/functional/newsletter_plugin_admin_controller_test.rb
... ... @@ -4,8 +4,6 @@ class NewsletterPluginAdminControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = NewsletterPluginAdminController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7  
10 8 @admin = create_user('admin_newsletter').person
11 9 @environment = @admin.environment
... ...
plugins/newsletter/test/functional/newsletter_plugin_controller_test.rb
... ... @@ -4,8 +4,7 @@ class NewsletterPluginControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = NewsletterPluginController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
  7 +
9 8 environment = fast_create(Environment)
10 9 environment.enable_plugin(NewsletterPlugin)
11 10 @controller.stubs(:environment).returns(environment)
... ...
plugins/oauth_client/models/oauth_client_plugin/auth.rb
... ... @@ -10,6 +10,7 @@ class OauthClientPlugin::Auth &lt; ApplicationRecord
10 10 validates_presence_of :provider
11 11 validates_uniqueness_of :profile_id, scope: :provider_id
12 12  
  13 + extend ActsAsHavingSettings::ClassMethods
13 14 acts_as_having_settings field: :data
14 15  
15 16 def expires_in
... ...
plugins/oauth_client/models/oauth_client_plugin/provider.rb
... ... @@ -4,7 +4,10 @@ class OauthClientPlugin::Provider &lt; ApplicationRecord
4 4  
5 5 validates_presence_of :name, :strategy
6 6  
  7 + extend ActsAsHavingImage::ClassMethods
7 8 acts_as_having_image
  9 +
  10 + extend ActsAsHavingSettings::ClassMethods
8 11 acts_as_having_settings field: :options
9 12  
10 13 settings_items :site, type: String
... ... @@ -16,6 +19,4 @@ class OauthClientPlugin::Provider &lt; ApplicationRecord
16 19  
17 20 scope :enabled, -> { where enabled: true }
18 21  
19   - acts_as_having_image
20   -
21 22 end
... ...
plugins/oauth_provider/Gemfile
1   -gem 'doorkeeper', '~> 1.4.0'
  1 +gem 'doorkeeper', '~> 2.2.0'
... ...
plugins/oauth_provider/controllers/doorkeeper/application_controller.rb
... ... @@ -2,7 +2,8 @@ module Doorkeeper
2 2 class ApplicationController < ApplicationController
3 3  
4 4 include Helpers::Controller
5   - helper 'doorkeeper/form_errors'
  5 +
  6 + helper 'doorkeeper/dashboard'
6 7  
7 8 end
8 9 end
... ...
plugins/oauth_provider/controllers/doorkeeper/applications_controller.rb 0 → 100644
... ... @@ -0,0 +1,54 @@
  1 +module Doorkeeper
  2 + class ApplicationsController < Doorkeeper::ApplicationController
  3 + layout 'doorkeeper/admin'
  4 +
  5 + before_action :authenticate_admin!
  6 + before_action :set_application, only: [:show, :edit, :update, :destroy]
  7 +
  8 + def index
  9 + @applications = Application.all
  10 + end
  11 +
  12 + def new
  13 + @application = Application.new
  14 + end
  15 +
  16 + def create
  17 + @application = Application.new(application_params)
  18 + if @application.save
  19 + flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
  20 + redirect_to oauth_application_url(@application)
  21 + else
  22 + render :new
  23 + end
  24 + end
  25 +
  26 + def update
  27 + if @application.update_attributes(application_params)
  28 + flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :update])
  29 + redirect_to oauth_application_url(@application)
  30 + else
  31 + render :edit
  32 + end
  33 + end
  34 +
  35 + def destroy
  36 + flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :destroy]) if @application.destroy
  37 + redirect_to oauth_applications_url
  38 + end
  39 +
  40 + private
  41 +
  42 + def set_application
  43 + @application = Application.find(params[:id])
  44 + end
  45 +
  46 + def application_params
  47 + if params.respond_to?(:permit)
  48 + params.require(:doorkeeper_application).permit(:name, :redirect_uri, :scopes)
  49 + else
  50 + params[:doorkeeper_application].slice(:name, :redirect_uri, :scopes) rescue nil
  51 + end
  52 + end
  53 + end
  54 +end
... ...
plugins/oauth_provider/controllers/oauth_provider_authorizations_controller.rb
... ... @@ -6,4 +6,7 @@ class OauthProviderAuthorizationsController &lt; Doorkeeper::AuthorizationsControll
6 6 def index
7 7 end
8 8  
  9 + def show
  10 + end
  11 +
9 12 end
... ...
plugins/oauth_provider/db/migrate/20140829153047_create_doorkeeper_tables.rb
... ... @@ -1,41 +0,0 @@
1   -class CreateDoorkeeperTables < ActiveRecord::Migration
2   - def change
3   - create_table :oauth_applications do |t|
4   - t.string :name, null: false
5   - t.string :uid, null: false
6   - t.string :secret, null: false
7   - t.text :redirect_uri, null: false
8   - t.timestamps
9   - end
10   -
11   - add_index :oauth_applications, :uid, unique: true
12   -
13   - create_table :oauth_access_grants do |t|
14   - t.integer :resource_owner_id, null: false
15   - t.integer :application_id, null: false
16   - t.string :token, null: false
17   - t.integer :expires_in, null: false
18   - t.text :redirect_uri, null: false
19   - t.datetime :created_at, null: false
20   - t.datetime :revoked_at
21   - t.string :scopes
22   - end
23   -
24   - add_index :oauth_access_grants, :token, unique: true
25   -
26   - create_table :oauth_access_tokens do |t|
27   - t.integer :resource_owner_id
28   - t.integer :application_id
29   - t.string :token, null: false
30   - t.string :refresh_token
31   - t.integer :expires_in
32   - t.datetime :revoked_at
33   - t.datetime :created_at, null: false
34   - t.string :scopes
35   - end
36   -
37   - add_index :oauth_access_tokens, :token, unique: true
38   - add_index :oauth_access_tokens, :resource_owner_id
39   - add_index :oauth_access_tokens, :refresh_token, unique: true
40   - end
41   -end
plugins/oauth_provider/db/migrate/20170718153047_create_doorkeeper_tables.rb 0 → 100644
... ... @@ -0,0 +1,42 @@
  1 +class CreateDoorkeeperTables < ActiveRecord::Migration
  2 + def change
  3 + create_table :oauth_applications do |t|
  4 + t.string :name, null: false
  5 + t.string :uid, null: false
  6 + t.string :secret, null: false
  7 + t.text :redirect_uri, null: false
  8 + t.string :scopes, null: false, default: ''
  9 + t.timestamps
  10 + end
  11 +
  12 + add_index :oauth_applications, :uid, unique: true
  13 +
  14 + create_table :oauth_access_grants do |t|
  15 + t.integer :resource_owner_id, null: false
  16 + t.integer :application_id, null: false
  17 + t.string :token, null: false
  18 + t.integer :expires_in, null: false
  19 + t.text :redirect_uri, null: false
  20 + t.datetime :created_at, null: false
  21 + t.datetime :revoked_at
  22 + t.string :scopes
  23 + end
  24 +
  25 + add_index :oauth_access_grants, :token, unique: true
  26 +
  27 + create_table :oauth_access_tokens do |t|
  28 + t.integer :resource_owner_id
  29 + t.integer :application_id
  30 + t.string :token, null: false
  31 + t.string :refresh_token
  32 + t.integer :expires_in
  33 + t.datetime :revoked_at
  34 + t.datetime :created_at, null: false
  35 + t.string :scopes
  36 + end
  37 +
  38 + add_index :oauth_access_tokens, :token, unique: true
  39 + add_index :oauth_access_tokens, :resource_owner_id
  40 + add_index :oauth_access_tokens, :refresh_token, unique: true
  41 + end
  42 +end
... ...
plugins/oauth_provider/views/doorkeeper/applications/_delete_form.html.erb
1 1 <%- submit_btn_css ||= 'btn btn-link' %>
2   -<%= form_tag [:oauth, application] do %>
  2 +<%= form_tag oauth_application_path(application) do %>
3 3 <input type="hidden" name="_method" value="delete">
4   - <%= submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css %>
  4 + <%= submit_tag t('doorkeeper.applications.buttons.destroy'), onclick: "return confirm('#{ t('doorkeeper.applications.confirmations.destroy') }')", class: submit_btn_css %>
5 5 <% end %>
... ...
plugins/oauth_provider/views/doorkeeper/applications/_form.html.erb
1   -<%= form_for [:oauth, application], html: {class: 'form-horizontal', role: 'form'} do |f| %>
  1 +<%= form_for application, url: doorkeeper_submit_path(application), html: {class: 'form-horizontal', role: 'form'} do |f| %>
2 2 <% if application.errors.any? %>
3 3 <div class="alert alert-danger" data-alert>
4 4 <p><%= _('Whoops! Check your form for possible errors') %></p>
... ...
plugins/oauth_provider/views/doorkeeper/applications/index.html.erb
... ... @@ -17,15 +17,15 @@
17 17 <tbody>
18 18 <% @applications.each do |application| %>
19 19 <tr id="application_<%= application.id %>">
20   - <td><%= link_to application.name, [:oauth, application] %></td>
  20 + <td><%= link_to application.name, oauth_application_path(application) %></td>
21 21 <td><%= application.redirect_uri %></td>
22   - <td><%= link_to _('Edit'), edit_oauth_application_path(application), class: 'btn btn-link' %></td>
  22 + <td><%= link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(application), class: 'btn btn-link' %></td>
23 23 <td><%= render 'delete_form', application: application %></td>
24 24 </tr>
25 25 <% end %>
26 26 </tbody>
27 27 </table>
28   -<div class="actions">
  28 +<div class="actions" style="padding-top: 5px;">
29 29 <%= button(:back, _('Go back'), {:controller => 'oauth_provider_plugin_admin', :action => 'index'}) %>
30 30 </div>
31 31 </div>
... ...
plugins/oauth_provider/views/doorkeeper/applications/show.html.erb
... ... @@ -20,7 +20,8 @@
20 20 <td>
21 21 <code><%= uri %></code>
22 22 </td>
23   - <td>
  23 + <td>
  24 + <%= link_to t('doorkeeper.applications.buttons.authorize'), oauth_authorization_path(client_id: @application.uid, redirect_uri: uri, response_type: 'code', scope: @application.scopes), class: 'btn btn-success', target: '_blank' %>
24 25 </td>
25 26 </tr>
26 27 <% end %>
... ...
plugins/oauth_provider/views/doorkeeper/authorizations/new.html.erb
... ... @@ -6,16 +6,16 @@
6 6  
7 7 <main role="main">
8 8 <p class="h4">
9   - <%= _('Authorize %s to use your account?') % "<strong class=\"text-info\">#{@pre_auth.client.name}</strong>" %>
  9 + <%= raw _('Authorize %s to use your account?') % "<strong class=\"text-info\">#{@pre_auth.client.name}</strong>" %>
10 10 </p>
11 11  
12   - <% if @pre_auth.scopes %>
  12 + <% if @pre_auth.scopes.count > 0 %>
13 13 <div id="oauth-permissions">
14 14 <p><%= _('This application will be able to:') %></p>
15 15  
16 16 <ul class="text-info">
17 17 <% @pre_auth.scopes.each do |scope| %>
18   - <li><%= OauthProviderPlugin::SCOPE_TRANSLATION[scope] %></li>
  18 + <li><%= t scope, scope: [:doorkeeper, :scopes] %></li>
19 19 <% end %>
20 20 </ul>
21 21 </div>
... ...
plugins/oauth_provider/views/doorkeeper/authorized_applications/index.html.erb
... ... @@ -25,7 +25,7 @@
25 25 </table>
26 26 </main>
27 27  
28   -<div class="actions">
  28 +<div class="actions" style="padding-top: 5px;">
29 29 <%= button(:back, _('Go back'), :back) %>
30 30 </div>
31 31 </div>
... ...
plugins/open_graph/test/functional/open_graph_graph/my_profile_controller_test.rb
... ... @@ -8,8 +8,7 @@ class OpenGraphPlugin::MyprofileControllerTest &lt; ActionController::TestCase
8 8  
9 9 def setup
10 10 @controller = OpenGraphPlugin::MyprofileController.new
11   - @request = ActionController::TestRequest.new
12   - @response = ActionController::TestResponse.new
  11 +
13 12 @actor = create_user.person
14 13 end
15 14  
... ...
plugins/organization_ratings/lib/create_organization_rating_comment.rb
... ... @@ -14,6 +14,12 @@ class CreateOrganizationRatingComment &lt; Task
14 14 settings_items field.to_sym
15 15 end
16 16  
  17 + scope :with_rating, -> (user_rating){
  18 + CreateOrganizationRatingComment.find_each do |task|
  19 + return task if(task.organization_rating_id == user_rating.id)
  20 + end
  21 + }
  22 +
17 23 def perform
18 24 if (self.body && !self.body.blank?)
19 25 comment = Comment.create!(:source => self.target, :body => self.body, :author => self.requestor)
... ...
plugins/organization_ratings/test/functional/organization_ratings_plugin_admin_controller_test.rb
... ... @@ -8,8 +8,6 @@ class OrganizationRatingsPluginAdminControllerTest &lt; ActionController::TestCase
8 8  
9 9 def setup
10 10 @controller = OrganizationRatingsPluginAdminController.new
11   - @request = ActionController::TestRequest.new
12   - @response = ActionController::TestResponse.new
13 11  
14 12 @environment = Environment.default
15 13 @environment.enabled_plugins = ['OrganizationRatingsPlugin']
... ...
plugins/organization_ratings/test/functional/organization_ratings_plugin_profile_controller_test.rb
... ... @@ -8,8 +8,6 @@ class OrganizationRatingsPluginProfileControllerTest &lt; ActionController::TestCas
8 8  
9 9 def setup
10 10 @controller = OrganizationRatingsPluginProfileController.new
11   - @request = ActionController::TestRequest.new
12   - @response = ActionController::TestResponse.new
13 11  
14 12 @environment = Environment.default
15 13 @environment.enabled_plugins = ['OrganizationRatingsPlugin']
... ... @@ -148,6 +146,39 @@ class OrganizationRatingsPluginProfileControllerTest &lt; ActionController::TestCas
148 146  
149 147 get :new_rating, profile: @community.identifier
150 148 assert_tag :tag => 'p', :content => /Report waiting for approval/, :attributes => {:class =>/moderation-msg/}
  149 + assert_tag :tag => 'p', :attributes => {:class =>/comment-body/}
  150 + end
  151 +
  152 + test "display rejected comment to env admin" do
  153 + post :new_rating, profile: @community.identifier, :comments => {:body => "rejected comment"}, :organization_rating_value => 3
  154 +
  155 + @admin = create_admin_user(@environment)
  156 + login_as @admin
  157 + @controller.stubs(:current_user).returns(Profile[@admin].user)
  158 +
  159 + CreateOrganizationRatingComment.last.cancel
  160 +
  161 + get :new_rating, profile: @community.identifier
  162 + assert_tag :tag => 'p', :attributes => {:class =>/comment-body/}, :content => /rejected comment/
  163 + end
  164 +
  165 + test "not display rejected comment to regular user" do
  166 + p1 = create_user('regularUser').person
  167 + @community.add_member p1
  168 + login_as(p1.identifier)
  169 + @controller.stubs(:logged_in?).returns(true)
  170 + @controller.stubs(:current_user).returns(p1.user)
  171 +
  172 + post :new_rating, profile: @community.identifier, :comments => {:body => "rejected comment"}, :organization_rating_value => 3
  173 + CreateOrganizationRatingComment.last.cancel
  174 + get :new_rating, profile: @community.identifier
  175 + assert_no_tag :tag => 'p', :attributes => {:class =>/comment-body/}
  176 + end
  177 +
  178 + test "not display rejected comment to community admin" do
  179 + post :new_rating, profile: @community.identifier, :comments => {:body => "rejected comment"}, :organization_rating_value => 3
  180 + CreateOrganizationRatingComment.last.cancel
  181 + get :new_rating, profile: @community.identifier
151 182 assert_no_tag :tag => 'p', :attributes => {:class =>/comment-body/}
152 183 end
153 184  
... ...
plugins/organization_ratings/views/shared/_user_rating_container.html.erb
... ... @@ -29,6 +29,9 @@
29 29 <%= status_message_for(user, user_rate) %>
30 30 <% if user_rate.comment.present? %>
31 31 <p class="comment-body"> <%= user_rate.comment.body %> </p>
  32 + <% elsif user && user.is_admin? %>
  33 + <% rating_task = CreateOrganizationRatingComment.with_rating(user_rate) %>
  34 + <p class="comment-body"> <%= rating_task.body %> </p>
32 35 <% end %>
33 36 </div>
34 37 <%= @plugins.dispatch(:organization_ratings_plugin_container_extra_fields, user_rate).collect { |content| instance_exec(&content) }.join("") %>
... ...
plugins/people_block/test/functional/people_block_plugin_environment_design_controller_test.rb
... ... @@ -4,8 +4,7 @@ class EnvironmentDesignControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = EnvironmentDesignController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
  7 +
9 8 Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([PeopleBlockPlugin.new])
10 9 end
11 10  
... ...
plugins/people_block/test/functional/people_block_plugin_profile_design_controller_test.rb
... ... @@ -4,8 +4,7 @@ class ProfileDesignControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = ProfileDesignController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
  7 +
9 8 Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([PeopleBlockPlugin.new])
10 9 end
11 10  
... ...
plugins/people_block/test/functional/profile_controller_test.rb
... ... @@ -4,8 +4,7 @@ class ProfileControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = ProfileController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
  7 +
9 8 Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([PeopleBlockPlugin.new])
10 9 end
11 10  
... ...
plugins/products/models/products_plugin/product.rb
... ... @@ -45,6 +45,7 @@ class ProductsPlugin::Product &lt; ApplicationRecord
45 45 has_many :qualifiers, through: :product_qualifiers
46 46 has_many :certifiers, through: :product_qualifiers
47 47  
  48 + extend ActsAsHavingSettings::ClassMethods
48 49 acts_as_having_settings field: :data
49 50  
50 51 track_actions :create_product, :after_create, keep_params: [:name, :url ], if: Proc.new { |a| a.is_trackable? }, custom_user: :action_tracker_user
... ... @@ -129,6 +130,7 @@ class ProductsPlugin::Product &lt; ApplicationRecord
129 130 image ? image.public_filename(size) : '/images/icons-app/product-default-pic-%s.png' % size
130 131 end
131 132  
  133 + extend ActsAsHavingImage::ClassMethods
132 134 acts_as_having_image
133 135  
134 136 def save_image
... ...
plugins/recent_activities/lib/recent_activities_plugin.rb 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +class RecentActivitiesPlugin < Noosfero::Plugin
  2 + def self.plugin_name
  3 + 'RecentActivitiesPlugin'
  4 + end
  5 +
  6 + def self.plugin_description
  7 + _('Adds a block that lists recent profile activity.')
  8 + end
  9 +
  10 + def self.extra_blocks
  11 + {
  12 + RecentActivitiesPlugin::ActivitiesBlock => { type: [Community, Person] }
  13 + }
  14 + end
  15 +
  16 + def self.has_admin_url?
  17 + false
  18 + end
  19 +
  20 + def stylesheet?
  21 + true
  22 + end
  23 +end
  24 +
  25 +ApplicationHelper.include ActionTrackerHelper
... ...
plugins/recent_activities/lib/recent_activities_plugin/activities_block.rb 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +class RecentActivitiesPlugin::ActivitiesBlock < Block
  2 + attr_accessible :limit
  3 + settings_items :limit, type: :integer, default: 5
  4 +
  5 + def view_title
  6 + self.default_title
  7 + end
  8 +
  9 + def activities
  10 + activities = owner.activities.where(activity_type: ActionTracker::Record.to_s)
  11 + list = self.limit.nil? ? activities : activities.limit(self.get_limit)
  12 + list.map(&:activity)
  13 + end
  14 +
  15 + def extra_option
  16 + { }
  17 + end
  18 +
  19 + def self.description
  20 + _('Display the latest activities by the owner of the context where the block is available.')
  21 + end
  22 +
  23 + def help
  24 + _('This block lists your latest activities. By default, any user that goes to your profile will be able to see all activities. Configure the "Display to users" option if you don\'t want that.')
  25 + end
  26 +
  27 + def default_title
  28 + _('Recent activities')
  29 + end
  30 +
  31 + def api_content
  32 + Api::Entities::Activity.represent(activities).as_json
  33 + end
  34 +
  35 + def display_api_content_by_default?
  36 + false
  37 + end
  38 +
  39 + def timeout
  40 + 4.hours
  41 + end
  42 +
  43 + def self.expire_on
  44 + { profile: [:article] }
  45 + end
  46 +end
... ...
plugins/recent_activities/public/style.css 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +.recent-activities-block ul {
  2 + padding: 0;
  3 +}
  4 +
  5 +.recent-activities-block li {
  6 + list-style: none;
  7 + border-bottom: 1px solid #ccc;
  8 +}
  9 +
  10 +.recent-activities-block time,
  11 +.recent-activities-block p {
  12 + margin: 10px 0;
  13 +}
  14 +
  15 +.recent-activities-block time {
  16 + display: block;
  17 + margin-bottom: 10px;
  18 + color: #ccc;
  19 +}
  20 +
  21 +.recent-activities-block img {
  22 + padding: 1px;
  23 + border: 1px solid #ccc;
  24 + margin: 3px 3px 0 0;
  25 +}
  26 +
  27 +.recent-activities-block p:first-letter {
  28 + text-transform: capitalize
  29 +}
... ...
plugins/recent_activities/test/test_helper.rb 0 → 100644
... ... @@ -0,0 +1 @@
  1 +require_relative '../../../test/test_helper'
... ...
plugins/recent_activities/test/unit/recent_activities_block_test.rb 0 → 100644
... ... @@ -0,0 +1,73 @@
  1 +require_relative '../test_helper'
  2 +
  3 +class RecentActivitiesBlockTest < ActiveSupport::TestCase
  4 + should 'describe itself' do
  5 + assert_not_equal Block.description, RecentActivitiesPlugin::ActivitiesBlock.description
  6 + end
  7 +
  8 + should 'is editable' do
  9 + block = RecentActivitiesPlugin::ActivitiesBlock.new
  10 + assert block.editable?
  11 + end
  12 +
  13 + should 'return last activities' do
  14 + profile = create_user('testuser').person
  15 + a1 = fast_create(ActionTracker::Record, user_id: profile.id, created_at: Time.now, updated_at: Time.now)
  16 + a2 = fast_create(ActionTracker::Record, user_id: profile.id, created_at: Time.now, updated_at: Time.now)
  17 + ProfileActivity.create! profile_id: profile.id, activity: a1
  18 + ProfileActivity.create! profile_id: profile.id, activity: a2
  19 +
  20 + block = RecentActivitiesPlugin::ActivitiesBlock.new
  21 + block.stubs(:owner).returns(profile)
  22 +
  23 + assert_equal [a2, a1].map(&:id), block.activities.map(&:id)
  24 + end
  25 +
  26 + should 'return last activities with limit' do
  27 + profile = create_user('testuser').person
  28 + a1 = fast_create(ActionTracker::Record, user_id: profile.id, created_at: Time.now, updated_at: Time.now)
  29 + a2 = fast_create(ActionTracker::Record, user_id: profile.id, created_at: Time.now, updated_at: Time.now)
  30 + ProfileActivity.create! profile_id: profile.id, activity: a1
  31 + ProfileActivity.create! profile_id: profile.id, activity: a2
  32 +
  33 + block = RecentActivitiesPlugin::ActivitiesBlock.new
  34 + block.stubs(:owner).returns(profile)
  35 + block.limit = 1
  36 +
  37 + assert_equal [a2].map(&:id), block.activities.map(&:id)
  38 + end
  39 +
  40 + should 'return only action tracker records as activities' do
  41 + profile = create_user('testuser').person
  42 + friend = create_user('friend').person
  43 + scrap = create(Scrap, defaults_for_scrap(sender: friend, receiver: profile))
  44 + a1 = fast_create(ActionTracker::Record, user_id: profile.id, created_at: Time.now, updated_at: Time.now)
  45 + a2 = fast_create(ActionTracker::Record, user_id: profile.id, created_at: Time.now, updated_at: Time.now)
  46 + ProfileActivity.create! profile_id: profile.id, activity: a1
  47 + ProfileActivity.create! profile_id: profile.id, activity: a2
  48 +
  49 + block = RecentActivitiesPlugin::ActivitiesBlock.new
  50 + block.stubs(:owner).returns(profile)
  51 +
  52 + assert_equal [a2, a1, scrap], block.owner.activities.map(&:activity)
  53 + assert_equal [a2, a1], block.activities
  54 + end
  55 +end
  56 +
  57 +require 'boxes_helper'
  58 +
  59 +class RecentActivitiesBlockViewTest < ActionView::TestCase
  60 + include BoxesHelper
  61 +
  62 + should 'return activities in api_content' do
  63 + profile = create_user('testuser').person
  64 +
  65 + a = fast_create(ActionTracker::Record, user_id: profile.id, created_at: Time.now, updated_at: Time.now)
  66 + ProfileActivity.create! profile_id: profile.id, activity: a
  67 +
  68 + block = RecentActivitiesPlugin::ActivitiesBlock.new
  69 + block.stubs(:owner).returns(profile)
  70 +
  71 + assert_equal [a.id], block.api_content['activities'].map{ |a| a[:id] }
  72 + end
  73 +end
... ...
plugins/recent_activities/views/blocks/activities.html.erb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +<%= block_title(block.view_title, block.subtitle) %>
  2 +
  3 +<div class="recent-activities-block">
  4 + <% unless block.activities.size == 0 %>
  5 + <ul>
  6 + <% block.activities.each do |activity| %>
  7 + <li>
  8 + <p><%= describe(activity).html_safe %></p>
  9 + <time datetime="<%= activity.created_at %>"><%= time_ago_in_words(activity.created_at) %></time>
  10 + </li>
  11 + <% end %>
  12 + </ul>
  13 + <% else %>
  14 + <div class="recent-activities-block-none"><%= c_('None') %></div>
  15 + <% end %>
  16 +</div>
... ...
plugins/recent_activities/views/box_organizer/recent_activities_plugin/_activities_block.html.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<div id="activities_block_plugin">
  2 + <%= labelled_form_field(_('Limit'), text_field(:block, :limit, size: 3)) %>
  3 +</div>
... ...
plugins/recent_activities/views/environment_design 0 → 120000
... ... @@ -0,0 +1 @@
  1 +box_organizer/
0 2 \ No newline at end of file
... ...
plugins/recent_activities/views/profile_design 0 → 120000
... ... @@ -0,0 +1 @@
  1 +box_organizer/
0 2 \ No newline at end of file
... ...
plugins/relevant_content/test/unit/article.rb
... ... @@ -8,8 +8,7 @@ class RelevantContentBlockTest &lt; ActiveSupport::TestCase
8 8  
9 9 def setup
10 10 @controller = CommentController.new
11   - @request = ActionController::TestRequest.new
12   - @response = ActionController::TestResponse.new
  11 +
13 12 @profile = create_user('testinguser').person
14 13 @environment = @profile.environment
15 14 end
... ...
plugins/relevant_content/test/unit/relevant_content_block_test.rb
... ... @@ -9,8 +9,6 @@ class RelevantContentBlockTest &lt; ActiveSupport::TestCase
9 9  
10 10 def setup
11 11 @controller = CommentController.new
12   - @request = ActionController::TestRequest.new
13   - @response = ActionController::TestResponse.new
14 12  
15 13 @profile = create_user('testinguser').person
16 14 @environment = @profile.environment
... ...
plugins/remote_user/test/functional/remote_user_plugin_test.rb
... ... @@ -2,6 +2,8 @@ require &#39;test_helper&#39;
2 2  
3 3 class AccountControllerTest < ActionController::TestCase
4 4 def setup
  5 + @controller = AccountController.new
  6 +
5 7 @environment = Environment.default
6 8 @environment.enabled_plugins = ['RemoteUserPlugin']
7 9 @environment.save
... ... @@ -9,10 +11,6 @@ class AccountControllerTest &lt; ActionController::TestCase
9 11 @another_environment = Environment.new(name: "AnotherEnvironment")
10 12 @another_environment.enabled_plugins = ['RemoteUserPlugin']
11 13 @another_environment.save
12   -
13   - @controller = AccountController.new
14   - @request = ActionController::TestRequest.new
15   - @response = ActionController::TestResponse.new
16 14 end
17 15  
18 16 should 'not authenticate user if there is no remote user' do
... ...
plugins/responsive/views/layouts/_usermenu_logged_in.html.slim
... ... @@ -10,7 +10,7 @@ li.dropdown
10 10  
11 11 ul class='dropdown-menu' role='menu'
12 12 li
13   - = link_to user.public_profile_url, id: "homepage-link", title: _('Go to your homepage')
  13 + = link_to user.url, id: 'homepage-link', title: _('Go to your homepage')
14 14 span class='icon-person' = _('Profile')
15 15 li.divider
16 16  
... ...
plugins/shopping_cart/db/migrate/20131226125124_move_shopping_cart_purchase_order_to_orders_plugin_order.rb
... ... @@ -2,6 +2,7 @@ OrdersPlugin.send :remove_const, :Item if defined? OrdersPlugin::Item
2 2 OrdersPlugin.send :remove_const, :Order if defined? OrdersPlugin::Order
3 3  
4 4 class ShoppingCartPlugin::PurchaseOrder < ApplicationRecord
  5 + extend ActsAsHavingSettings::ClassMethods
5 6 acts_as_having_settings field: :data
6 7  
7 8 module Status
... ...
plugins/shopping_cart/test/functional/shopping_cart_plugin_controller_test.rb
... ... @@ -5,8 +5,7 @@ class ShoppingCartPluginControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = ShoppingCartPluginController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
  8 +
10 9 @profile = fast_create(Enterprise)
11 10 @profile.contact_email = 'enterprise@noosfero.org';@profile.save
12 11 @product = fast_create(Product, profile_id: @profile.id)
... ...
plugins/social_share_privacy/test/functional/content_viewer_controller_test.rb
... ... @@ -5,8 +5,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = ContentViewerController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @profile = create_user('testinguser').person
12 10 @environment = @profile.environment
... ...
plugins/solr/test/functional/search_controller_test.rb
... ... @@ -4,14 +4,12 @@ require_relative &#39;../../lib/ext/facets_browse&#39;
4 4 class SearchControllerTest < ActionController::TestCase
5 5  
6 6 def setup
  7 + @controller = SearchController.new
  8 +
7 9 TestSolr.enable
8 10 p1 = File.join(RAILS_ROOT, 'app', 'views')
9 11 p2 = File.join(File.dirname(__FILE__) + '/../../views')
10 12 SearchController.append_view_path([p1,p2])
11   - @controller = SearchController.new
12   - @request = ActionController::TestRequest.new
13   - @request.stubs(:ssl?).returns(false)
14   - @response = ActionController::TestResponse.new
15 13  
16 14 @category = Category.create!(:name => 'my category', :environment => Environment.default)
17 15  
... ...
plugins/spaminator/lib/spaminator_plugin/mailer.rb
... ... @@ -2,14 +2,14 @@ class SpaminatorPlugin::Mailer &lt; Noosfero::Plugin::MailerBase
2 2  
3 3 include Rails.application.routes.url_helpers
4 4 def inactive_person_notification(person)
  5 + @person = person
  6 + @environment = person.environment
  7 + @url = url_for(:host => person.default_hostname, :controller => 'account', :action => 'forgot_password')
5 8 mail(
6 9 :to => person.email,
7 10 :from => "#{person.environment.name} <#{person.environment.noreply_email}>",
8 11 :subject => _("[%s] You must reactivate your account.") % person.environment.name,
9 12 :content_type => 'text/html',
10   - :body => {:person => person,
11   - :environment => person.environment,
12   - :url => url_for(:host => person.default_hostname, :controller => 'account', :action => 'forgot_password')}
13 13 )
14 14 end
15 15  
... ...
plugins/spaminator/test/functional/spaminator_plugin_admin_controller_test.rb
... ... @@ -4,8 +4,7 @@ require_relative &#39;../../controllers/spaminator_plugin_admin_controller&#39;
4 4 class SpaminatorPluginAdminControllerTest < ActionController::TestCase
5 5 def setup
6 6 @controller = SpaminatorPluginAdminController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
  7 +
9 8 @environment = Environment.default
10 9 @settings = Noosfero::Plugin::Settings.new(@environment, SpaminatorPlugin)
11 10 login_as(create_admin_user(@environment))
... ...
plugins/spaminator/views/spaminator_plugin/mailer/inactive_person_notification.html.erb
... ... @@ -10,7 +10,7 @@
10 10 <%= _('Due to the recent increase in the number of spams on %s, we decided to deactivate all inactive users on the network. It just happens that you are one of them! But there is no need to worry. Your account is completely preserved and if you want to reactivate it you just need to click on the following link and recover your password (since it was changed to a huge scramble of random characters):') % @environment.name %>
11 11 </p>
12 12 <p>
13   - <%= @url %>
  13 + <a href="<%= @url %>"><%= @url %></a>
14 14 </p>
15 15 <p>
16 16 <%= _("We are sorry that this procedure might bother you a lot, but it's necessary for the healthy of our network.") %>
... ...
plugins/stoa/test/functional/account_controller_test.rb
... ... @@ -19,8 +19,6 @@ class AccountControllerTest &lt; ActionController::TestCase
19 19  
20 20 def setup
21 21 @controller = AccountController.new
22   - @request = ActionController::TestRequest.new
23   - @response = ActionController::TestResponse.new
24 22 StoaPlugin::UspUser.create!({:codpes => 12345678, :cpf => Digest::MD5.hexdigest(SALT+'12345678'), :birth_date => '1970-01-30'}, :without_protection => true)
25 23 Environment.default.enable_plugin(StoaPlugin.name)
26 24 @user = create_user('joao-stoa', {:password => 'pass', :password_confirmation => 'pass'},:usp_id=>'87654321')
... ...
plugins/stoa/test/functional/invite_controller_test.rb
... ... @@ -5,8 +5,7 @@ class InviteControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = InviteController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
  8 +
10 9 environment = Environment.default
11 10 environment.enabled_plugins = ['StoaPlugin']
12 11 environment.save!
... ...
plugins/stoa/test/functional/profile_editor_controller_test.rb
... ... @@ -7,8 +7,7 @@ class StoaPluginProfileEditorControllerTest &lt; ActionController::TestCase
7 7  
8 8 def setup
9 9 @controller = ProfileEditorController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
  10 +
12 11 @person = User.create(:login => 'test_user', :email => 'test_user@example.com', :password => 'test', :password_confirmation => 'test').person
13 12 login_as(@person.identifier)
14 13 Environment.default.enable_plugin(StoaPlugin.name)
... ...
plugins/stoa/test/functional/stoa_plugin_controller_test.rb
... ... @@ -7,8 +7,6 @@ class StoaPluginControllerTest &lt; ActionController::TestCase
7 7  
8 8 def setup
9 9 @controller = StoaPluginController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
12 10 ApplicationRecord.configurations['stoa'] = {:adapter => 'sqlite3', :database => ':memory:', :verbosity => 'quiet'}
13 11 env = Environment.default
14 12 env.enable_plugin(StoaPlugin.name)
... ...
plugins/sub_organizations/test/functional/sub_organizations_plugin_myprofile_controller_test.rb
... ... @@ -7,8 +7,7 @@ class SubOrganizationsPluginMyprofileController; def rescue_action(e) raise e en
7 7 class SubOrganizationsPluginMyprofileControllerTest < ActionController::TestCase
8 8 def setup
9 9 @controller = SubOrganizationsPluginMyprofileController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
  10 +
12 11 @organization = Organization.create!(:name => 'My Organization', :identifier => 'my-organization')
13 12 @person = create_user('person').person
14 13 @organization.add_admin(@person)
... ...
plugins/sub_organizations/test/functional/sub_organizations_plugin_profile_controller_test.rb
... ... @@ -8,8 +8,7 @@ class SubOrganizationsPluginProfileControllerTest &lt; ActionController::TestCase
8 8  
9 9 def setup
10 10 @controller = SubOrganizationsPluginProfileController.new
11   - @request = ActionController::TestRequest.new
12   - @response = ActionController::TestResponse.new
  11 +
13 12 @organization = Organization.create!(:name => 'My Organization', :identifier => 'my-organization')
14 13 @person = create_user('person').person
15 14 @organization.add_admin(@person)
... ...
plugins/video/lib/video_plugin/video.rb
1   -require 'noosfero/translatable_content'
2 1 require 'application_helper'
3 2 require 'net/http'
4 3  
... ... @@ -12,8 +11,8 @@ class VideoPlugin::Video &lt; Article
12 11 settings_items :video_format, :type => :string
13 12 settings_items :video_id, :type => :string
14 13 settings_items :video_thumbnail_url, :type => :string, :default => '/plugins/video/images/video_generic_thumbnail.jpg'
15   - settings_items :video_thumbnail_width, :type=> :integer
16   - settings_items :video_thumbnail_height, :type=> :integer
  14 + settings_items :video_thumbnail_width, :type=> :integer, :default => 239
  15 + settings_items :video_thumbnail_height, :type=> :integer, :default => 210
17 16 settings_items :video_duration, :type=> :integer, :default => 0
18 17  
19 18 attr_accessible :video_url
... ... @@ -35,14 +34,14 @@ class VideoPlugin::Video &lt; Article
35 34 def self.description
36 35 _('Display embedded videos.')
37 36 end
38   -
  37 +
39 38 def is_youtube?
40 39 VideoPlugin::Video.is_youtube?(self.video_url)
41 40 end
42 41  
43 42 def is_vimeo?
44 43 VideoPlugin::Video.is_vimeo?(self.video_url)
45   - end
  44 + end
46 45  
47 46 include ActionView::Helpers::TagHelper
48 47 def to_html(options={})
... ...
plugins/video/lib/video_plugin/video_gallery.rb
... ... @@ -14,7 +14,8 @@ class VideoPlugin::VideoGallery &lt; Folder
14 14 errors.add(:parent, "A video gallery should not belong to a blog.") if parent && parent.blog?
15 15 end
16 16  
17   - acts_as_having_settings :field => :setting
  17 + extend ActsAsHavingSettings::ClassMethods
  18 + acts_as_having_settings field: :setting
18 19  
19 20 xss_terminate :only => [ :body ], :with => 'white_list', :on => 'validation'
20 21  
... ...
plugins/video/test/functional/video_plugin_environment_design_controller_test.rb
... ... @@ -4,8 +4,6 @@ class EnvironmentDesignControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = EnvironmentDesignController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7  
10 8 Environment.delete_all
11 9 User.delete_all
... ...
plugins/video/test/functional/video_plugin_profile_design_controller_test.rb
... ... @@ -4,8 +4,6 @@ class ProfileDesignControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = ProfileDesignController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7  
10 8 user = create_user('testinguser')
11 9 login_as(user.login)
... ...
plugins/video/test/unit/video_test.rb
... ... @@ -46,6 +46,11 @@ class VideoTest &lt; ActiveSupport::TestCase
46 46 assert_equal thumbnail_fitted_height, @video.thumbnail_fitted_height
47 47 end
48 48  
  49 + should "have dimensions of default thumbnail" do
  50 + assert @video.video_thumbnail_height.present? && @video.video_thumbnail_height > 0
  51 + assert @video.video_thumbnail_width.present? && @video.video_thumbnail_width > 0
  52 + end
  53 +
49 54 should "show a no_browser_support_message" do
50 55 assert_equal @video.no_browser_support_message, '<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>'
51 56 end
... ...
plugins/work_assignment/lib/work_assignment_plugin/email_contact.rb
... ... @@ -50,13 +50,13 @@ class WorkAssignmentPlugin::EmailContact
50 50 mail(options)
51 51 end
52 52  
53   - def build_mail_message(email_contact, uploaded_files)
  53 + def self.build_mail_message(email_contact, uploaded_files)
54 54 message = ""
55 55 if uploaded_files && uploaded_files.first && uploaded_files.first.parent && uploaded_files.first.parent.parent
56 56 article = uploaded_files.first.parent.parent
57 57 message = article.default_email + "<br>"
58 58 uploaded_files.each do |file|
59   - url = url_for(file.url)
  59 + url = Rails.application.routes.url_helpers.url_for(file.url)
60 60 message += "<br><a href='#{url}'>#{url}</a>"
61 61 end
62 62 end
... ...
plugins/work_assignment/lib/work_assignment_plugin/helper.rb
... ... @@ -16,7 +16,7 @@ module WorkAssignmentPlugin::Helper
16 16 end
17 17  
18 18 def display_author_folder(author_folder, user)
19   - return if author_folder.children.empty?
  19 + return if author_folder.children(true).empty?
20 20 content_tag('tr',
21 21 content_tag('td', link_to_last_submission(author_folder, user)) +
22 22 content_tag('td', time_format(author_folder.children.last.created_at)) +
... ...
plugins/work_assignment/test/functional/cms_controller_test.rb
... ... @@ -5,8 +5,7 @@ class CmsControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = CmsController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
  8 +
10 9 @person = create_user('test_user').person
11 10 login_as :test_user
12 11 e = Environment.default
... ...
plugins/work_assignment/test/functional/content_viewer_controller_test.rb
... ... @@ -5,8 +5,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = ContentViewerController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
  8 +
10 9 @profile = create_user('testinguser').person
11 10  
12 11 @organization = fast_create(Organization)
... ... @@ -35,6 +34,14 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
35 34 assert_response :success
36 35 end
37 36  
  37 + should 'display users submissions' do
  38 + folder = work_assignment.find_or_create_author_folder(@person)
  39 + submission = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :profile => organization, :parent => folder)
  40 + get :view_page, :profile => @organization.identifier, :page => work_assignment.path
  41 + assert_response :success
  42 + assert_match /rails.png/, @response.body
  43 + end
  44 +
38 45 should "display 'Upload files' when create children of image gallery" do
39 46 login_as(profile.identifier)
40 47 f = Gallery.create!(:name => 'gallery', :profile => profile)
... ...
plugins/work_assignment/test/functional/work_assignment_plugin_myprofile_controller_test.rb
... ... @@ -5,8 +5,7 @@ class WorkAssignmentPluginMyprofileControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = WorkAssignmentPluginMyprofileController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
  8 +
10 9 @person = create_user('test_user').person
11 10 login_as :test_user
12 11 e = Environment.default
... ...
public/javascripts/application.js
... ... @@ -26,6 +26,8 @@
26 26 *= require pagination.js
27 27 * views speficics
28 28 *= require add-and-join.js
  29 +*= require followers.js
  30 +*= require manage-followers.js
29 31 *= require report-abuse.js
30 32 *= require autogrow.js
31 33 *= require require_login.js
... ... @@ -550,6 +552,11 @@ function loading_for_button(selector) {
550 552 jQuery(selector).css('cursor', 'progress');
551 553 }
552 554  
  555 +function hide_loading_for_button(selector) {
  556 + selector.css("cursor","");
  557 + $(".small-loading").remove();
  558 +}
  559 +
553 560 function new_qualifier_row(selector, select_qualifiers, delete_button) {
554 561 index = jQuery(selector + ' tr').size() - 1;
555 562 jQuery(selector).append("<tr><td>" + select_qualifiers + "</td><td id='certifier-area-" + index + "'><select></select>" + delete_button + "</td></tr>");
... ...
public/javascripts/followers.js 0 → 100644
... ... @@ -0,0 +1,81 @@
  1 +$("#action-follow").live("click", function() {
  2 + var button = $(this);
  3 + var url = button.attr("href");
  4 + loading_for_button(button);
  5 +
  6 + $.post(url, function(data) {
  7 + button.fadeOut("fast", function() {
  8 + $("#circles-container").html(data);
  9 + $("#circles-container").fadeIn();
  10 + });
  11 + }).always(function() {
  12 + hide_loading_for_button(button);
  13 + });
  14 + return false;
  15 +});
  16 +
  17 +$("#cancel-set-circle").live("click", function() {
  18 + $("#circles-container").fadeOut("fast", function() {
  19 + $("#action-follow").fadeIn();
  20 + });
  21 + return false;
  22 +});
  23 +
  24 +$("#new-circle").live("click", function() {
  25 + $(this).fadeOut();
  26 + $("#circle-actions").fadeOut("fast", function() {
  27 + $("#new-circle-form").fadeIn();
  28 + });
  29 + return false;
  30 +});
  31 +
  32 +$("#new-circle-cancel").live("click", function() {
  33 + $("#new-circle-form").fadeOut("fast", function() {
  34 + $("#circle-actions").fadeIn();
  35 + $("#new-circle").fadeIn();
  36 + $("#text-field-name-new-circle").val('')
  37 + });
  38 + return false;
  39 +});
  40 +
  41 +$('#follow-circles-form').live("submit", function() {
  42 + var valuesToSubmit = $(this).serialize();
  43 + $.ajax({
  44 + type: "POST",
  45 + url: $(this).attr('action'),
  46 + data: valuesToSubmit,
  47 + dataType: "JSON",
  48 + statusCode: {
  49 + 200: function(response){
  50 + $("#circles-container").fadeOut();
  51 + $("#action-unfollow").fadeIn();
  52 + $.colorbox.close();
  53 + display_notice(response.responseText);
  54 + },
  55 + 400: function(response) {
  56 + display_notice(response.responseText);
  57 + }
  58 + }
  59 + })
  60 + return false;
  61 +});
  62 +
  63 +$("#new-circle-submit").live("click", function() {
  64 + $.ajax({
  65 + method: 'POST',
  66 + url: $(this).attr("href"),
  67 + data: {'circle[name]': $("#text-field-name-new-circle").val(),
  68 + 'circle[profile_type]': $("#circle_profile_type").val()},
  69 + success: function(response) {
  70 + $('#circles-checkboxes').append(response);
  71 + },
  72 + error: function(response) {
  73 + display_notice(response.responseText);
  74 + },
  75 + complete: function(response) {
  76 + $("#text-field-name-new-circle").val('')
  77 + $("#new-circle-cancel").trigger("click");
  78 + }
  79 + })
  80 + return false;
  81 +});
... ...
public/javascripts/manage-followers.js 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +$('#profile-type-filter').live('change', function() {
  2 + var filter_type = $(this).val();
  3 + $(".profile-list").addClass("fetching");
  4 + $.get(window.location.pathname, {filter: filter_type}, function(data) {
  5 + $(".main-content").html(data);
  6 + }).fail(function(data) {
  7 + $(".profile-list").removeClass("fetching");
  8 + });
  9 +});
... ...
public/stylesheets/blocks/profile-info.scss
... ... @@ -99,3 +99,44 @@
99 99 margin: 0px 0px 5px 0px;
100 100 padding: 2px;
101 101 }
  102 +#circles-container {
  103 + background-color: #eee;
  104 + padding: 5px;
  105 + display: flex;
  106 +}
  107 +#circles-container p {
  108 + font-size: 12px;
  109 + margin-bottom: 5px;
  110 +}
  111 +#circle-actions {
  112 + margin-top: 15px;
  113 +}
  114 +#new-category-field-actions-block {
  115 + float: left;
  116 + width: 80%;
  117 + margin-bottom: 10px;
  118 +}
  119 +#new-circle-form {
  120 + margin-top: 10px;
  121 +}
  122 +#new-circle-form input {
  123 + width: 90px;
  124 +}
  125 +#new-circle-form select {
  126 + margin-top: 2px;
  127 + width: 95px;
  128 +}
  129 +#new-circle-form label {
  130 + font-size: 10px;
  131 + margin-right: 5px;
  132 +}
  133 +#new-circle-form .button-bar {
  134 + padding-top: 0px;
  135 +}
  136 +#new-circle-form .button {
  137 + width: 60px;
  138 +}
  139 +#new-circle-form .button-bar .button {
  140 + width: 40px;
  141 + font-size: 10px;
  142 +}
... ...
public/stylesheets/profile-activity.scss
... ... @@ -167,7 +167,9 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img {
167 167  
168 168 #profile-wall li.profile-activity-item.join_community .profile-activity-text a img,
169 169 #profile-wall li.profile-activity-item.new_friendship .profile-activity-text a img,
  170 +#profile-wall li.profile-activity-item.new_follower .profile-activity-text a img,
170 171 #profile-network li.profile-activity-item.join_community .profile-activity-text a img,
  172 +#profile-network li.profile-activity-item.new_follower .profile-activity-text a img,
171 173 #profile-network li.profile-activity-item.new_friendship .profile-activity-text a img {
172 174 margin: 5px 5px 0 0;
173 175 padding: 1px;
... ...
public/stylesheets/profile-editor.scss
... ... @@ -263,3 +263,6 @@
263 263 -webkit-border-radius: 5px;
264 264 }
265 265  
  266 +#profile_allow_follows {
  267 + margin-top: 10px;
  268 +}
... ...
public/stylesheets/profile-list.scss
... ... @@ -23,6 +23,7 @@
23 23 }
24 24 .controller-favorite_enterprises .profile-list a.profile-link,
25 25 .controller-friends .profile-list a.profile-link,
  26 +.controller-followers .profile-list a.profile-link,
26 27 .list-profile-connections .profile-list a.profile-link,
27 28 .profiles-suggestions .profile-list a.profile-link {
28 29 text-decoration: none;
... ... @@ -32,11 +33,13 @@
32 33 }
33 34 .controller-favorite_enterprises .profile-list a.profile-link:hover,
34 35 .controller-friends .profile-list a.profile-link:hover,
  36 +.controller-followers .profile-list a.profile-link:hover,
35 37 .profiles-suggestions .profile-list a.profile-link:hover {
36 38 color: #FFF;
37 39 }
38 40 .controller-favorite_enterprises .profile-list .profile_link span,
39 41 .controller-friends .profile-list .profile_link span,
  42 +.controller-followers .profile-list .profile_link span,
40 43 .box-1 .profiles-suggestions .profile-list .profile_link span {
41 44 width: 80px;
42 45 display: block;
... ... @@ -44,12 +47,14 @@
44 47 }
45 48 .controller-favorite_enterprises .profile-list,
46 49 .controller-friends .profile-list,
  50 +.controller-followers .profile-list,
47 51 .profiles-suggestions .profile-list {
48 52 position: relative;
49 53 }
50 54  
51 55 .controller-favorite_enterprises .profile-list .controll,
52 56 .controller-friends .profile-list .controll,
  57 +.controller-followers .profile-list .controll,
53 58 .profiles-suggestions .profile-list .controll {
54 59 position: absolute;
55 60 top: 7px;
... ... @@ -57,17 +62,20 @@
57 62 }
58 63 .controller-favorite_enterprises .profile-list .controll a,
59 64 .controller-friends .profile-list .controll a,
  65 +.controller-followers .profile-list .controll a,
60 66 .profiles-suggestions .profile-list .controll a {
61 67 display: block;
62 68 margin-bottom: 2px;
63 69 }
64 70 .controller-favorite_enterprises .msie6 .profile-list .controll a,
65 71 .controller-friends .msie6 .profile-list .controll a,
  72 +.controller-folloed_people .msie6 .profile-list .controll a,
66 73 .profiles-suggestions .msie6 .profile-list .controll a {
67 74 width: 0px;
68 75 }
69 76 .controller-favorite_enterprises .button-bar,
70 77 .controller-friends .button-bar,
  78 +.controller-followers .button-bar,
71 79 .profiles-suggestions .button-bar {
72 80 clear: both;
73 81 padding-top: 20px;
... ... @@ -208,22 +216,35 @@
208 216 font-size: 12px;
209 217 }
210 218 .action-profile-members .profile_link{
211   - position: relative;
  219 + position: relative;
212 220 }
213 221 .action-profile-members .profile_link span.new-profile:last-child{
214   - position: absolute;
215   - top: 3px;
216   - right: 2px;
217   - text-transform: uppercase;
218   - color: #FFF;
219   - font-size: 9px;
220   - background: #66CC33;
221   - padding: 2px;
222   - display: block;
223   - width: 35px;
224   - font-weight: 700;
  222 + position: absolute;
  223 + top: 3px;
  224 + right: 2px;
  225 + text-transform: uppercase;
  226 + color: #FFF;
  227 + font-size: 9px;
  228 + background: #66CC33;
  229 + padding: 2px;
  230 + display: block;
  231 + width: 35px;
  232 + font-weight: 700;
225 233 }
226 234 .action-profile-members .profile_link .fn{
227   - font-style: normal;
228   - color: #000;
  235 + font-style: normal;
  236 + color: #000;
  237 +}
  238 +.category-name {
  239 + margin-top: 0px;
  240 + margin-bottom: 0px;
  241 + font-style: italic;
  242 + color: #888a85;
  243 + text-align: center;
  244 +}
  245 +.set-category-modal {
  246 + width: 250px;
  247 +}
  248 +.set-category-modal #actions-container {
  249 + margin-top: 20px
229 250 }
... ...
public/stylesheets/profile.scss
... ... @@ -39,3 +39,12 @@
39 39 width: 470px;
40 40 overflow-x: hidden;
41 41 }
  42 +
  43 +#circles-checkboxes {
  44 + text-align: left;
  45 + margin-left: 15%;
  46 +}
  47 +
  48 +#circles-checkboxes .circle:last-child {
  49 + margin-bottom: 10px;
  50 +}
... ...
script/install-dependencies/ubuntu-precise.sh
... ... @@ -7,7 +7,7 @@ export DEBIAN_INTERFACE=noninteractive
7 7  
8 8 run sudo apt-get install -qy dctrl-tools
9 9  
10   -packages=$(grep-dctrl -n -s Build-Depends,Depends,Recommends -S -X noosfero debian/control | sed -e 's/([^)]*)//g; s/,\s*/\n/g' | grep -v 'rake\|ruby\|thin\|debhelper\|cucumber\|rail\|memcached\|debconf\|dbconfig-common\|misc:Depends\|adduser\|mail-transport-agent')
  10 +packages=$(grep-dctrl -n -s Build-Depends,Depends,Recommends -S -X noosfero debian/control | sed -e 's/([^)]*)//g; s/,\s*/\n/g' | grep -v 'rake\|ruby\|thin\|debhelper\|cucumber\|rail\|memcached\|debconf\|dbconfig-common\|misc:Depends\|adduser\|mail-transport-agent\|bundler\|unicorn')
11 11  
12 12 run sudo apt-get install -qy ruby1.9.1-full build-essential libxml2-dev libxslt-dev libpq-dev libmagickcore-dev libmagickwand-dev $packages
13 13  
... ...
test/api/boxes_test.rb
... ... @@ -5,7 +5,6 @@ class BoxesTest &lt; ActiveSupport::TestCase
5 5 def setup
6 6 create_and_activate_user
7 7 login_api
8   -# @request = ActionController::TestRequest.new
9 8 end
10 9  
11 10 kinds= %w[Profile Community Person Enterprise Environment]
... ...
test/api/people_test.rb
... ... @@ -397,4 +397,62 @@ class PeopleTest &lt; ActiveSupport::TestCase
397 397 assert_not_nil person.image
398 398 assert_equal person.image.filename, base64_image[:filename]
399 399 end
  400 +
  401 + should 'add logged person as member of a profile' do
  402 + login_api
  403 + profile = fast_create(Community)
  404 + post "/api/v1/profiles/#{profile.id}/members?#{params.to_query}"
  405 + json = JSON.parse(last_response.body)
  406 + assert_equal json['pending'], false
  407 + assert person.is_member_of?(profile)
  408 + end
  409 +
  410 + should 'create task when add logged person as member of a moderated profile' do
  411 + login_api
  412 + profile = fast_create(Community, public_profile: false)
  413 + profile.add_member(create_user.person)
  414 + profile.closed = true
  415 + profile.save!
  416 + post "/api/v1/profiles/#{profile.id}/members?#{params.to_query}"
  417 + json = JSON.parse(last_response.body)
  418 + assert_equal json['pending'], true
  419 + assert !person.is_member_of?(profile)
  420 + end
  421 +
  422 + should 'remove logged person as member of a profile' do
  423 + login_api
  424 + profile = fast_create(Community)
  425 + profile.add_member(person)
  426 + delete "/api/v1/profiles/#{profile.id}/members?#{params.to_query}"
  427 + json = JSON.parse(last_response.body)
  428 + assert_equal person.identifier, json['person']['identifier']
  429 + assert !person.is_member_of?(profile)
  430 + end
  431 +
  432 + should 'forbid access to add members for non logged user' do
  433 + profile = fast_create(Community)
  434 + post "/api/v1/profiles/#{profile.id}/members?#{params.to_query}"
  435 + assert_equal 401, last_response.status
  436 + end
  437 +
  438 + should 'forbid access to remove members for non logged user' do
  439 + profile = fast_create(Community)
  440 + delete "/api/v1/profiles/#{profile.id}/members?#{params.to_query}"
  441 + assert_equal 401, last_response.status
  442 + end
  443 +
  444 + should 'forbid to add person as member when the profile does not allow' do
  445 + login_api
  446 + profile = fast_create(Person)
  447 + post "/api/v1/profiles/#{profile.id}/members?#{params.to_query}"
  448 + assert_equal 403, last_response.status
  449 + end
  450 +
  451 + should 'forbid to add person as member when the profile is secret' do
  452 + login_api
  453 + profile = fast_create(Community, secret: true)
  454 + post "/api/v1/profiles/#{profile.id}/members?#{params.to_query}"
  455 + assert !person.is_member_of?(profile)
  456 + assert_equal 403, last_response.status
  457 + end
400 458 end
... ...
test/functional/account_controller_test.rb
... ... @@ -11,8 +11,7 @@ class AccountControllerTest &lt; ActionController::TestCase
11 11  
12 12 def setup
13 13 @controller = AccountController.new
14   - @request = ActionController::TestRequest.new
15   - @response = ActionController::TestResponse.new
  14 +
16 15 disable_signup_bot_check
17 16 end
18 17  
... ...
test/functional/admin_panel_controller_test.rb
... ... @@ -6,8 +6,7 @@ class AdminPanelControllerTest &lt; ActionController::TestCase
6 6 all_fixtures
7 7 def setup
8 8 @controller = AdminPanelController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
  9 +
11 10 login_as(create_admin_user(Environment.default))
12 11 end
13 12  
... ...
test/functional/application_controller_test.rb
... ... @@ -4,10 +4,9 @@ require &#39;test_controller&#39;
4 4  
5 5 class ApplicationControllerTest < ActionController::TestCase
6 6 all_fixtures
  7 +
7 8 def setup
8 9 @controller = TestController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
11 10 end
12 11  
13 12 def test_detection_of_environment_by_host
... ... @@ -50,7 +49,7 @@ class ApplicationControllerTest &lt; ActionController::TestCase
50 49 current = fast_create(Environment, :name => 'test environment')
51 50 current.domains.create!(:name => 'example.com')
52 51  
53   - @request.expects(:host).returns('example.com').at_least_once
  52 + @request.env['HTTP_HOST'] = 'example.com'
54 53 get :index
55 54  
56 55 assert_equal current, assigns(:environment)
... ...
test/functional/categories_controller_test.rb
... ... @@ -5,8 +5,6 @@ class CategoriesControllerTest &lt; ActionController::TestCase
5 5 all_fixtures
6 6 def setup
7 7 @controller = CategoriesController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @env = fast_create(Environment, :name => "My test environment")
12 10 Environment.stubs(:default).returns(env)
... ...
test/functional/circles_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,129 @@
  1 +require_relative "../test_helper"
  2 +require 'circles_controller'
  3 +
  4 +class CirclesControllerTest < ActionController::TestCase
  5 +
  6 + def setup
  7 + @controller = CirclesController.new
  8 + @person = create_user('person').person
  9 + login_as(@person.identifier)
  10 + end
  11 +
  12 + should 'return all circles of a profile' do
  13 + circle1 = Circle.create!(:name => "circle1", :person => @person, :profile_type => 'Person')
  14 + circle2 = Circle.create!(:name => "circle2", :person => @person, :profile_type => 'Person')
  15 + get :index, :profile => @person.identifier
  16 +
  17 + assert_equivalent [circle1, circle2], assigns[:circles]
  18 + end
  19 +
  20 + should 'initialize an empty circle for creation' do
  21 + get :new, :profile => @person.identifier
  22 + assert_nil assigns[:circle].id
  23 + assert_nil assigns[:circle].name
  24 + end
  25 +
  26 + should 'create a new circle' do
  27 + assert_difference '@person.circles.count' do
  28 + post :create, :profile => @person.identifier,
  29 + :circle => { :name => 'circle' , :profile_type => Person.name}
  30 + end
  31 + assert_redirected_to :action => :index
  32 + end
  33 +
  34 + should 'not create a circle without a name' do
  35 + assert_no_difference '@person.circles.count' do
  36 + post :create, :profile => @person.identifier, :circle => { :name => nil }
  37 + end
  38 + assert_template :new
  39 + end
  40 +
  41 + should 'retrieve an existing circle when editing' do
  42 + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person')
  43 + get :edit, :profile => @person.identifier, :id => circle.id
  44 + assert_equal circle.name, assigns[:circle].name
  45 + end
  46 +
  47 + should 'return 404 when editing a circle that does not exist' do
  48 + get :edit, :profile => @person.identifier, :id => "nope"
  49 + assert_response 404
  50 + end
  51 +
  52 + should 'update an existing circle' do
  53 + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person')
  54 + post :update, :profile => @person.identifier, :id => circle.id,
  55 + :circle => { :name => "new name" }
  56 +
  57 + circle.reload
  58 + assert_equal "new name", circle.name
  59 + assert_redirected_to :action => :index
  60 + end
  61 +
  62 + should 'not update an existing circle without a name' do
  63 + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person')
  64 + post :update, :profile => @person.identifier, :id => circle.id,
  65 + :circle => { :name => nil }
  66 +
  67 + circle.reload
  68 + assert_equal "circle", circle.name
  69 + assert_template :edit
  70 + end
  71 +
  72 + should 'return 404 when updating a circle that does not exist' do
  73 + post :update, :profile => @person.identifier, :id => "nope", :name => "new name"
  74 + assert_response 404
  75 + end
  76 +
  77 + should 'destroy an existing circle and update related profiles' do
  78 + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person')
  79 + follower = fast_create(ProfileFollower, :profile_id => fast_create(Person).id,
  80 + :circle_id => circle.id)
  81 +
  82 + assert_difference "@person.circles.count", -1 do
  83 + post :destroy, :profile => @person.identifier, :id => circle.id
  84 + end
  85 +
  86 + follower.reload
  87 + assert_nil follower.circle
  88 + end
  89 +
  90 + should 'not destroy an existing circle if action is not post' do
  91 + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person')
  92 +
  93 + assert_no_difference "@person.circles.count" do
  94 + get :destroy, :profile => @person.identifier, :id => circle.id
  95 + end
  96 + assert_response 404
  97 + end
  98 +
  99 + should 'return 404 when deleting and circle that does not exist' do
  100 + get :destroy, :profile => @person.identifier, :id => "nope"
  101 + assert_response 404
  102 + end
  103 +
  104 + should 'return 404 for xhr_create if request is not xhr' do
  105 + post :xhr_create, :profile => @person.identifier
  106 + assert_response 404
  107 + end
  108 +
  109 + should 'return 400 if not possible to create circle via xhr' do
  110 + xhr :post, :xhr_create, :profile => @person.identifier,
  111 + :circle => { :name => 'Invalid Circle' }
  112 + assert_response 400
  113 + end
  114 +
  115 + should 'create a new circle via xhr' do
  116 + xhr :post, :xhr_create, :profile => @person.identifier,
  117 + :circle => { :name => 'A Brand New Circle',
  118 + :profile_type => Person.name }
  119 + assert_response 201
  120 + assert_match /A Brand New Circle/, response.body
  121 + end
  122 +
  123 + should 'not create a new circle via xhr with an invalid profile_type' do
  124 + xhr :post, :xhr_create, :profile => @person.identifier,
  125 + :circle => { :name => 'A Brand New Circle',
  126 + :profile_type => '__invalid__' }
  127 + assert_response 400
  128 + end
  129 +end
... ...
test/functional/comment_controller_test.rb
... ... @@ -5,8 +5,6 @@ class CommentControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = CommentController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @profile = create_user('testinguser').person
12 10 @environment = @profile.environment
... ...
test/functional/contact_controller_test.rb
... ... @@ -8,8 +8,6 @@ class ContactControllerTest &lt; ActionController::TestCase
8 8  
9 9 def setup
10 10 @controller = ContactController.new
11   - @request = ActionController::TestRequest.new
12   - @response = ActionController::TestResponse.new
13 11  
14 12 @profile = create_user('contact_test_user').person
15 13 @enterprise = fast_create(Enterprise, :identifier => 'contact_test_enterprise', :name => 'Test contact enteprise')
... ...
test/functional/content_viewer_controller_test.rb
... ... @@ -7,8 +7,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
7 7  
8 8 def setup
9 9 @controller = ContentViewerController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
12 10  
13 11 @profile = create_user('testinguser').person
14 12 @environment = @profile.environment
... ... @@ -220,8 +218,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
220 218 profile.domains << Domain.create!(:name => 'micojones.net')
221 219 profile.save!
222 220  
223   - ActionController::TestRequest.any_instance.expects(:host).returns('www.micojones.net').at_least_once
224   -
  221 + @request.env['HTTP_HOST'] = 'www.micojones.net'
225 222 get :view_page, :page => []
226 223  
227 224 assert_equal profile, assigns(:profile)
... ...
test/functional/edit_template_controller_test.rb
... ... @@ -7,8 +7,7 @@ class EditTemplateControllerTest &lt; ActionController::TestCase
7 7 all_fixtures
8 8 def setup
9 9 @controller = EditTemplateController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
  10 +
12 11 login_as 'ze'
13 12 end
14 13  
... ...
test/functional/enterprise_registration_controller_test.rb
... ... @@ -9,8 +9,7 @@ class EnterpriseRegistrationControllerTest &lt; ActionController::TestCase
9 9 def setup
10 10 super
11 11 @controller = EnterpriseRegistrationController.new
12   - @request = ActionController::TestRequest.new
13   - @response = ActionController::TestResponse.new
  12 +
14 13 login_as 'ze'
15 14 end
16 15  
... ...
test/functional/enterprise_validation_controller_test.rb
... ... @@ -7,8 +7,6 @@ class EnterpriseValidationControllerTest &lt; ActionController::TestCase
7 7  
8 8 def setup
9 9 @controller = EnterpriseValidationController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
12 10  
13 11 login_as 'ze'
14 12 @user = Profile['ze']
... ...
test/functional/environment_design_controller_test.rb
... ... @@ -7,8 +7,7 @@ class EnvironmentDesignControllerTest &lt; ActionController::TestCase
7 7  
8 8 def setup
9 9 @controller = EnvironmentDesignController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
  10 +
12 11 Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([])
13 12 end
14 13  
... ...
test/functional/environment_role_manager_controller_test.rb
... ... @@ -4,8 +4,6 @@ require &#39;environment_role_manager_controller&#39;
4 4 class EnvironmentRoleManagerControllerTest < ActionController::TestCase
5 5 def setup
6 6 @controller = EnvironmentRoleManagerController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7 end
10 8  
11 9 end
... ...
test/functional/environment_themes_controller_test.rb
... ... @@ -4,8 +4,6 @@ class EnvironmentThemesControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = EnvironmentThemesController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7  
10 8 Theme.stubs(:user_themes_dir).returns(TMP_THEMES_DIR)
11 9  
... ...
test/functional/favorite_enterprises_controller_test.rb
... ... @@ -6,8 +6,6 @@ class FavoriteEnterprisesControllerTest &lt; ActionController::TestCase
6 6 self.default_params = {profile: 'testuser'}
7 7 def setup
8 8 @controller = FavoriteEnterprisesController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
11 9  
12 10 self.profile = create_user('testuser').person
13 11 self.favorite_enterprise = fast_create(Enterprise, :name => 'the_enterprise', :identifier => 'the_enterprise')
... ...
test/functional/features_controller_test.rb
... ... @@ -6,8 +6,6 @@ class FeaturesControllerTest &lt; ActionController::TestCase
6 6 all_fixtures
7 7 def setup
8 8 @controller = FeaturesController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
11 9  
12 10 login_as(create_admin_user(Environment.find(2)))
13 11 end
... ...
test/functional/followers_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,58 @@
  1 +require_relative "../test_helper"
  2 +require 'followers_controller'
  3 +
  4 +class FollowersControllerTest < ActionController::TestCase
  5 + def setup
  6 + @profile = create_user('testuser').person
  7 + end
  8 +
  9 + should 'return followed people list' do
  10 + login_as(@profile.identifier)
  11 + person = fast_create(Person)
  12 + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person')
  13 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id)
  14 +
  15 + get :index, :profile => @profile.identifier
  16 + assert_includes assigns(:followed_people), person
  17 + end
  18 +
  19 + should 'return filtered followed people list' do
  20 + login_as(@profile.identifier)
  21 + person = fast_create(Person)
  22 + community = fast_create(Community)
  23 + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person')
  24 + circle2 = Circle.create!(:person=> @profile, :name => "Teams", :profile_type => 'Community')
  25 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id)
  26 + fast_create(ProfileFollower, :profile_id => community.id, :circle_id => circle2.id)
  27 +
  28 + get :index, :profile => @profile.identifier, :filter => "Community"
  29 + assert_equal assigns(:followed_people), [community]
  30 +
  31 + get :index, :profile => @profile.identifier, :filter => "Person"
  32 + assert_equal assigns(:followed_people), [person]
  33 + end
  34 +
  35 + should 'redirect to login page if not logged in' do
  36 + get :index, :profile => @profile.identifier
  37 + assert_redirected_to :controller => 'account', :action => 'login'
  38 + end
  39 +
  40 + should 'render set category modal' do
  41 + login_as(@profile.identifier)
  42 + person = fast_create(Person)
  43 + get :set_category_modal, :profile => @profile.identifier, :followed_profile_id => person.id
  44 + assert_tag :tag => "input", :attributes => { :id => "followed_profile_id", :value => person.id }
  45 + end
  46 +
  47 + should 'update followed person category' do
  48 + login_as(@profile.identifier)
  49 + person = fast_create(Person)
  50 + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person')
  51 + circle2 = Circle.create!(:person=> @profile, :name => "DotA", :profile_type => 'Person')
  52 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id)
  53 +
  54 + post :update_category, :profile => @profile.identifier, :circles => {"DotA"=> circle2.id}, :followed_profile_id => person.id
  55 + assert_equivalent ProfileFollower.with_profile(person).with_follower(@profile).map(&:circle), [circle2]
  56 + end
  57 +
  58 +end
... ...
test/functional/friends_controller_test.rb
... ... @@ -6,8 +6,6 @@ class FriendsControllerTest &lt; ActionController::TestCase
6 6 self.default_params = {profile: 'testuser'}
7 7 def setup
8 8 @controller = FriendsController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
11 9  
12 10 self.profile = create_user('testuser').person
13 11 self.friend = create_user('thefriend').person
... ...
test/functional/home_controller_test.rb
... ... @@ -10,8 +10,6 @@ class HomeControllerTest &lt; ActionController::TestCase
10 10 all_fixtures
11 11 def setup
12 12 @controller = HomeController.new
13   - @request = ActionController::TestRequest.new
14   - @response = ActionController::TestResponse.new
15 13 end
16 14  
17 15 should 'not display news from portal if disabled in environment' do
... ...
test/functional/licenses_controller_test.rb
... ... @@ -5,8 +5,7 @@ class LicensesControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = LicensesController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
  8 +
10 9 @environment = Environment.default
11 10 login_as(create_admin_user(@environment))
12 11 end
... ...
test/functional/mailconf_controller_test.rb
... ... @@ -4,8 +4,7 @@ class MailconfControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = MailconfController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
  7 +
9 8 User.destroy_all
10 9 @user = create_user('ze')
11 10  
... ...
test/functional/map_balloon_controller_test.rb
... ... @@ -5,8 +5,6 @@ class MapBalloonControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = MapBalloonController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @profile = create_user('test_profile').person
12 10 login_as(@profile.identifier)
... ...
test/functional/maps_controller_test.rb
... ... @@ -6,8 +6,6 @@ class MapsControllerTest &lt; ActionController::TestCase
6 6  
7 7 def setup
8 8 @controller = MapsController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
11 9  
12 10 @profile = create_user('test_profile').person
13 11 login_as(@profile.identifier)
... ...
test/functional/memberships_controller_test.rb
... ... @@ -7,8 +7,6 @@ class MembershipsControllerTest &lt; ActionController::TestCase
7 7  
8 8 def setup
9 9 @controller = MembershipsController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
12 10  
13 11 @profile = create_user('testuser').person
14 12 login_as('testuser')
... ...
test/functional/my_profile_controller_test.rb
... ... @@ -13,8 +13,6 @@ class MyProfileControllerTest &lt; ActionController::TestCase
13 13 all_fixtures
14 14 def setup
15 15 @controller = MyProfileController.new
16   - @request = ActionController::TestRequest.new
17   - @response = ActionController::TestResponse.new
18 16 end
19 17  
20 18 def test_should_allow_person
... ...
test/functional/organizations_controller_test.rb
... ... @@ -5,8 +5,6 @@ class OrganizationsControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = OrganizationsController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @environment = Environment.default
12 10  
... ...
test/functional/plugins_controller_test.rb
... ... @@ -6,8 +6,7 @@ class PluginsControllerTest &lt; ActionController::TestCase
6 6 all_fixtures
7 7 def setup
8 8 @controller = PluginsController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
  9 +
11 10 @environment = Environment.default
12 11 login_as(create_admin_user(@environment))
13 12 end
... ...
test/functional/profile_controller_test.rb
... ... @@ -771,12 +771,15 @@ class ProfileControllerTest &lt; ActionController::TestCase
771 771 assert_equal 15, assigns(:activities).size
772 772 end
773 773  
774   - should 'not see the friends activities in the current profile' do
  774 + should 'not see the followers activities in the current profile' do
  775 + circle = Circle.create!(:person=> profile, :name => "Zombies", :profile_type => 'Person')
  776 +
775 777 p2 = create_user.person
776   - refute profile.is_a_friend?(p2)
  778 + refute profile.follows?(p2)
777 779 p3 = create_user.person
778   - p3.add_friend(profile)
779   - assert p3.is_a_friend?(profile)
  780 + profile.follow(p3, circle)
  781 + assert profile.follows?(p3)
  782 +
780 783 ActionTracker::Record.destroy_all
781 784  
782 785 scrap1 = create(Scrap, defaults_for_scrap(:sender => p2, :receiver => p3))
... ... @@ -964,7 +967,11 @@ class ProfileControllerTest &lt; ActionController::TestCase
964 967 should 'have activities defined if logged in and is following profile' do
965 968 login_as(profile.identifier)
966 969 p1= fast_create(Person)
967   - p1.add_friend(profile)
  970 +
  971 + circle = Circle.create!(:person=> profile, :name => "Zombies", :profile_type => 'Person')
  972 +
  973 + profile.follow(p1, circle)
  974 +
968 975 ActionTracker::Record.destroy_all
969 976 get :index, :profile => p1.identifier
970 977 assert_equal [], assigns(:activities)
... ... @@ -1932,4 +1939,110 @@ class ProfileControllerTest &lt; ActionController::TestCase
1932 1939 assert_redirected_to :controller => 'account', :action => 'login'
1933 1940 end
1934 1941  
  1942 + should 'not follow a user without defining a circle' do
  1943 + login_as(@profile.identifier)
  1944 + person = fast_create(Person)
  1945 + assert_no_difference 'ProfileFollower.count' do
  1946 + post :follow, :profile => person.identifier, :circles => {}
  1947 + end
  1948 + end
  1949 +
  1950 + should "not follow user if not logged" do
  1951 + person = fast_create(Person)
  1952 + get :follow, :profile => person.identifier
  1953 +
  1954 + assert_redirected_to :controller => 'account', :action => 'login'
  1955 + end
  1956 +
  1957 + should 'follow a user with a circle' do
  1958 + login_as(@profile.identifier)
  1959 + person = fast_create(Person)
  1960 +
  1961 + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person')
  1962 +
  1963 + assert_difference 'ProfileFollower.count' do
  1964 + post :follow, :profile => person.identifier, :circles => {"Zombies" => circle.id}
  1965 + end
  1966 + end
  1967 +
  1968 + should 'follow a user with more than one circle' do
  1969 + login_as(@profile.identifier)
  1970 + person = fast_create(Person)
  1971 +
  1972 + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person')
  1973 + circle2 = Circle.create!(:person=> @profile, :name => "Brainsss", :profile_type => 'Person')
  1974 +
  1975 + assert_difference 'ProfileFollower.count', 2 do
  1976 + post :follow, :profile => person.identifier, :circles => {"Zombies" => circle.id, "Brainsss"=> circle2.id}
  1977 + end
  1978 + end
  1979 +
  1980 + should 'not follow a user with no circle selected' do
  1981 + login_as(@profile.identifier)
  1982 + person = fast_create(Person)
  1983 +
  1984 + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person')
  1985 + circle2 = Circle.create!(:person=> @profile, :name => "Brainsss", :profile_type => 'Person')
  1986 +
  1987 + assert_no_difference 'ProfileFollower.count' do
  1988 + post :follow, :profile => person.identifier, :circles => {"Zombies" => "0", "Brainsss" => "0"}
  1989 + end
  1990 +
  1991 + assert_match /Select at least one circle to follow/, response.body
  1992 + end
  1993 +
  1994 + should 'not follow if current_person already follows the person' do
  1995 + login_as(@profile.identifier)
  1996 + person = fast_create(Person)
  1997 +
  1998 + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person')
  1999 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id)
  2000 +
  2001 + assert_no_difference 'ProfileFollower.count' do
  2002 + post :follow, :profile => person.identifier, :follow => { :circles => {"Zombies" => circle.id} }
  2003 + end
  2004 + assert_response 400
  2005 + end
  2006 +
  2007 + should "not unfollow user if not logged" do
  2008 + person = fast_create(Person)
  2009 + post :unfollow, :profile => person.identifier
  2010 +
  2011 + assert_redirected_to :controller => 'account', :action => 'login'
  2012 + end
  2013 +
  2014 + should "unfollow a followed person" do
  2015 + login_as(@profile.identifier)
  2016 + person = fast_create(Person)
  2017 +
  2018 + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person')
  2019 + follower = fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id)
  2020 +
  2021 + assert_not_nil follower
  2022 +
  2023 + post :unfollow, :profile => person.identifier
  2024 + follower = ProfileFollower.find_by(:profile_id => person.id, :circle_id => circle.id)
  2025 + assert_nil follower
  2026 + end
  2027 +
  2028 + should "not unfollow a not followed person" do
  2029 + login_as(@profile.identifier)
  2030 + person = fast_create(Person)
  2031 +
  2032 + assert_no_difference 'ProfileFollower.count' do
  2033 + post :unfollow, :profile => person.identifier
  2034 + end
  2035 + end
  2036 +
  2037 + should "redirect to page after unfollow" do
  2038 + login_as(@profile.identifier)
  2039 + person = fast_create(Person)
  2040 +
  2041 + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person')
  2042 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id)
  2043 +
  2044 + post :unfollow, :profile => person.identifier, :redirect_to => "/some/url"
  2045 + assert_redirected_to "/some/url"
  2046 + end
  2047 +
1935 2048 end
... ...
test/functional/profile_design_controller_test.rb
... ... @@ -11,8 +11,6 @@ class ProfileDesignControllerTest &lt; ActionController::TestCase
11 11 attr_reader :holder
12 12 def setup
13 13 @controller = ProfileDesignController.new
14   - @request = ActionController::TestRequest.new
15   - @response = ActionController::TestResponse.new
16 14  
17 15 @profile = @holder = create_user('designtestuser').person
18 16 holder.save!
... ...
test/functional/profile_editor_controller_test.rb
... ... @@ -6,8 +6,7 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
6 6  
7 7 def setup
8 8 @controller = ProfileEditorController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
  9 +
11 10 @profile = create_user('default_user').person
12 11 Environment.default.affiliate(@profile, [Environment::Roles.admin(Environment.default.id)] + Profile::Roles.all_roles(Environment.default.id))
13 12 login_as('default_user')
... ... @@ -643,9 +642,10 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
643 642  
644 643 profile.domains << Domain.new(:name => 'myowndomain.net')
645 644 profile.environment.domains << Domain.new(:name => 'myenv.net')
646   - ActionController::TestRequest.any_instance.stubs(:host).returns(profile.hostname)
647 645  
  646 + @request.env['HTTP_HOST'] = profile.hostname
648 647 get :edit, :profile => profile.identifier
  648 +
649 649 assert_tag :tag => 'select', :attributes => { :name => 'profile_data[preferred_domain_id]' }, :descendant => { :tag => "option", :content => 'myowndomain.net' }
650 650 assert_tag :tag => 'select', :attributes => { :name => 'profile_data[preferred_domain_id]' }, :descendant => { :tag => "option", :content => 'myenv.net' }
651 651  
... ... @@ -659,9 +659,10 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
659 659  
660 660 profile.domains << Domain.new(:name => 'myowndomain.net')
661 661 profile.environment.domains << Domain.new(:name => 'myenv.net')
662   - ActionController::TestRequest.any_instance.stubs(:host).returns(profile.hostname)
663 662  
  663 + @request.env['HTTP_HOST'] = profile.hostname
664 664 get :edit, :profile => profile.identifier
  665 +
665 666 assert_tag :tag => "select", :attributes => { :name => 'profile_data[preferred_domain_id]'}, :descendant => { :tag => 'option', :content => '&lt;Select domain&gt;', :attributes => { :value => '' } }
666 667  
667 668 post :edit, :profile => profile.identifier, :profile_data => { :preferred_domain_id => '' }
... ... @@ -1112,8 +1113,10 @@ class ProfileEditorControllerTest &lt; ActionController::TestCase
1112 1113 should 'not redirect if the profile_hostname is the same as environment hostname' do
1113 1114 Person.any_instance.stubs(:hostname).returns('hostname.org')
1114 1115 Environment.any_instance.stubs(:default_hostname).returns('hostname.org')
1115   - ActionController::TestRequest.any_instance.stubs(:host).returns('hostname.org')
  1116 +
  1117 + @request.env['HTTP_HOST'] = 'hostname.org'
1116 1118 get :index, :profile => profile.identifier
  1119 +
1117 1120 assert_response :success
1118 1121 end
1119 1122  
... ...
test/functional/profile_members_controller_test.rb
... ... @@ -5,8 +5,6 @@ class ProfileMembersControllerTest &lt; ActionController::TestCase
5 5 def setup
6 6 super
7 7 @controller = ProfileMembersController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8 end
11 9  
12 10 should 'not access index if dont have permission' do
... ...
test/functional/profile_roles_controller_test.rb
... ... @@ -5,8 +5,7 @@ class ProfileRolesControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = ProfileRolesController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
  8 +
10 9 @role = Role.first
11 10 end
12 11  
... ...
test/functional/profile_search_controller_test.rb
... ... @@ -4,8 +4,6 @@ require &#39;profile_search_controller&#39;
4 4 class ProfileSearchControllerTest < ActionController::TestCase
5 5 def setup
6 6 @controller = ProfileSearchController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7  
10 8 @person = fast_create(Person)
11 9 end
... ...
test/functional/profile_themes_controller_test.rb
... ... @@ -4,8 +4,6 @@ class ProfileThemesControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = ProfileThemesController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7  
10 8 Theme.stubs(:user_themes_dir).returns(TMP_THEMES_DIR)
11 9  
... ...
test/functional/region_validators_controller_test.rb
... ... @@ -5,8 +5,7 @@ class RegionValidatorsControllerTest &lt; ActionController::TestCase
5 5 all_fixtures
6 6 def setup
7 7 @controller = RegionValidatorsController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
  8 +
10 9 login_as('ze')
11 10 end
12 11  
... ...
test/functional/role_controller_test.rb
... ... @@ -6,8 +6,7 @@ class RoleControllerTest &lt; ActionController::TestCase
6 6  
7 7 def setup
8 8 @controller = RoleController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
  9 +
11 10 @role = Role.first
12 11 login_as(:ze)
13 12 end
... ...
test/functional/search_controller_test.rb
... ... @@ -4,9 +4,6 @@ class SearchControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = SearchController.new
7   - @request = ActionController::TestRequest.new
8   - @request.stubs(:ssl?).returns(false)
9   - @response = ActionController::TestResponse.new
10 7  
11 8 @environment = Environment.default
12 9 @category = Category.create!(:name => 'my-category', :environment => @environment)
... ...
test/functional/system_controller_test.rb
... ... @@ -4,8 +4,6 @@ require &#39;system_controller&#39;
4 4 class SystemControllerTest < ActionController::TestCase
5 5 def setup
6 6 @controller = SystemController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7 end
10 8  
11 9 # Replace this with your real tests.
... ...
test/functional/tasks_controller_test.rb
... ... @@ -7,8 +7,6 @@ class TasksControllerTest &lt; ActionController::TestCase
7 7  
8 8 def setup
9 9 @controller = TasksController.new
10   - @request = ActionController::TestRequest.new
11   - @response = ActionController::TestResponse.new
12 10  
13 11 self.profile = create_user('testuser').person
14 12 @controller.stubs(:profile).returns(profile)
... ...
test/functional/templates_controller_test.rb
... ... @@ -5,8 +5,6 @@ class TemplatesControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = TemplatesController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @environment = Environment.default
12 10 login_as(create_admin_user(@environment))
... ...
test/functional/trusted_sites_controller_test.rb
... ... @@ -6,8 +6,7 @@ class TrustedSitesControllerTest &lt; ActionController::TestCase
6 6  
7 7 def setup
8 8 @controller = TrustedSitesController.new
9   - @request = ActionController::TestRequest.new
10   - @response = ActionController::TestResponse.new
  9 +
11 10 @role = Role.first
12 11 @environment = Environment.default
13 12 @environment.trusted_sites_for_iframe = ['existing.site.com']
... ...
test/functional/users_controller_test.rb
... ... @@ -5,8 +5,6 @@ class UsersControllerTest &lt; ActionController::TestCase
5 5  
6 6 def setup
7 7 @controller = UsersController.new
8   - @request = ActionController::TestRequest.new
9   - @response = ActionController::TestResponse.new
10 8  
11 9 @environment = Environment.default
12 10  
... ...
test/unit/article_test.rb
... ... @@ -1099,9 +1099,10 @@ class ArticleTest &lt; ActiveSupport::TestCase
1099 1099 assert_equal 3, ActionTrackerNotification.where(action_tracker_id: second_activity.id).count
1100 1100 end
1101 1101  
1102   - should 'create notifications to friends when creating an article' do
  1102 + should 'create notifications to followers when creating an article' do
1103 1103 friend = fast_create(Person)
1104   - profile.add_friend(friend)
  1104 + circle = Circle.create!(:person=> friend, :name => "Zombies", :profile_type => 'Person')
  1105 + friend.follow(profile, circle)
1105 1106 Article.destroy_all
1106 1107 ActionTracker::Record.destroy_all
1107 1108 ActionTrackerNotification.destroy_all
... ... @@ -1112,9 +1113,10 @@ class ArticleTest &lt; ActiveSupport::TestCase
1112 1113 assert_equal friend, ActionTrackerNotification.last.profile
1113 1114 end
1114 1115  
1115   - should 'create the notification to the friend when one friend has the notification and the other no' do
  1116 + should 'create the notification to the follower when one follower has the notification and the other no' do
1116 1117 f1 = fast_create(Person)
1117   - profile.add_friend(f1)
  1118 + circle = Circle.create!(:person=> f1, :name => "Zombies", :profile_type => 'Person')
  1119 + f1.follow(profile, circle)
1118 1120  
1119 1121 User.current = profile.user
1120 1122 article = create TinyMceArticle, :name => 'Tracked Article 1', :profile_id => profile.id
... ... @@ -1123,16 +1125,22 @@ class ArticleTest &lt; ActiveSupport::TestCase
1123 1125 assert_equal 2, ActionTrackerNotification.where(action_tracker_id: article.activity.id).count
1124 1126  
1125 1127 f2 = fast_create(Person)
1126   - profile.add_friend(f2)
  1128 + circle2 = Circle.create!(:person=> f2, :name => "Zombies", :profile_type => 'Person')
  1129 + f2.follow(profile, circle2)
  1130 +
1127 1131 article2 = create TinyMceArticle, :name => 'Tracked Article 2', :profile_id => profile.id
1128 1132 assert_equal 2, ActionTracker::Record.where(verb: 'create_article').count
1129 1133 process_delayed_job_queue
1130 1134 assert_equal 3, ActionTrackerNotification.where(action_tracker_id: article2.activity.id).count
1131 1135 end
1132 1136  
1133   - should 'destroy activity and notifications of friends when destroying an article' do
  1137 + should 'destroy activity and notifications of followers when destroying an article' do
1134 1138 friend = fast_create(Person)
1135   - profile.add_friend(friend)
  1139 +
  1140 + circle = Circle.create!(:person=> friend, :name => "Zombies", :profile_type => 'Person')
  1141 +
  1142 + friend.follow(profile, circle)
  1143 +
1136 1144 Article.destroy_all
1137 1145 ActionTracker::Record.destroy_all
1138 1146 ActionTrackerNotification.destroy_all
... ...
test/unit/custom_design_test.rb 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +require_relative "../test_helper"
  2 +
  3 +class CustomDesignTest < ActionView::TestCase
  4 +
  5 + include CustomDesign
  6 + include ActionView::Helpers::TagHelper
  7 +
  8 + def setup
  9 + end
  10 +
  11 + should 'allow class instance customization of custom design' do
  12 + self.class.use_custom_design boxes_limit: 1
  13 + assert_equal({boxes_limit: 1}, self.custom_design)
  14 + @custom_design = {boxes_limit: 2}
  15 + assert_equal({boxes_limit: 2}, self.custom_design)
  16 +
  17 + end
  18 +
  19 +end
... ...
test/unit/event_test.rb
... ... @@ -282,7 +282,7 @@ class EventTest &lt; ActiveSupport::TestCase
282 282 end
283 283  
284 284 should 'be translatable' do
285   - assert_kind_of Noosfero::TranslatableContent, Event.new
  285 + assert_kind_of TranslatableContent, Event.new
286 286 end
287 287  
288 288 should 'tiny mce editor is enabled' do
... ...
test/unit/friendship_test.rb
... ... @@ -28,14 +28,14 @@ class FriendshipTest &lt; ActiveSupport::TestCase
28 28 f.person = a
29 29 f.friend = b
30 30 f.save!
31   - ta = ActionTracker::Record.last
  31 + ta = ActionTracker::Record.where(:target_type => "Friendship").last
32 32 assert_equal a, ta.user
33 33 assert_equal 'b', ta.get_friend_name[0]
34 34 f = Friendship.new
35 35 f.person = a
36 36 f.friend = c
37 37 f.save!
38   - ta = ActionTracker::Record.last
  38 + ta = ActionTracker::Record.where(:target_type => "Friendship").last
39 39 assert_equal a, ta.user
40 40 assert_equal 'c', ta.get_friend_name[1]
41 41 end
... ... @@ -46,14 +46,14 @@ class FriendshipTest &lt; ActiveSupport::TestCase
46 46 f.person = a
47 47 f.friend = b
48 48 f.save!
49   - ta = ActionTracker::Record.last
  49 + ta = ActionTracker::Record.where(:target_type => "Friendship").last
50 50 assert_equal a, ta.user
51 51 assert_equal ['b'], ta.get_friend_name
52 52 f = Friendship.new
53 53 f.person = b
54 54 f.friend = a
55 55 f.save!
56   - ta = ActionTracker::Record.last
  56 + ta = ActionTracker::Record.where(:target_type => "Friendship").last
57 57 assert_equal b, ta.user
58 58 assert_equal ['a'], ta.get_friend_name
59 59 end
... ... @@ -72,4 +72,55 @@ class FriendshipTest &lt; ActiveSupport::TestCase
72 72 assert_not_includes p2.friends(true), p1
73 73 end
74 74  
  75 + should 'add follower when adding friend' do
  76 + p1 = create_user('testuser1').person
  77 + p2 = create_user('testuser2').person
  78 +
  79 + assert_difference 'ProfileFollower.count', 2 do
  80 + p1.add_friend(p2, 'friends')
  81 + p2.add_friend(p1, 'friends')
  82 + end
  83 +
  84 + assert_includes p1.followers(true), p2
  85 + assert_includes p2.followers(true), p1
  86 + end
  87 +
  88 + should 'remove follower when a friend removal occurs' do
  89 + p1 = create_user('testuser1').person
  90 + p2 = create_user('testuser2').person
  91 +
  92 + p1.add_friend(p2, 'friends')
  93 + p2.add_friend(p1, 'friends')
  94 +
  95 + Friendship.remove_friendship(p1, p2)
  96 +
  97 + assert_not_includes p1.followers(true), p2
  98 + assert_not_includes p2.followers(true), p1
  99 + end
  100 +
  101 + should 'keep friendship intact when stop following' do
  102 + p1 = create_user('testuser1').person
  103 + p2 = create_user('testuser2').person
  104 +
  105 + p1.add_friend(p2, 'friends')
  106 + p2.add_friend(p1, 'friends')
  107 +
  108 + p1.unfollow(p2)
  109 +
  110 + assert_includes p1.friends(true), p2
  111 + assert_includes p2.friends(true), p1
  112 + end
  113 +
  114 + should 'do not add friendship when start following' do
  115 + p1 = create_user('testuser1').person
  116 + p2 = create_user('testuser2').person
  117 +
  118 + circle1 = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person')
  119 + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person')
  120 + p1.follow(p2, circle1)
  121 + p2.follow(p1, circle2)
  122 +
  123 + assert_not_includes p1.friends(true), p2
  124 + assert_not_includes p2.friends(true), p1
  125 + end
75 126 end
... ...
test/unit/noosfero_test.rb
... ... @@ -43,10 +43,12 @@ class NoosferoTest &lt; ActiveSupport::TestCase
43 43 end
44 44  
45 45 should 'change locale temporarily' do
46   - Noosfero.with_locale('pt') do
47   - assert_equal 'pt', FastGettext.locale
  46 + current_locale = FastGettext.locale
  47 + another_locale = current_locale == 'pt' ? 'en' : 'pt'
  48 + Noosfero.with_locale(another_locale) do
  49 + assert_equal another_locale, FastGettext.locale
48 50 end
49   - assert_equal 'en', FastGettext.locale
  51 + assert_equal current_locale, FastGettext.locale
50 52 end
51 53  
52 54 should "use default hostname of default environment as hostname of Noosfero instance" do
... ...
test/unit/notify_activity_to_profiles_job_test.rb
... ... @@ -24,15 +24,21 @@ class NotifyActivityToProfilesJobTest &lt; ActiveSupport::TestCase
24 24 end
25 25 end
26 26  
27   - should 'notify just the users and his friends tracking user actions' do
  27 + should 'notify just the users and his followers tracking user actions' do
28 28 person = fast_create(Person)
29 29 community = fast_create(Community)
30 30 action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :verb => 'create_article')
31 31 refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb)
32 32 p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person)
33   - fast_create(Friendship, :person_id => person.id, :friend_id => p1.id)
34   - fast_create(Friendship, :person_id => person.id, :friend_id => p2.id)
35   - fast_create(Friendship, :person_id => p1.id, :friend_id => m1.id)
  33 +
  34 + circle1 = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person')
  35 + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person')
  36 + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Person')
  37 +
  38 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle1.id)
  39 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle2.id)
  40 + fast_create(ProfileFollower, :profile_id => m1.id, :circle_id => circle.id)
  41 +
36 42 fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id)
37 43 ActionTrackerNotification.delete_all
38 44 job = NotifyActivityToProfilesJob.new(action_tracker.id)
... ... @@ -66,23 +72,24 @@ class NotifyActivityToProfilesJobTest &lt; ActiveSupport::TestCase
66 72 end
67 73 end
68 74  
69   - should 'notify users its friends, the community and its members' do
  75 + should 'notify users its followers, the community and its members' do
70 76 person = fast_create(Person)
71 77 community = fast_create(Community)
72 78 action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => community.id, :verb => 'create_article')
73 79 refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb)
74 80 p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person)
75   - fast_create(Friendship, :person_id => person.id, :friend_id => p1.id)
76   - fast_create(Friendship, :person_id => person.id, :friend_id => p2.id)
  81 +
  82 + circle1 = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person')
  83 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle1.id)
  84 +
77 85 fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id)
78 86 fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id)
79 87 ActionTrackerNotification.delete_all
80 88 job = NotifyActivityToProfilesJob.new(action_tracker.id)
81 89 job.perform
82 90 process_delayed_job_queue
83   -
84   - assert_equal 6, ActionTrackerNotification.count
85   - [person, community, p1, p2, m1, m2].each do |profile|
  91 + assert_equal 5, ActionTrackerNotification.count
  92 + [person, community, p1, m1, m2].each do |profile|
86 93 notification = ActionTrackerNotification.find_by profile_id: profile.id
87 94 assert_equal action_tracker, notification.action_tracker
88 95 end
... ... @@ -119,8 +126,13 @@ class NotifyActivityToProfilesJobTest &lt; ActiveSupport::TestCase
119 126 action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => community.id, :verb => 'join_community')
120 127 refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb)
121 128 p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person)
122   - fast_create(Friendship, :person_id => person.id, :friend_id => p1.id)
123   - fast_create(Friendship, :person_id => person.id, :friend_id => p2.id)
  129 +
  130 + circle1 = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person')
  131 + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person')
  132 +
  133 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle1.id)
  134 + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle2.id)
  135 +
124 136 fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id)
125 137 fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id)
126 138 ActionTrackerNotification.delete_all
... ...
test/unit/person_notifier_test.rb
... ... @@ -178,6 +178,7 @@ class PersonNotifierTest &lt; ActiveSupport::TestCase
178 178 update_product: -> { create Product, profile: @profile, product_category: create(ProductCategory, environment: Environment.default) },
179 179 remove_product: -> { create Product, profile: @profile, product_category: create(ProductCategory, environment: Environment.default) },
180 180 favorite_enterprise: -> { create FavoriteEnterprisePerson, enterprise: create(Enterprise), person: @member },
  181 + new_follower: -> { @member }
181 182 }
182 183  
183 184 ActionTrackerConfig.verb_names.each do |verb|
... ... @@ -197,6 +198,7 @@ class PersonNotifierTest &lt; ActiveSupport::TestCase
197 198 'friend_url' => '/', 'friend_profile_custom_icon' => [], 'friend_name' => ['joe'],
198 199 'resource_name' => ['resource'], 'resource_profile_custom_icon' => [], 'resource_url' => ['/'],
199 200 'enterprise_name' => 'coop', 'enterprise_url' => '/coop',
  201 + 'follower_url' => '/', 'follower_profile_custom_icon' => [], 'follower_name' => ['joe'],
200 202 'view_url'=> ['/'], 'thumbnail_path' => ['1'],
201 203 }
202 204 a.get_url = ''
... ...
test/unit/person_test.rb
... ... @@ -728,7 +728,7 @@ class PersonTest &lt; ActiveSupport::TestCase
728 728 assert_equal [s4], p2.scraps_received.not_replies
729 729 end
730 730  
731   - should "the followed_by method be protected and true to the person friends and herself by default" do
  731 + should "the followed_by method return true to the person friends and herself by default" do
732 732 p1 = fast_create(Person)
733 733 p2 = fast_create(Person)
734 734 p3 = fast_create(Person)
... ... @@ -740,9 +740,9 @@ class PersonTest &lt; ActiveSupport::TestCase
740 740 assert p1.is_a_friend?(p4)
741 741  
742 742 assert_equal true, p1.send(:followed_by?,p1)
743   - assert_equal true, p1.send(:followed_by?,p2)
744   - assert_equal true, p1.send(:followed_by?,p4)
745   - assert_equal false, p1.send(:followed_by?,p3)
  743 + assert_equal true, p2.send(:followed_by?,p1)
  744 + assert_equal true, p4.send(:followed_by?,p1)
  745 + assert_equal false, p3.send(:followed_by?,p1)
746 746 end
747 747  
748 748 should "the person follows her friends and herself by default" do
... ... @@ -757,9 +757,9 @@ class PersonTest &lt; ActiveSupport::TestCase
757 757 assert p4.is_a_friend?(p1)
758 758  
759 759 assert_equal true, p1.follows?(p1)
760   - assert_equal true, p1.follows?(p2)
761   - assert_equal true, p1.follows?(p4)
762   - assert_equal false, p1.follows?(p3)
  760 + assert_equal true, p2.follows?(p1)
  761 + assert_equal true, p4.follows?(p1)
  762 + assert_equal false, p3.follows?(p1)
763 763 end
764 764  
765 765 should "a person member of a community follows the community" do
... ... @@ -836,18 +836,21 @@ class PersonTest &lt; ActiveSupport::TestCase
836 836 assert_nil Scrap.find_by(id: scrap.id)
837 837 end
838 838  
839   - should "the tracked action be notified to person friends and herself" do
  839 + should "the tracked action be notified to person followers and herself" do
840 840 Person.destroy_all
841 841 p1 = fast_create(Person)
842 842 p2 = fast_create(Person)
843 843 p3 = fast_create(Person)
844 844 p4 = fast_create(Person)
845 845  
846   - p1.add_friend(p2)
847   - assert p1.is_a_friend?(p2)
848   - refute p1.is_a_friend?(p3)
849   - p1.add_friend(p4)
850   - assert p1.is_a_friend?(p4)
  846 + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person')
  847 + circle4 = Circle.create!(:person=> p4, :name => "Zombies", :profile_type => 'Person')
  848 +
  849 + p2.follow(p1, circle2)
  850 + assert p2.follows?(p1)
  851 + refute p3.follows?(p1)
  852 + p4.follow(p1, circle4)
  853 + assert p4.follows?(p1)
851 854  
852 855 action_tracker = fast_create(ActionTracker::Record, :user_id => p1.id)
853 856 ActionTrackerNotification.delete_all
... ... @@ -880,17 +883,19 @@ class PersonTest &lt; ActiveSupport::TestCase
880 883 end
881 884 end
882 885  
883   - should "the tracked action notify friends with one delayed job process" do
  886 + should "the tracked action notify followers with one delayed job process" do
884 887 p1 = fast_create(Person)
885 888 p2 = fast_create(Person)
886 889 p3 = fast_create(Person)
887 890 p4 = fast_create(Person)
888 891  
889   - p1.add_friend(p2)
890   - assert p1.is_a_friend?(p2)
891   - refute p1.is_a_friend?(p3)
892   - p1.add_friend(p4)
893   - assert p1.is_a_friend?(p4)
  892 + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person')
  893 + circle4 = Circle.create!(:person=> p4, :name => "Zombies", :profile_type => 'Person')
  894 + p2.follow(p1, circle2)
  895 + assert p2.follows?(p1)
  896 + refute p3.follows?(p1)
  897 + p4.follow(p1, circle4)
  898 + assert p4.follows?(p1)
894 899  
895 900 action_tracker = fast_create(ActionTracker::Record, :user_id => p1.id)
896 901  
... ... @@ -1035,11 +1040,13 @@ class PersonTest &lt; ActiveSupport::TestCase
1035 1040 p2 = create_user('p2').person
1036 1041 p3 = create_user('p3').person
1037 1042 c = fast_create(Community, :name => "Foo")
  1043 +
1038 1044 c.add_member(p1)
1039 1045 process_delayed_job_queue
1040 1046 c.add_member(p3)
1041 1047 process_delayed_job_queue
1042   - assert_equal 4, ActionTracker::Record.count
  1048 +
  1049 + assert_equal 5, ActionTracker::Record.count
1043 1050 assert_equal 5, ActionTrackerNotification.count
1044 1051 has_add_member_notification = false
1045 1052 ActionTrackerNotification.all.map do |notification|
... ... @@ -1951,4 +1958,51 @@ class PersonTest &lt; ActiveSupport::TestCase
1951 1958 person.save!
1952 1959 end
1953 1960  
  1961 + should 'update profile circles for a person' do
  1962 + person = create_user('testuser').person
  1963 + community = fast_create(Community)
  1964 + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community')
  1965 + circle2 = Circle.create!(:person=> person, :name => "Dota", :profile_type => 'Community')
  1966 + circle3 = Circle.create!(:person=> person, :name => "Quadrado", :profile_type => 'Community')
  1967 + person.follow(community, [circle, circle2])
  1968 + person.update_profile_circles(community, [circle2, circle3])
  1969 + assert_equivalent [circle2, circle3], ProfileFollower.with_profile(community).with_follower(person).map(&:circle)
  1970 + end
  1971 +
  1972 + should 'a person follow a profile' do
  1973 + person = create_user('testuser').person
  1974 + community = fast_create(Community)
  1975 + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community')
  1976 + person.follow(community, circle)
  1977 + assert_includes person.followed_profiles, community
  1978 + end
  1979 +
  1980 + should 'a person follow a profile with more than one circle' do
  1981 + person = create_user('testuser').person
  1982 + community = fast_create(Community)
  1983 + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community')
  1984 + circle2 = Circle.create!(:person=> person, :name => "Dota", :profile_type => 'Community')
  1985 + person.follow(community, [circle, circle2])
  1986 + assert_includes person.followed_profiles, community
  1987 + assert_equivalent [circle, circle2], ProfileFollower.with_profile(community).with_follower(person).map(&:circle)
  1988 + end
  1989 +
  1990 + should 'a person unfollow a profile' do
  1991 + person = create_user('testuser').person
  1992 + community = fast_create(Community)
  1993 + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community')
  1994 + person.follow(community, circle)
  1995 + person.unfollow(community)
  1996 + assert_not_includes person.followed_profiles, community
  1997 + end
  1998 +
  1999 + should 'a person remove a profile from a circle' do
  2000 + person = create_user('testuser').person
  2001 + community = fast_create(Community)
  2002 + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community')
  2003 + circle2 = Circle.create!(:person=> person, :name => "Dota", :profile_type => 'Community')
  2004 + person.follow(community, [circle, circle2])
  2005 + person.remove_profile_from_circle(community, circle)
  2006 + assert_equivalent [circle2], ProfileFollower.with_profile(community).with_follower(person).map(&:circle)
  2007 + end
1954 2008 end
... ...
test/unit/profile_followers_test.rb 0 → 100644
... ... @@ -0,0 +1,73 @@
  1 +require_relative "../test_helper"
  2 +
  3 +class ProfileFollowersTest < ActiveSupport::TestCase
  4 +
  5 + should 'a person follow another' do
  6 + p1 = create_user('person_test').person
  7 + p2 = create_user('person_test_2').person
  8 + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person')
  9 +
  10 + assert_difference 'ProfileFollower.count' do
  11 + p1.follow(p2, circle)
  12 + end
  13 +
  14 + assert_includes p2.followers(true), p1
  15 + assert_not_includes p1.followers(true), p2
  16 + end
  17 +
  18 + should 'a person unfollow another person' do
  19 + p1 = create_user('person_test').person
  20 + p2 = create_user('person_test_2').person
  21 + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person')
  22 +
  23 + p1.follow(p2,circle)
  24 +
  25 + assert_difference 'ProfileFollower.count', -1 do
  26 + p1.unfollow(p2)
  27 + end
  28 +
  29 + assert_not_includes p2.followers(true), p1
  30 + end
  31 +
  32 + should 'get the followed persons for a profile' do
  33 + p1 = create_user('person_test').person
  34 + p2 = create_user('person_test_2').person
  35 + p3 = create_user('person_test_3').person
  36 + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person')
  37 +
  38 + p1.follow(p2, circle)
  39 + p1.follow(p3, circle)
  40 +
  41 + assert_equivalent p1.followed_profiles, [p2,p3]
  42 + assert_equivalent Profile.followed_by(p1), [p2,p3]
  43 + end
  44 +
  45 + should 'not follow same person twice' do
  46 + p1 = create_user('person_test').person
  47 + p2 = create_user('person_test_2').person
  48 + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person')
  49 +
  50 + assert_difference 'ProfileFollower.count' do
  51 + p1.follow(p2, circle)
  52 + p1.follow(p2, circle)
  53 + end
  54 +
  55 + assert_equivalent p1.followed_profiles, [p2]
  56 + assert_equivalent p2.followers, [p1]
  57 + end
  58 +
  59 + should 'show the correct message when a profile is followed by the same person' do
  60 + p1 = create_user('person_test').person
  61 + p2 = create_user('person_test_2').person
  62 + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person')
  63 +
  64 + p1.follow(p2, circle)
  65 + profile_follower = ProfileFollower.new
  66 + profile_follower.circle = circle
  67 + profile_follower.profile = p2
  68 + profile_follower.valid?
  69 +
  70 + assert_includes profile_follower.errors.messages[:profile_id],
  71 + "can't put a profile in the same circle twice"
  72 + end
  73 +end
... ...
test/unit/profile_test.rb
... ... @@ -2224,4 +2224,12 @@ class ProfileTest &lt; ActiveSupport::TestCase
2224 2224 assert !profile.send("allow_#{permission.gsub(/_profile/,'')}?", nil)
2225 2225 end
2226 2226 end
  2227 +
  2228 + should 'not allow to add members in secret profiles' do
  2229 + c = fast_create(Community, secret: true)
  2230 + p = create_user('mytestuser').person
  2231 + assert_raise RuntimeError do
  2232 + c.add_member(p)
  2233 + end
  2234 + end
2227 2235 end
... ...
test/unit/scrap_test.rb
... ... @@ -125,11 +125,11 @@ class ScrapTest &lt; ActiveSupport::TestCase
125 125 assert_equal c, ta.target
126 126 end
127 127  
128   - should "notify leave_scrap action tracker verb to friends and itself" do
  128 + should "notify leave_scrap action tracker verb to followers and itself" do
129 129 User.current = create_user
130 130 p1 = User.current.person
131 131 p2 = create_user.person
132   - p1.add_friend(p2)
  132 + p2.add_friend(p1)
133 133 process_delayed_job_queue
134 134 s = Scrap.new
135 135 s.sender= p1
... ... @@ -180,11 +180,11 @@ class ScrapTest &lt; ActiveSupport::TestCase
180 180 assert_equal p, ta.user
181 181 end
182 182  
183   - should "notify leave_scrap_to_self action tracker verb to friends and itself" do
  183 + should "notify leave_scrap_to_self action tracker verb to followers and itself" do
184 184 User.current = create_user
185 185 p1 = User.current.person
186 186 p2 = create_user.person
187   - p1.add_friend(p2)
  187 + p2.add_friend(p1)
188 188 ActionTrackerNotification.delete_all
189 189 Delayed::Job.delete_all
190 190 s = Scrap.new
... ...
test/unit/text_article_test.rb
... ... @@ -15,7 +15,7 @@ class TextArticleTest &lt; ActiveSupport::TestCase
15 15 end
16 16  
17 17 should 'be translatable' do
18   - assert_kind_of Noosfero::TranslatableContent, TextArticle.new
  18 + assert_kind_of TranslatableContent, TextArticle.new
19 19 end
20 20  
21 21 should 'return article icon name' do
... ...
test/unit/translatable_content_test.rb
... ... @@ -4,7 +4,7 @@ class TranslatableContentTest &lt; ActiveSupport::TestCase
4 4  
5 5 class Content
6 6 attr_accessor :parent, :profile
7   - include Noosfero::TranslatableContent
  7 + include TranslatableContent
8 8 end
9 9  
10 10 def setup
... ...
vendor/plugins/access_control/test/permission_check_test.rb
... ... @@ -4,8 +4,6 @@ class PermissionCheckTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @controller = AccessControlTestController.new
7   - @request = ActionController::TestRequest.new
8   - @response = ActionController::TestResponse.new
9 7 end
10 8  
11 9 def test_access_denied
... ...
vendor/plugins/action_tracker/test/action_tracker_test.rb
... ... @@ -54,12 +54,11 @@ ActionController::Routing::Routes.draw { |map| map.resources :things, :collectio
54 54 class ActionTrackerTest < ActiveSupport::TestCase
55 55  
56 56 def setup
  57 + @controller = ThingsController.new
  58 +
57 59 ActionTrackerConfig.current_user = proc{ SomeModel.first || SomeModel.create! }
58 60 ActionTracker::Record.delete_all
59 61 ActionTrackerConfig.verbs = { :some_verb => { :description => "Did something" } }
60   - @request = ActionController::TestRequest.new
61   - @response = ActionController::TestResponse.new
62   - @controller = ThingsController.new
63 62 end
64 63  
65 64 def test_index
... ...