Compare View

switch
from
...
to
 
Commits (54)
Showing 300 changed files   Show diff stats

Too many changes.

To preserve performance only 100 of 300 files displayed.

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