Commit 0a7afb0f39bee4f3cdb40562ef6fd4995f295142

Authored by Larissa Reis
2 parents fa46a8f5 53a2c9a9

Merge branch 'master' into federation

Showing 295 changed files with 3841 additions and 1771 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 295 files displayed.

app/api/entities.rb
... ... @@ -111,6 +111,10 @@ module Api
111 111 hash[value.custom_field.name]=value.value
112 112 end
113 113  
  114 + profile.public_fields.each do |field|
  115 + hash[field] = profile.send(field.to_sym)
  116 + end
  117 +
114 118 private_values = profile.custom_field_values - profile.public_values
115 119 private_values.each do |value|
116 120 if Entities.can_display_profile_field?(profile,options)
... ... @@ -124,6 +128,7 @@ module Api
124 128 expose :type
125 129 expose :custom_header
126 130 expose :custom_footer
  131 + expose :layout_template
127 132 expose :permissions do |profile, options|
128 133 Entities.permissions_for_entity(profile, options[:current_person],
129 134 :allow_post_content?, :allow_edit?, :allow_destroy?)
... ... @@ -258,12 +263,28 @@ module Api
258 263 root 'tasks', 'task'
259 264 expose :id
260 265 expose :type
  266 + expose :requestor, using: Profile
  267 + expose :status
  268 + expose :created_at
  269 + expose :data
  270 + expose :accept_details
  271 + expose :reject_details
  272 + expose :accept_disabled?, as: :accept_disabled
  273 + expose :reject_disabled?, as: :reject_disabled
  274 + expose :target do |task, options|
  275 + type_map = {Profile => ::Profile, Environment => ::Environment}.find {|h| task.target.kind_of?(h.last)}
  276 + type_map.first.represent(task.target) unless type_map.nil?
  277 + end
261 278 end
262 279  
263 280 class Environment < Entity
264 281 expose :name
265 282 expose :id
266 283 expose :description
  284 + expose :layout_template
  285 + expose :signup_intro
  286 + expose :terms_of_use
  287 + expose :top_url, as: :host
267 288 expose :settings, if: lambda { |instance, options| options[:is_admin] }
268 289 end
269 290  
... ...
app/api/helpers.rb
... ... @@ -4,7 +4,7 @@ require &#39;tempfile&#39;
4 4 module Api
5 5 module Helpers
6 6 PRIVATE_TOKEN_PARAM = :private_token
7   - DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type, :author_id, :identifier, :archived]
  7 + ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type, :author_id, :identifier, :archived, :status]
8 8  
9 9 include SanitizeParams
10 10 include Noosfero::Plugin::HotSpot
... ... @@ -127,8 +127,8 @@ module Api
127 127 present_partial article, with: Entities::Article, params: params, current_person: current_person
128 128 end
129 129  
130   - def present_articles_for_asset(asset, method = 'articles')
131   - articles = find_articles(asset, method)
  130 + def present_articles_for_asset(asset, method_or_relation = 'articles')
  131 + articles = find_articles(asset, method_or_relation)
132 132 present_articles(articles)
133 133 end
134 134  
... ... @@ -136,8 +136,8 @@ module Api
136 136 present_partial paginate(articles), :with => Entities::Article, :params => params, current_person: current_person
137 137 end
138 138  
139   - def find_articles(asset, method = 'articles')
140   - articles = select_filtered_collection_of(asset, method, params)
  139 + def find_articles(asset, method_or_relation = 'articles')
  140 + articles = select_filtered_collection_of(asset, method_or_relation, params)
141 141 if current_person.present?
142 142 articles = articles.display_filter(current_person, nil)
143 143 else
... ... @@ -146,14 +146,17 @@ module Api
146 146 articles
147 147 end
148 148  
149   - def find_task(asset, id)
150   - task = asset.tasks.find(id)
  149 + def find_task(asset, method_or_relation, id)
  150 + task = is_a_relation?(method_or_relation) ? method_or_relation : asset.send(method_or_relation)
  151 + task = task.find_by_id(id)
  152 + not_found! if task.blank?
151 153 current_person.has_permission?(task.permission, asset) ? task : forbidden!
152 154 end
153 155  
154 156 def post_task(asset, params)
155 157 klass_type= params[:content_type].nil? ? 'Task' : params[:content_type]
156 158 return forbidden! unless klass_type.constantize <= Task
  159 + return forbidden! if !current_person.has_permission?(:perform_task, asset)
157 160  
158 161 task = klass_type.constantize.new(params[:task])
159 162 task.requestor_id = current_person.id
... ... @@ -166,15 +169,24 @@ module Api
166 169 present_partial task, :with => Entities::Task
167 170 end
168 171  
169   - def present_task(asset)
170   - task = find_task(asset, params[:id])
  172 + def find_tasks(asset, method_or_relation = 'tasks')
  173 + return forbidden! if !current_person.has_permission?(:perform_task, asset)
  174 + tasks = select_filtered_collection_of(asset, method_or_relation, params)
  175 + tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)}
  176 + tasks
  177 + end
  178 +
  179 + def present_task(asset, method_or_relation = 'tasks')
  180 + task = find_task(asset, method_or_relation, params[:id])
171 181 present_partial task, :with => Entities::Task
172 182 end
173 183  
174   - def present_tasks(asset)
175   - tasks = select_filtered_collection_of(asset, 'tasks', params)
176   - tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)}
177   - return forbidden! if tasks.empty? && !current_person.has_permission?(:perform_task, asset)
  184 + def present_tasks_for_asset(asset, method_or_relation = 'tasks')
  185 + tasks = find_tasks(asset, method_or_relation)
  186 + present_tasks(tasks)
  187 + end
  188 +
  189 + def present_tasks(tasks)
178 190 present_partial tasks, :with => Entities::Task
179 191 end
180 192  
... ... @@ -183,7 +195,6 @@ module Api
183 195 conditions = {}
184 196 from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from]
185 197 until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until]
186   -
187 198 conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil?
188 199  
189 200 conditions[:created_at] = period(from_date, until_date) if from_date || until_date
... ... @@ -193,7 +204,7 @@ module Api
193 204 end
194 205  
195 206 # changing make_order_with_parameters to avoid sql injection
196   - def make_order_with_parameters(object, method, params)
  207 + def make_order_with_parameters(object, method_or_relation, params)
197 208 order = "created_at DESC"
198 209 unless params[:order].blank?
199 210 if params[:order].include? '\'' or params[:order].include? '"'
... ... @@ -202,9 +213,9 @@ module Api
202 213 order = 'RANDOM()'
203 214 else
204 215 field_name, direction = params[:order].split(' ')
205   - assoc = object.class.reflect_on_association(method.to_sym)
206   - if !field_name.blank? and assoc
207   - if assoc.klass.attribute_names.include? field_name
  216 + assoc_class = extract_associated_classname(method_or_relation)
  217 + if !field_name.blank? and assoc_class
  218 + if assoc_class.attribute_names.include? field_name
208 219 if direction.present? and ['ASC','DESC'].include? direction.upcase
209 220 order = "#{field_name} #{direction.upcase}"
210 221 end
... ... @@ -215,12 +226,14 @@ module Api
215 226 return order
216 227 end
217 228  
218   - def make_timestamp_with_parameters_and_method(params, method)
  229 + def make_timestamp_with_parameters_and_method(params, method_or_relation)
219 230 timestamp = nil
220 231 if params[:timestamp]
221 232 datetime = DateTime.parse(params[:timestamp])
222   - table_name = method.to_s.singularize.camelize.constantize.table_name
223   - timestamp = "#{table_name}.updated_at >= '#{datetime}'"
  233 + table_name = extract_associated_tablename(method_or_relation)
  234 + assoc_class = extract_associated_classname(method_or_relation)
  235 + date_atrr = assoc_class.attribute_names.include?('updated_at') ? 'updated_at' : 'created_at'
  236 + timestamp = "#{table_name}.#{date_atrr} >= '#{datetime}'"
224 237 end
225 238  
226 239 timestamp
... ... @@ -245,12 +258,12 @@ module Api
245 258 end
246 259 end
247 260  
248   - def select_filtered_collection_of(object, method, params)
  261 + def select_filtered_collection_of(object, method_or_relation, params)
249 262 conditions = make_conditions_with_parameter(params)
250   - order = make_order_with_parameters(object,method,params)
251   - timestamp = make_timestamp_with_parameters_and_method(params, method)
  263 + order = make_order_with_parameters(object,method_or_relation,params)
  264 + timestamp = make_timestamp_with_parameters_and_method(params, method_or_relation)
252 265  
253   - objects = object.send(method)
  266 + objects = is_a_relation?(method_or_relation) ? method_or_relation : object.send(method_or_relation)
254 267 objects = by_reference(objects, params)
255 268 objects = by_categories(objects, params)
256 269  
... ... @@ -305,12 +318,12 @@ module Api
305 318 end
306 319  
307 320 def cant_be_saved_request!(attribute)
308   - message = _("(Invalid request) %s can't be saved") % attribute
  321 + message = _("(Invalid request) %s can't be saved").html_safe % attribute
309 322 render_api_error!(message, 400)
310 323 end
311 324  
312 325 def bad_request!(attribute)
313   - message = _("(Invalid request) %s not given") % attribute
  326 + message = _("(Invalid request) %s not given").html_safe % attribute
314 327 render_api_error!(message, 400)
315 328 end
316 329  
... ... @@ -396,10 +409,27 @@ module Api
396 409 end
397 410 private
398 411  
  412 + def extract_associated_tablename(method_or_relation)
  413 + extract_associated_classname(method_or_relation).table_name
  414 + end
  415 +
  416 + def extract_associated_classname(method_or_relation)
  417 + if is_a_relation?(method_or_relation)
  418 + method_or_relation.blank? ? '' : method_or_relation.first.class
  419 + else
  420 + method_or_relation.to_s.singularize.camelize.constantize
  421 + end
  422 + end
  423 +
  424 + def is_a_relation?(method_or_relation)
  425 + method_or_relation.kind_of?(ActiveRecord::Relation)
  426 + end
  427 +
  428 +
399 429 def parser_params(params)
400 430 parsed_params = {}
401 431 params.map do |k,v|
402   - parsed_params[k.to_sym] = v if DEFAULT_ALLOWED_PARAMETERS.include?(k.to_sym)
  432 + parsed_params[k.to_sym] = v if ALLOWED_PARAMETERS.include?(k.to_sym)
403 433 end
404 434 parsed_params
405 435 end
... ...
app/api/v1/people.rb
... ... @@ -136,6 +136,20 @@ module Api
136 136 members = select_filtered_collection_of(profile, 'members', params)
137 137 present members, :with => Entities::Person, :current_person => current_person
138 138 end
  139 +
  140 + post do
  141 + authenticate!
  142 + profile = environment.profiles.find_by id: params[:profile_id]
  143 + profile.add_member(current_person) rescue forbidden!
  144 + {pending: !current_person.is_member_of?(profile)}
  145 + end
  146 +
  147 + delete do
  148 + authenticate!
  149 + profile = environment.profiles.find_by id: params[:profile_id]
  150 + profile.remove_member(current_person)
  151 + present current_person, :with => Entities::Person, :current_person => current_person
  152 + end
139 153 end
140 154 end
141 155 end
... ...
app/api/v1/tasks.rb
1 1 module Api
2 2 module V1
3 3 class Tasks < Grape::API
4   -# before { authenticate! }
  4 + before { authenticate! }
5 5  
6   -# ARTICLE_TYPES = Article.descendants.map{|a| a.to_s}
  6 + MAX_PER_PAGE = 50
7 7  
8 8 resource :tasks do
9 9  
10   - # Collect tasks
  10 + paginate max_per_page: MAX_PER_PAGE
  11 + # Collect all tasks that current person has permission
11 12 #
12 13 # Parameters:
13 14 # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created
... ... @@ -17,15 +18,22 @@ module Api
17 18 # Example Request:
18 19 # GET host/api/v1/tasks?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317
19 20 get do
20   - tasks = select_filtered_collection_of(environment, 'tasks', params)
21   - tasks = tasks.select {|t| current_person.has_permission?(t.permission, environment)}
22   - present_partial tasks, :with => Entities::Task
  21 + tasks = Task.to(current_person)
  22 + present_tasks_for_asset(current_person, tasks)
23 23 end
24 24  
25 25 desc "Return the task id"
26 26 get ':id' do
27   - task = find_task(environment, params[:id])
28   - present_partial task, :with => Entities::Task
  27 + present_task(current_person, Task.to(current_person))
  28 + end
  29 +
  30 + %w[finish cancel].each do |action|
  31 + desc "#{action.capitalize} a task"
  32 + put ":id/#{action}" do
  33 + task = find_task(current_person, Task.to(current_person), params[:id])
  34 + task.send(action, current_person) if (task.status == Task::Status::ACTIVE)
  35 + present_partial task, :with => Entities::Task
  36 + end
29 37 end
30 38 end
31 39  
... ... @@ -36,7 +44,8 @@ module Api
36 44 resource :tasks do
37 45 get do
38 46 profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
39   - present_tasks(profile)
  47 + tasks = Task.to(profile)
  48 + present_tasks_for_asset(profile, tasks)
40 49 end
41 50  
42 51 get ':id' do
... ...
app/concerns/authenticated_system.rb
... ... @@ -1,185 +0,0 @@
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 = nil
29   - if session[:external]
30   - user = User.new
31   - external_person = ExternalPerson.find_by(id: session[:external])
32   - if external_person
33   - user.external_person_id = external_person.id
34   - user.email = external_person.email
35   - else
36   - session[:external] = nil
37   - end
38   - else
39   - user = User.find_by(id: user_id) if user_id
40   - end
41   - user.session = session if user
42   - User.current = user
43   - user
44   - end
45   - end
46   -
47   - # Store the given user in the session.
48   - def current_user=(new_user)
49   - if new_user.nil?
50   - session.delete(:user)
51   - else
52   - if new_user.id
53   - session[:user] = new_user.id
54   - else
55   - session[:external] = new_user.external_person_id
56   - end
57   - new_user.session = session
58   - new_user.register_login if new_user.id
59   - end
60   - @current_user = User.current = new_user
61   - end
62   -
63   - # See impl. from http://stackoverflow.com/a/2513456/670229
64   - def user_set_current
65   - User.current = current_user
66   - yield
67   - ensure
68   - # to address the thread variable leak issues in Puma/Thin webserver
69   - User.current = nil
70   - end
71   -
72   - # Check if the user is authorized.
73   - #
74   - # Override this method in your controllers if you want to restrict access
75   - # to only a few actions or if you want to check if the user
76   - # has the correct rights.
77   - #
78   - # Example:
79   - #
80   - # # only allow nonbobs
81   - # def authorize?
82   - # current_user.login != "bob"
83   - # end
84   - def authorized?
85   - true
86   - end
87   -
88   - # Filter method to enforce a login requirement.
89   - #
90   - # To require logins for all actions, use this in your controllers:
91   - #
92   - # before_filter :login_required
93   - #
94   - # To require logins for specific actions, use this in your controllers:
95   - #
96   - # before_filter :login_required, :only => [ :edit, :update ]
97   - #
98   - # To skip this in a subclassed controller:
99   - #
100   - # skip_before_filter :login_required
101   - #
102   - def login_required
103   - username, passwd = get_auth_data
104   - if username && passwd
105   - self.current_user ||= User.authenticate(username, passwd) || nil
106   - end
107   - if logged_in? && authorized?
108   - true
109   - else
110   - if params[:require_login_popup]
111   - render :json => { :require_login_popup => true }
112   - else
113   - access_denied
114   - end
115   - end
116   - end
117   -
118   - # Redirect as appropriate when an access request fails.
119   - #
120   - # The default action is to redirect to the login screen.
121   - #
122   - # Override this method in your controllers if you want to have special
123   - # behavior in case the user is not authorized
124   - # to access the requested action. For example, a popup window might
125   - # simply close itself.
126   - def access_denied
127   - respond_to do |accepts|
128   - accepts.html do
129   - if request.xhr?
130   - render :text => _('Access denied'), :status => 401
131   - else
132   - store_location
133   - redirect_to :controller => '/account', :action => 'login'
134   - end
135   - end
136   - accepts.xml do
137   - headers["Status"] = "Unauthorized"
138   - headers["WWW-Authenticate"] = %(Basic realm="Web Password")
139   - render :text => "Could't authenticate you", :status => '401 Unauthorized'
140   - end
141   - end
142   - false
143   - end
144   -
145   - # Store the URI of the current request in the session.
146   - #
147   - # We can return to this location by calling #redirect_back_or_default.
148   - def store_location(location = request.url)
149   - session[:return_to] = location
150   - end
151   -
152   - # Redirect to the URI stored by the most recent store_location call or
153   - # to the passed default.
154   - def redirect_back_or_default(default)
155   - if session[:return_to]
156   - redirect_to(session.delete(:return_to))
157   - else
158   - redirect_to(default)
159   - end
160   - end
161   -
162   - def override_user
163   - return if params[:override_user].blank?
164   - return unless logged_in? and user.is_admin? environment
165   - @current_user = nil
166   - current_user params[:override_user]
167   - end
168   -
169   - # When called with before_filter :login_from_cookie will check for an :auth_token
170   - # cookie and log the user back in if apropriate
171   - def login_from_cookie
172   - return if cookies[:auth_token].blank? or logged_in?
173   - user = User.where(remember_token: cookies[:auth_token]).first
174   - self.current_user = user if user and user.remember_token?
175   - end
176   -
177   - private
178   - @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
179   - # gets BASIC auth info
180   - def get_auth_data
181   - auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
182   - auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
183   - return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
184   - end
185   -end
app/controllers/admin/categories_controller.rb
... ... @@ -44,7 +44,7 @@ class CategoriesController &lt; 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
... ... @@ -15,6 +15,20 @@ class ApplicationController &lt; ActionController::Base
15 15 before_filter :redirect_to_current_user
16 16  
17 17 before_filter :set_session_theme
  18 +
  19 + # FIXME: only include necessary methods
  20 + include ApplicationHelper
  21 +
  22 + # concerns
  23 + include PermissionCheck
  24 + include CustomDesign
  25 + include NeedsProfile
  26 +
  27 + # implementations
  28 + include FindByContents
  29 + include Noosfero::Plugin::HotSpot
  30 + include SearchTermHelper
  31 +
18 32 def set_session_theme
19 33 if params[:theme]
20 34 session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil
... ... @@ -49,7 +63,6 @@ class ApplicationController &lt; ActionController::Base
49 63 end
50 64 end
51 65  
52   - include ApplicationHelper
53 66 layout :get_layout
54 67 def get_layout
55 68 return false if request.format == :js or request.xhr?
... ... @@ -75,9 +88,6 @@ class ApplicationController &lt; ActionController::Base
75 88 helper :document
76 89 helper :language
77 90  
78   - include DesignHelper
79   - include PermissionCheck
80   -
81 91 before_filter :set_locale
82 92 def set_locale
83 93 FastGettext.available_locales = environment.available_locales
... ... @@ -90,8 +100,6 @@ class ApplicationController &lt; ActionController::Base
90 100 end
91 101 end
92 102  
93   - include NeedsProfile
94   -
95 103 attr_reader :environment
96 104  
97 105 # declares that the given <tt>actions</tt> cannot be accessed by other HTTP
... ... @@ -108,6 +116,10 @@ class ApplicationController &lt; ActionController::Base
108 116  
109 117 protected
110 118  
  119 + def accept_only_post
  120 + return render_not_found if !request.post?
  121 + end
  122 +
111 123 def verified_request?
112 124 super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
113 125 end
... ... @@ -154,8 +166,6 @@ class ApplicationController &lt; ActionController::Base
154 166 end
155 167 end
156 168  
157   - include Noosfero::Plugin::HotSpot
158   -
159 169 # FIXME this filter just loads @plugins to children controllers and helpers
160 170 def init_noosfero_plugins
161 171 plugins
... ... @@ -187,9 +197,6 @@ class ApplicationController &lt; ActionController::Base
187 197 end
188 198 end
189 199  
190   - include SearchTermHelper
191   - include FindByContents
192   -
193 200 def find_suggestions(query, context, asset, options={})
194 201 plugins.dispatch_first(:find_suggestions, query, context, asset, options)
195 202 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,185 @@
  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 = nil
  29 + if session[:external]
  30 + user = User.new
  31 + external_person = ExternalPerson.find_by(id: session[:external])
  32 + if external_person
  33 + user.external_person_id = external_person.id
  34 + user.email = external_person.email
  35 + else
  36 + session[:external] = nil
  37 + end
  38 + else
  39 + user = User.find_by(id: user_id) if user_id
  40 + end
  41 + user.session = session if user
  42 + User.current = user
  43 + user
  44 + end
  45 + end
  46 +
  47 + # Store the given user in the session.
  48 + def current_user=(new_user)
  49 + if new_user.nil?
  50 + session.delete(:user)
  51 + else
  52 + if new_user.id
  53 + session[:user] = new_user.id
  54 + else
  55 + session[:external] = new_user.external_person_id
  56 + end
  57 + new_user.session = session
  58 + new_user.register_login if new_user.id
  59 + end
  60 + @current_user = User.current = new_user
  61 + end
  62 +
  63 + # See impl. from http://stackoverflow.com/a/2513456/670229
  64 + def user_set_current
  65 + User.current = current_user
  66 + yield
  67 + ensure
  68 + # to address the thread variable leak issues in Puma/Thin webserver
  69 + User.current = nil
  70 + end
  71 +
  72 + # Check if the user is authorized.
  73 + #
  74 + # Override this method in your controllers if you want to restrict access
  75 + # to only a few actions or if you want to check if the user
  76 + # has the correct rights.
  77 + #
  78 + # Example:
  79 + #
  80 + # # only allow nonbobs
  81 + # def authorize?
  82 + # current_user.login != "bob"
  83 + # end
  84 + def authorized?
  85 + true
  86 + end
  87 +
  88 + # Filter method to enforce a login requirement.
  89 + #
  90 + # To require logins for all actions, use this in your controllers:
  91 + #
  92 + # before_filter :login_required
  93 + #
  94 + # To require logins for specific actions, use this in your controllers:
  95 + #
  96 + # before_filter :login_required, :only => [ :edit, :update ]
  97 + #
  98 + # To skip this in a subclassed controller:
  99 + #
  100 + # skip_before_filter :login_required
  101 + #
  102 + def login_required
  103 + username, passwd = get_auth_data
  104 + if username && passwd
  105 + self.current_user ||= User.authenticate(username, passwd) || nil
  106 + end
  107 + if logged_in? && authorized?
  108 + true
  109 + else
  110 + if params[:require_login_popup]
  111 + render :json => { :require_login_popup => true }
  112 + else
  113 + access_denied
  114 + end
  115 + end
  116 + end
  117 +
  118 + # Redirect as appropriate when an access request fails.
  119 + #
  120 + # The default action is to redirect to the login screen.
  121 + #
  122 + # Override this method in your controllers if you want to have special
  123 + # behavior in case the user is not authorized
  124 + # to access the requested action. For example, a popup window might
  125 + # simply close itself.
  126 + def access_denied
  127 + respond_to do |accepts|
  128 + accepts.html do
  129 + if request.xhr?
  130 + render :text => _('Access denied'), :status => 401
  131 + else
  132 + store_location
  133 + redirect_to :controller => '/account', :action => 'login'
  134 + end
  135 + end
  136 + accepts.xml do
  137 + headers["Status"] = "Unauthorized"
  138 + headers["WWW-Authenticate"] = %(Basic realm="Web Password")
  139 + render :text => "Could't authenticate you", :status => '401 Unauthorized'
  140 + end
  141 + end
  142 + false
  143 + end
  144 +
  145 + # Store the URI of the current request in the session.
  146 + #
  147 + # We can return to this location by calling #redirect_back_or_default.
  148 + def store_location(location = request.url)
  149 + session[:return_to] = location
  150 + end
  151 +
  152 + # Redirect to the URI stored by the most recent store_location call or
  153 + # to the passed default.
  154 + def redirect_back_or_default(default)
  155 + if session[:return_to]
  156 + redirect_to(session.delete(:return_to))
  157 + else
  158 + redirect_to(default)
  159 + end
  160 + end
  161 +
  162 + def override_user
  163 + return if params[:override_user].blank?
  164 + return unless logged_in? and user.is_admin? environment
  165 + @current_user = nil
  166 + current_user params[:override_user]
  167 + end
  168 +
  169 + # When called with before_filter :login_from_cookie will check for an :auth_token
  170 + # cookie and log the user back in if apropriate
  171 + def login_from_cookie
  172 + return if cookies[:auth_token].blank? or logged_in?
  173 + user = User.where(remember_token: cookies[:auth_token]).first
  174 + self.current_user = user if user and user.remember_token?
  175 + end
  176 +
  177 + private
  178 + @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
  179 + # gets BASIC auth info
  180 + def get_auth_data
  181 + auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
  182 + auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
  183 + return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
  184 + end
  185 +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/my_profile/tasks_controller.rb
... ... @@ -14,7 +14,7 @@ class TasksController &lt; MyProfileController
14 14 @filter_text = params[:filter_text].presence
15 15 @filter_responsible = params[:filter_responsible]
16 16 @task_types = Task.pending_types_for(profile)
17   - @tasks = Task.pending_all(profile, @filter_type, @filter_text).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
  17 + @tasks = Task.pending_all_by_filter(profile, @filter_type, @filter_text).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
18 18 @tasks = @tasks.where(:responsible_id => @filter_responsible.to_i != -1 ? @filter_responsible : nil) if @filter_responsible.present?
19 19 @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page])
20 20 @failed = params ? params[:failed] : {}
... ... @@ -101,7 +101,7 @@ class TasksController &lt; MyProfileController
101 101 def search_tasks
102 102 filter_type = params[:filter_type].presence
103 103 filter_text = params[:filter_text].presence
104   - result = Task.pending_all(profile,filter_type, filter_text)
  104 + result = Task.pending_all_by_filter(profile,filter_type, filter_text)
105 105  
106 106 render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} }
107 107 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_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.')
... ... @@ -448,4 +487,8 @@ class ProfileController &lt; PublicController
448 487 [:image, :domains, :preferred_domain, :environment]
449 488 end
450 489  
  490 + def allow_followers?
  491 + render_not_found unless profile.allow_followers?
  492 + end
  493 +
451 494 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/boxes_helper.rb
... ... @@ -285,7 +285,7 @@ module BoxesHelper
285 285 end
286 286  
287 287 if block.respond_to?(:help)
288   - buttons << modal_inline_icon(:help, _('Help on this block'), {}, "#help-on-box-#{block.id}") << content_tag('div', content_tag('h2', _('Help')) + content_tag('div', block.help, :style => 'margin-bottom: 1em;') + modal_close_button(_('Close')), :style => 'display: none;', :id => "help-on-box-#{block.id}")
  288 + buttons << modal_inline_icon(:help, _('Help on this block'), {}, "#help-on-box-#{block.id}") << content_tag('div', content_tag('h2', _('Help')) + content_tag('div', block.help.html_safe, :style => 'margin-bottom: 1em;') + modal_close_button(_('Close')), :style => 'display: none;', :id => "help-on-box-#{block.id}")
289 289 end
290 290  
291 291 if block.embedable?
... ...
app/helpers/design_helper.rb
... ... @@ -1,50 +0,0 @@
1   -module DesignHelper
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/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
... ... @@ -203,6 +203,7 @@ class Environment &lt; ApplicationRecord
203 203 # Relationships and applied behaviour
204 204 # #################################################
205 205  
  206 + extend ActsAsHavingBoxes::ClassMethods
206 207 acts_as_having_boxes
207 208  
208 209 after_create do |env|
... ... @@ -254,7 +255,8 @@ class Environment &lt; ApplicationRecord
254 255 # #################################################
255 256  
256 257 # store the Environment settings as YAML-serialized Hash.
257   - acts_as_having_settings :field => :settings
  258 + extend ActsAsHavingSettings::ClassMethods
  259 + acts_as_having_settings field: :settings
258 260  
259 261 # introduce and explain to users something about the signup
260 262 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,22 @@ 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 + groups = friendship.group.blank? ? ['friendships'] : friendship.group.split(',').map(&:strip)
  24 + groups.each do |group|
  25 + circle = Circle.find_by(:person => friendship.person, :name => group )
  26 + friendship.person.remove_profile_from_circle(friendship.friend, circle) if circle
  27 + end
17 28 end
18 29  
19 30 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
... ... @@ -10,7 +10,6 @@ class Person &lt; Profile
10 10 :display => %w[compact]
11 11 }
12 12  
13   -
14 13 def self.type_name
15 14 _('Person')
16 15 end
... ... @@ -94,6 +93,7 @@ class Person &lt; Profile
94 93 has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article
95 94 has_many :friendships, :dependent => :destroy
96 95 has_many :friends, :class_name => 'Person', :through => :friendships
  96 + has_many :circles
97 97  
98 98 scope :online, -> {
99 99 joins(:user).where("users.chat_status != '' AND users.chat_status_at >= ?", DateTime.now - User.expires_chat_status_every.minutes)
... ... @@ -181,6 +181,33 @@ class Person &lt; Profile
181 181 end
182 182 end
183 183  
  184 + def follow(profile, circles)
  185 + circles = [circles] unless circles.is_a?(Array)
  186 + circles.each do |new_circle|
  187 + ProfileFollower.create(profile: profile, circle: new_circle)
  188 + end
  189 + end
  190 +
  191 + def update_profile_circles(profile, new_circles)
  192 + profile_circles = ProfileFollower.with_profile(profile).with_follower(self).map(&:circle)
  193 + circles_to_add = new_circles - profile_circles
  194 + circles_to_remove = profile_circles - new_circles
  195 + circles_to_add.each do |new_circle|
  196 + ProfileFollower.create(profile: profile, circle: new_circle)
  197 + end
  198 +
  199 + ProfileFollower.where('circle_id IN (?) AND profile_id = ?',
  200 + circles_to_remove.map(&:id), profile.id).destroy_all
  201 + end
  202 +
  203 + def unfollow(profile)
  204 + ProfileFollower.with_follower(self).with_profile(profile).destroy_all
  205 + end
  206 +
  207 + def remove_profile_from_circle(profile, circle)
  208 + ProfileFollower.with_profile(profile).with_circle(circle).destroy_all
  209 + end
  210 +
184 211 def already_request_friendship?(person)
185 212 person.tasks.where(requestor_id: self.id, type: 'AddFriend', status: Task::Status::ACTIVE).first
186 213 end
... ... @@ -574,9 +601,12 @@ class Person &lt; Profile
574 601 }
575 602 end
576 603  
577   - protected
  604 + def followed_profiles
  605 + Profile.followed_by self
  606 + end
578 607  
579   - def followed_by?(profile)
580   - self == profile || self.is_a_friend?(profile)
  608 + def in_social_circle?(person)
  609 + self.is_a_friend?(person) || super
581 610 end
  611 +
582 612 end
... ...
app/models/profile.rb
... ... @@ -6,7 +6,7 @@ class Profile &lt; ApplicationRecord
6 6 include ProfileEntity
7 7  
8 8 attr_accessible :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time,
9   - :custom_url_redirection, :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification, :redirection_after_login
  9 + :custom_url_redirection, :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification, :redirection_after_login, :allow_followers
10 10  
11 11 # use for internationalizable human type names in search facets
12 12 # reimplement on subclasses
... ... @@ -89,6 +89,8 @@ class Profile &lt; ApplicationRecord
89 89 }
90 90  
91 91 acts_as_accessible
  92 +
  93 + include Customizable
92 94 acts_as_customizable
93 95  
94 96 include Noosfero::Plugin::HotSpot
... ... @@ -184,6 +186,21 @@ class Profile &lt; ApplicationRecord
184 186 Person.members_of(self).by_role(roles)
185 187 end
186 188  
  189 + extend ActsAsHavingSettings::ClassMethods
  190 + acts_as_having_settings field: :data
  191 +
  192 + def settings
  193 + data
  194 + end
  195 +
  196 + settings_items :redirect_l10n, :type => :boolean, :default => false
  197 + settings_items :public_content, :type => :boolean, :default => true
  198 + settings_items :description
  199 + settings_items :fields_privacy, :type => :hash, :default => {}
  200 + settings_items :email_suggestions, :type => :boolean, :default => false
  201 + settings_items :profile_admin_mail_notification, :type => :boolean, :default => true
  202 +
  203 + extend ActsAsHavingBoxes::ClassMethods
187 204 acts_as_having_boxes
188 205  
189 206 acts_as_taggable
... ... @@ -202,6 +219,23 @@ class Profile &lt; ApplicationRecord
202 219 scope :more_active, -> { order 'activities_count DESC' }
203 220 scope :more_recent, -> { order "created_at DESC" }
204 221  
  222 + scope :followed_by, -> person{
  223 + distinct.select('profiles.*').
  224 + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id').
  225 + joins('left join circles ON circles.id = profiles_circles.circle_id').
  226 + where('circles.person_id = ?', person.id)
  227 + }
  228 +
  229 + scope :in_circle, -> circle{
  230 + distinct.select('profiles.*').
  231 + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id').
  232 + joins('left join circles ON circles.id = profiles_circles.circle_id').
  233 + where('circles.id = ?', circle.id)
  234 + }
  235 +
  236 + settings_items :allow_followers, :type => :boolean, :default => true
  237 + alias_method :allow_followers?, :allow_followers
  238 +
205 239 acts_as_trackable :dependent => :destroy
206 240  
207 241 has_many :profile_activities
... ... @@ -214,6 +248,9 @@ class Profile &lt; ApplicationRecord
214 248  
215 249 has_many :email_templates, :foreign_key => :owner_id
216 250  
  251 + has_many :profile_followers
  252 + has_many :followers, -> { uniq }, :class_name => 'Person', :through => :profile_followers, :source => :person
  253 +
217 254 # Although this should be a has_one relation, there are no non-silly names for
218 255 # a foreign key on article to reference the template to which it is
219 256 # welcome_page... =P
... ... @@ -228,19 +265,6 @@ class Profile &lt; ApplicationRecord
228 265 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap)
229 266 end
230 267  
231   - acts_as_having_settings :field => :data
232   -
233   - def settings
234   - data
235   - end
236   -
237   - settings_items :redirect_l10n, :type => :boolean, :default => false
238   - settings_items :public_content, :type => :boolean, :default => true
239   - settings_items :description
240   - settings_items :fields_privacy, :type => :hash, :default => {}
241   - settings_items :email_suggestions, :type => :boolean, :default => false
242   - settings_items :profile_admin_mail_notification, :type => :boolean, :default => true
243   -
244 268 validates_length_of :description, :maximum => 550, :allow_nil => true
245 269  
246 270 # Valid identifiers must match this format.
... ... @@ -281,6 +305,7 @@ class Profile &lt; ApplicationRecord
281 305  
282 306 has_many :files, :class_name => 'UploadedFile'
283 307  
  308 + extend ActsAsHavingImage::ClassMethods
284 309 acts_as_having_image
285 310  
286 311 has_many :tasks, :dependent => :destroy, :as => 'target'
... ... @@ -695,12 +720,13 @@ private :generate_url, :url_options
695 720  
696 721 # Adds a person as member of this Profile.
697 722 def add_member(person, attributes={})
698   - if self.has_members?
  723 + if self.has_members? && !self.secret
699 724 if self.closed? && members.count > 0
700 725 AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person)
701 726 else
702 727 self.affiliate(person, Profile::Roles.admin(environment.id), attributes) if members.count == 0
703 728 self.affiliate(person, Profile::Roles.member(environment.id), attributes)
  729 + person.follow(self, Circle.find_or_create_by(:person => person, :name =>_('memberships'), :profile_type => 'Community'))
704 730 end
705 731 person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel }
706 732 remove_from_suggestion_list person
... ... @@ -983,7 +1009,11 @@ private :generate_url, :url_options
983 1009 end
984 1010  
985 1011 def followed_by?(person)
986   - person.is_member_of?(self)
  1012 + (person == self) || (person.in? self.followers)
  1013 + end
  1014 +
  1015 + def in_social_circle?(person)
  1016 + (person == self) || (person.is_member_of?(self))
987 1017 end
988 1018  
989 1019 def display_private_info_to?(user)
... ... @@ -1009,4 +1039,23 @@ private :generate_url, :url_options
1009 1039 suggestion.disable if suggestion
1010 1040 end
1011 1041  
  1042 + def allow_invitation_from(person)
  1043 + false
  1044 + end
  1045 +
  1046 + def allow_post_content?(person = nil)
  1047 + person.kind_of?(Profile) && person.has_permission?('post_content', self)
  1048 + end
  1049 +
  1050 + def allow_edit?(person = nil)
  1051 + person.kind_of?(Profile) && person.has_permission?('edit_profile', self)
  1052 + end
  1053 +
  1054 + def allow_destroy?(person = nil)
  1055 + person.kind_of?(Profile) && person.has_permission?('destroy_profile', self)
  1056 + end
  1057 +
  1058 + def in_circle?(circle, follower)
  1059 + ProfileFollower.with_follower(follower).with_circle(circle).with_profile(self).present?
  1060 + end
1012 1061 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
... ... @@ -313,7 +314,7 @@ class Task &lt; ApplicationRecord
313 314 where "LOWER(#{field}) LIKE ?", "%#{value.downcase}%"
314 315 end
315 316 }
316   - scope :pending_all, -> profile, filter_type, filter_text {
  317 + scope :pending_all_by_filter, -> profile, filter_type, filter_text {
317 318 self.to(profile).without_spam.pending.of(filter_type).like('data', filter_text)
318 319 }
319 320  
... ... @@ -322,10 +323,17 @@ class Task &lt; ApplicationRecord
322 323 if profile.person?
323 324 envs_ids = Environment.all.select{ |env| profile.is_admin?(env) }.map{ |env| "target_id = #{env.id}"}.join(' OR ')
324 325 environment_condition = envs_ids.blank? ? nil : "(target_type = 'Environment' AND (#{envs_ids}))"
  326 +
  327 + organizations = profile.memberships.all.select do |organization|
  328 + profile.has_permission?(:perform_task, organization)
  329 + end
  330 + organization_ids = organizations.map{ |organization| "target_id = #{organization.id}"}.join(' OR ')
  331 +
  332 + organization_conditions = organization_ids.blank? ? nil : "(target_type = 'Profile' AND (#{organization_ids}))"
325 333 end
326 334 profile_condition = "(target_type = 'Profile' AND target_id = #{profile.id})"
327 335  
328   - where [environment_condition, profile_condition].compact.join(' OR ')
  336 + where [environment_condition, organization_conditions, profile_condition].compact.join(' OR ')
329 337 }
330 338  
331 339 scope :from_closed_date, -> closed_from {
... ...
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/box_organizer/show_block_type_info.html.erb
... ... @@ -15,7 +15,7 @@
15 15 <div id="block-info-description">
16 16 <h2><%= _('Description') %></h2>
17 17 <p><%= @block.class.description %></p>
18   - <p><%= @block.help if @block.class.method_defined?(:help) %></p>
  18 + <p><%= @block.help.html_safe if @block.class.method_defined?(:help) %></p>
19 19 </div>
20 20  
21 21 </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>
... ...