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,6 +111,10 @@ module Api
111 hash[value.custom_field.name]=value.value 111 hash[value.custom_field.name]=value.value
112 end 112 end
113 113
  114 + profile.public_fields.each do |field|
  115 + hash[field] = profile.send(field.to_sym)
  116 + end
  117 +
114 private_values = profile.custom_field_values - profile.public_values 118 private_values = profile.custom_field_values - profile.public_values
115 private_values.each do |value| 119 private_values.each do |value|
116 if Entities.can_display_profile_field?(profile,options) 120 if Entities.can_display_profile_field?(profile,options)
@@ -124,6 +128,7 @@ module Api @@ -124,6 +128,7 @@ module Api
124 expose :type 128 expose :type
125 expose :custom_header 129 expose :custom_header
126 expose :custom_footer 130 expose :custom_footer
  131 + expose :layout_template
127 expose :permissions do |profile, options| 132 expose :permissions do |profile, options|
128 Entities.permissions_for_entity(profile, options[:current_person], 133 Entities.permissions_for_entity(profile, options[:current_person],
129 :allow_post_content?, :allow_edit?, :allow_destroy?) 134 :allow_post_content?, :allow_edit?, :allow_destroy?)
@@ -258,12 +263,28 @@ module Api @@ -258,12 +263,28 @@ module Api
258 root 'tasks', 'task' 263 root 'tasks', 'task'
259 expose :id 264 expose :id
260 expose :type 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 end 278 end
262 279
263 class Environment < Entity 280 class Environment < Entity
264 expose :name 281 expose :name
265 expose :id 282 expose :id
266 expose :description 283 expose :description
  284 + expose :layout_template
  285 + expose :signup_intro
  286 + expose :terms_of_use
  287 + expose :top_url, as: :host
267 expose :settings, if: lambda { |instance, options| options[:is_admin] } 288 expose :settings, if: lambda { |instance, options| options[:is_admin] }
268 end 289 end
269 290
app/api/helpers.rb
@@ -4,7 +4,7 @@ require &#39;tempfile&#39; @@ -4,7 +4,7 @@ require &#39;tempfile&#39;
4 module Api 4 module Api
5 module Helpers 5 module Helpers
6 PRIVATE_TOKEN_PARAM = :private_token 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 include SanitizeParams 9 include SanitizeParams
10 include Noosfero::Plugin::HotSpot 10 include Noosfero::Plugin::HotSpot
@@ -127,8 +127,8 @@ module Api @@ -127,8 +127,8 @@ module Api
127 present_partial article, with: Entities::Article, params: params, current_person: current_person 127 present_partial article, with: Entities::Article, params: params, current_person: current_person
128 end 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 present_articles(articles) 132 present_articles(articles)
133 end 133 end
134 134
@@ -136,8 +136,8 @@ module Api @@ -136,8 +136,8 @@ module Api
136 present_partial paginate(articles), :with => Entities::Article, :params => params, current_person: current_person 136 present_partial paginate(articles), :with => Entities::Article, :params => params, current_person: current_person
137 end 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 if current_person.present? 141 if current_person.present?
142 articles = articles.display_filter(current_person, nil) 142 articles = articles.display_filter(current_person, nil)
143 else 143 else
@@ -146,14 +146,17 @@ module Api @@ -146,14 +146,17 @@ module Api
146 articles 146 articles
147 end 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 current_person.has_permission?(task.permission, asset) ? task : forbidden! 153 current_person.has_permission?(task.permission, asset) ? task : forbidden!
152 end 154 end
153 155
154 def post_task(asset, params) 156 def post_task(asset, params)
155 klass_type= params[:content_type].nil? ? 'Task' : params[:content_type] 157 klass_type= params[:content_type].nil? ? 'Task' : params[:content_type]
156 return forbidden! unless klass_type.constantize <= Task 158 return forbidden! unless klass_type.constantize <= Task
  159 + return forbidden! if !current_person.has_permission?(:perform_task, asset)
157 160
158 task = klass_type.constantize.new(params[:task]) 161 task = klass_type.constantize.new(params[:task])
159 task.requestor_id = current_person.id 162 task.requestor_id = current_person.id
@@ -166,15 +169,24 @@ module Api @@ -166,15 +169,24 @@ module Api
166 present_partial task, :with => Entities::Task 169 present_partial task, :with => Entities::Task
167 end 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 present_partial task, :with => Entities::Task 181 present_partial task, :with => Entities::Task
172 end 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 present_partial tasks, :with => Entities::Task 190 present_partial tasks, :with => Entities::Task
179 end 191 end
180 192
@@ -183,7 +195,6 @@ module Api @@ -183,7 +195,6 @@ module Api
183 conditions = {} 195 conditions = {}
184 from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from] 196 from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from]
185 until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until] 197 until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until]
186 -  
187 conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil? 198 conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil?
188 199
189 conditions[:created_at] = period(from_date, until_date) if from_date || until_date 200 conditions[:created_at] = period(from_date, until_date) if from_date || until_date
@@ -193,7 +204,7 @@ module Api @@ -193,7 +204,7 @@ module Api
193 end 204 end
194 205
195 # changing make_order_with_parameters to avoid sql injection 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 order = "created_at DESC" 208 order = "created_at DESC"
198 unless params[:order].blank? 209 unless params[:order].blank?
199 if params[:order].include? '\'' or params[:order].include? '"' 210 if params[:order].include? '\'' or params[:order].include? '"'
@@ -202,9 +213,9 @@ module Api @@ -202,9 +213,9 @@ module Api
202 order = 'RANDOM()' 213 order = 'RANDOM()'
203 else 214 else
204 field_name, direction = params[:order].split(' ') 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 if direction.present? and ['ASC','DESC'].include? direction.upcase 219 if direction.present? and ['ASC','DESC'].include? direction.upcase
209 order = "#{field_name} #{direction.upcase}" 220 order = "#{field_name} #{direction.upcase}"
210 end 221 end
@@ -215,12 +226,14 @@ module Api @@ -215,12 +226,14 @@ module Api
215 return order 226 return order
216 end 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 timestamp = nil 230 timestamp = nil
220 if params[:timestamp] 231 if params[:timestamp]
221 datetime = DateTime.parse(params[:timestamp]) 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 end 237 end
225 238
226 timestamp 239 timestamp
@@ -245,12 +258,12 @@ module Api @@ -245,12 +258,12 @@ module Api
245 end 258 end
246 end 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 conditions = make_conditions_with_parameter(params) 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 objects = by_reference(objects, params) 267 objects = by_reference(objects, params)
255 objects = by_categories(objects, params) 268 objects = by_categories(objects, params)
256 269
@@ -305,12 +318,12 @@ module Api @@ -305,12 +318,12 @@ module Api
305 end 318 end
306 319
307 def cant_be_saved_request!(attribute) 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 render_api_error!(message, 400) 322 render_api_error!(message, 400)
310 end 323 end
311 324
312 def bad_request!(attribute) 325 def bad_request!(attribute)
313 - message = _("(Invalid request) %s not given") % attribute 326 + message = _("(Invalid request) %s not given").html_safe % attribute
314 render_api_error!(message, 400) 327 render_api_error!(message, 400)
315 end 328 end
316 329
@@ -396,10 +409,27 @@ module Api @@ -396,10 +409,27 @@ module Api
396 end 409 end
397 private 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 def parser_params(params) 429 def parser_params(params)
400 parsed_params = {} 430 parsed_params = {}
401 params.map do |k,v| 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 end 433 end
404 parsed_params 434 parsed_params
405 end 435 end
app/api/v1/people.rb
@@ -136,6 +136,20 @@ module Api @@ -136,6 +136,20 @@ module Api
136 members = select_filtered_collection_of(profile, 'members', params) 136 members = select_filtered_collection_of(profile, 'members', params)
137 present members, :with => Entities::Person, :current_person => current_person 137 present members, :with => Entities::Person, :current_person => current_person
138 end 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 end 153 end
140 end 154 end
141 end 155 end
app/api/v1/tasks.rb
1 module Api 1 module Api
2 module V1 2 module V1
3 class Tasks < Grape::API 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 resource :tasks do 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 # Parameters: 13 # Parameters:
13 # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created 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,15 +18,22 @@ module Api
17 # Example Request: 18 # Example Request:
18 # 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 # 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 get do 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 end 23 end
24 24
25 desc "Return the task id" 25 desc "Return the task id"
26 get ':id' do 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 end 37 end
30 end 38 end
31 39
@@ -36,7 +44,8 @@ module Api @@ -36,7 +44,8 @@ module Api
36 resource :tasks do 44 resource :tasks do
37 get do 45 get do
38 profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) 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 end 49 end
41 50
42 get ':id' do 51 get ':id' do
app/concerns/authenticated_system.rb
@@ -1,185 +0,0 @@ @@ -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,7 +44,7 @@ class CategoriesController &lt; AdminController
44 if request.post? 44 if request.post?
45 @category.update!(params[:category]) 45 @category.update!(params[:category])
46 @saved = true 46 @saved = true
47 - session[:notice] = _("Category %s saved." % @category.name) 47 + session[:notice] = _("Category %s saved." % @category.name).html_safe
48 redirect_to :action => 'index' 48 redirect_to :action => 'index'
49 end 49 end
50 rescue Exception => e 50 rescue Exception => e
app/controllers/application_controller.rb
@@ -15,6 +15,20 @@ class ApplicationController &lt; ActionController::Base @@ -15,6 +15,20 @@ class ApplicationController &lt; ActionController::Base
15 before_filter :redirect_to_current_user 15 before_filter :redirect_to_current_user
16 16
17 before_filter :set_session_theme 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 def set_session_theme 32 def set_session_theme
19 if params[:theme] 33 if params[:theme]
20 session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil 34 session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil
@@ -49,7 +63,6 @@ class ApplicationController &lt; ActionController::Base @@ -49,7 +63,6 @@ class ApplicationController &lt; ActionController::Base
49 end 63 end
50 end 64 end
51 65
52 - include ApplicationHelper  
53 layout :get_layout 66 layout :get_layout
54 def get_layout 67 def get_layout
55 return false if request.format == :js or request.xhr? 68 return false if request.format == :js or request.xhr?
@@ -75,9 +88,6 @@ class ApplicationController &lt; ActionController::Base @@ -75,9 +88,6 @@ class ApplicationController &lt; ActionController::Base
75 helper :document 88 helper :document
76 helper :language 89 helper :language
77 90
78 - include DesignHelper  
79 - include PermissionCheck  
80 -  
81 before_filter :set_locale 91 before_filter :set_locale
82 def set_locale 92 def set_locale
83 FastGettext.available_locales = environment.available_locales 93 FastGettext.available_locales = environment.available_locales
@@ -90,8 +100,6 @@ class ApplicationController &lt; ActionController::Base @@ -90,8 +100,6 @@ class ApplicationController &lt; ActionController::Base
90 end 100 end
91 end 101 end
92 102
93 - include NeedsProfile  
94 -  
95 attr_reader :environment 103 attr_reader :environment
96 104
97 # declares that the given <tt>actions</tt> cannot be accessed by other HTTP 105 # declares that the given <tt>actions</tt> cannot be accessed by other HTTP
@@ -108,6 +116,10 @@ class ApplicationController &lt; ActionController::Base @@ -108,6 +116,10 @@ class ApplicationController &lt; ActionController::Base
108 116
109 protected 117 protected
110 118
  119 + def accept_only_post
  120 + return render_not_found if !request.post?
  121 + end
  122 +
111 def verified_request? 123 def verified_request?
112 super || form_authenticity_token == request.headers['X-XSRF-TOKEN'] 124 super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
113 end 125 end
@@ -154,8 +166,6 @@ class ApplicationController &lt; ActionController::Base @@ -154,8 +166,6 @@ class ApplicationController &lt; ActionController::Base
154 end 166 end
155 end 167 end
156 168
157 - include Noosfero::Plugin::HotSpot  
158 -  
159 # FIXME this filter just loads @plugins to children controllers and helpers 169 # FIXME this filter just loads @plugins to children controllers and helpers
160 def init_noosfero_plugins 170 def init_noosfero_plugins
161 plugins 171 plugins
@@ -187,9 +197,6 @@ class ApplicationController &lt; ActionController::Base @@ -187,9 +197,6 @@ class ApplicationController &lt; ActionController::Base
187 end 197 end
188 end 198 end
189 199
190 - include SearchTermHelper  
191 - include FindByContents  
192 -  
193 def find_suggestions(query, context, asset, options={}) 200 def find_suggestions(query, context, asset, options={})
194 plugins.dispatch_first(:find_suggestions, query, context, asset, options) 201 plugins.dispatch_first(:find_suggestions, query, context, asset, options)
195 end 202 end
app/controllers/box_organizer_controller.rb
@@ -109,7 +109,7 @@ class BoxOrganizerController &lt; ApplicationController @@ -109,7 +109,7 @@ class BoxOrganizerController &lt; ApplicationController
109 def show_block_type_info 109 def show_block_type_info
110 type = params[:type] 110 type = params[:type]
111 if type.blank? || !available_blocks.map(&:name).include?(type) 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 end 113 end
114 @block = type.constantize.new 114 @block = type.constantize.new
115 @block.box = Box.new(:owner => boxes_holder) 115 @block.box = Box.new(:owner => boxes_holder)
@@ -122,7 +122,7 @@ class BoxOrganizerController &lt; ApplicationController @@ -122,7 +122,7 @@ class BoxOrganizerController &lt; ApplicationController
122 122
123 def new_block(type, box) 123 def new_block(type, box)
124 if !available_blocks.map(&:name).include?(type) 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 end 126 end
127 block = type.constantize.new 127 block = type.constantize.new
128 box.blocks << block 128 box.blocks << block
app/controllers/concerns/authenticated_system.rb 0 → 100644
@@ -0,0 +1,185 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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,7 +14,7 @@ class TasksController &lt; MyProfileController
14 @filter_text = params[:filter_text].presence 14 @filter_text = params[:filter_text].presence
15 @filter_responsible = params[:filter_responsible] 15 @filter_responsible = params[:filter_responsible]
16 @task_types = Task.pending_types_for(profile) 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 @tasks = @tasks.where(:responsible_id => @filter_responsible.to_i != -1 ? @filter_responsible : nil) if @filter_responsible.present? 18 @tasks = @tasks.where(:responsible_id => @filter_responsible.to_i != -1 ? @filter_responsible : nil) if @filter_responsible.present?
19 @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page]) 19 @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page])
20 @failed = params ? params[:failed] : {} 20 @failed = params ? params[:failed] : {}
@@ -101,7 +101,7 @@ class TasksController &lt; MyProfileController @@ -101,7 +101,7 @@ class TasksController &lt; MyProfileController
101 def search_tasks 101 def search_tasks
102 filter_type = params[:filter_type].presence 102 filter_type = params[:filter_type].presence
103 filter_text = params[:filter_text].presence 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 render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} } 106 render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} }
107 end 107 end
app/controllers/public/account_controller.rb
@@ -205,7 +205,7 @@ class AccountController &lt; ApplicationController @@ -205,7 +205,7 @@ class AccountController &lt; ApplicationController
205 if params[:value].blank? 205 if params[:value].blank?
206 @change_password.errors[:base] << _('Can not recover user password with blank value.') 206 @change_password.errors[:base] << _('Can not recover user password with blank value.')
207 else 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 end 209 end
210 rescue ActiveRecord::RecordInvalid 210 rescue ActiveRecord::RecordInvalid
211 @change_password.errors[:base] << _('Could not perform password recovery for the user.') 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,9 +128,9 @@ class ContentViewerController &lt; ApplicationController
128 end 128 end
129 129
130 unless @page.display_to?(user) 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 render_access_denied 132 render_access_denied
133 - else #!profile.public? 133 + else
134 private_profile_partial_parameters 134 private_profile_partial_parameters
135 render :template => 'profile/_private_profile', :status => 403, :formats => [:html] 135 render :template => 'profile/_private_profile', :status => 403, :formats => [:html]
136 end 136 end
app/controllers/public/profile_controller.rb
@@ -3,7 +3,9 @@ class ProfileController &lt; PublicController @@ -3,7 +3,9 @@ class ProfileController &lt; PublicController
3 needs_profile 3 needs_profile
4 before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add] 4 before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add]
5 before_filter :store_location, :only => [:join, :join_not_logged, :report_abuse, :send_mail] 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 helper TagsHelper 10 helper TagsHelper
9 helper ActionTrackerHelper 11 helper ActionTrackerHelper
@@ -42,8 +44,8 @@ class ProfileController &lt; PublicController @@ -42,8 +44,8 @@ class ProfileController &lt; PublicController
42 feed_writer = FeedWriter.new 44 feed_writer = FeedWriter.new
43 data = feed_writer.write( 45 data = feed_writer.write(
44 tagged, 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 :link => url_for(profile.url) 49 :link => url_for(profile.url)
48 ) 50 )
49 render :text => data, :content_type => "text/xml" 51 render :text => data, :content_type => "text/xml"
@@ -65,6 +67,14 @@ class ProfileController &lt; PublicController @@ -65,6 +67,14 @@ class ProfileController &lt; PublicController
65 end 67 end
66 end 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 def members 78 def members
69 if is_cache_expired?(profile.members_cache_key(params)) 79 if is_cache_expired?(profile.members_cache_key(params))
70 sort = (params[:sort] == 'desc') ? params[:sort] : 'asc' 80 sort = (params[:sort] == 'desc') ? params[:sort] : 'asc'
@@ -88,7 +98,7 @@ class ProfileController &lt; PublicController @@ -88,7 +98,7 @@ class ProfileController &lt; PublicController
88 98
89 def join_modal 99 def join_modal
90 profile.add_member(user) 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 redirect_to :action => :index 102 redirect_to :action => :index
93 end 103 end
94 104
@@ -98,12 +108,12 @@ class ProfileController &lt; PublicController @@ -98,12 +108,12 @@ class ProfileController &lt; PublicController
98 108
99 profile.add_member(user) 109 profile.add_member(user)
100 if !profile.members.include?(user) 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 else 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 end 114 end
105 else 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 end 117 end
108 end 118 end
109 119
@@ -125,7 +135,7 @@ class ProfileController &lt; PublicController @@ -125,7 +135,7 @@ class ProfileController &lt; PublicController
125 render :text => current_person.leave(profile, params[:reload]) 135 render :text => current_person.leave(profile, params[:reload])
126 end 136 end
127 else 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 end 139 end
130 end 140 end
131 141
@@ -145,12 +155,41 @@ class ProfileController &lt; PublicController @@ -145,12 +155,41 @@ class ProfileController &lt; PublicController
145 # FIXME this shouldn't be in Person model? 155 # FIXME this shouldn't be in Person model?
146 if !user.memberships.include?(profile) 156 if !user.memberships.include?(profile)
147 AddFriend.create!(:person => user, :friend => profile) 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 else 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 end 175 end
152 end 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 def check_friendship 193 def check_friendship
155 unless logged_in? 194 unless logged_in?
156 render :text => '' 195 render :text => ''
@@ -178,7 +217,7 @@ class ProfileController &lt; PublicController @@ -178,7 +217,7 @@ class ProfileController &lt; PublicController
178 def unblock 217 def unblock
179 if current_person.is_admin?(profile.environment) 218 if current_person.is_admin?(profile.environment)
180 profile.unblock 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 redirect_to :controller => 'profile', :action => 'index' 221 redirect_to :controller => 'profile', :action => 'index'
183 else 222 else
184 message = _('You are not allowed to unblock enterprises in this environment.') 223 message = _('You are not allowed to unblock enterprises in this environment.')
@@ -448,4 +487,8 @@ class ProfileController &lt; PublicController @@ -448,4 +487,8 @@ class ProfileController &lt; PublicController
448 [:image, :domains, :preferred_domain, :environment] 487 [:image, :domains, :preferred_domain, :environment]
449 end 488 end
450 489
  490 + def allow_followers?
  491 + render_not_found unless profile.allow_followers?
  492 + end
  493 +
451 end 494 end
app/helpers/action_tracker_helper.rb
@@ -14,13 +14,23 @@ module ActionTrackerHelper @@ -14,13 +14,23 @@ module ActionTrackerHelper
14 } 14 }
15 end 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 def join_community_description ta 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 num: ta.get_resource_name.size, 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 link = link_to image_tag(ta.get_resource_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/community-icon.png")), 31 link = link_to image_tag(ta.get_resource_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/community-icon.png")),
22 ta.get_resource_url[i], title: n 32 ta.get_resource_url[i], title: n
23 - end.join.html_safe 33 + end)
24 } 34 }
25 end 35 end
26 36
@@ -68,9 +78,9 @@ module ActionTrackerHelper @@ -68,9 +78,9 @@ module ActionTrackerHelper
68 end 78 end
69 79
70 def favorite_enterprise_description ta 80 def favorite_enterprise_description ta
71 - _('favorited enterprise %{title}') % { 81 + (_('favorited enterprise %{title}') % {
72 title: link_to(truncate(ta.get_enterprise_name), ta.get_enterprise_url), 82 title: link_to(truncate(ta.get_enterprise_name), ta.get_enterprise_url),
73 - } 83 + }).html_safe
74 end 84 end
75 85
76 end 86 end
app/helpers/application_helper.rb
@@ -880,7 +880,7 @@ module ApplicationHelper @@ -880,7 +880,7 @@ module ApplicationHelper
880 link_to_all = link_to(content_tag('strong', _('See all')), :controller => 'memberships', :profile => user.identifier) 880 link_to_all = link_to(content_tag('strong', _('See all')), :controller => 'memberships', :profile => user.identifier)
881 end 881 end
882 link = list.map do |element| 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 end 884 end
885 if link_to_all 885 if link_to_all
886 link << link_to_all 886 link << link_to_all
@@ -921,7 +921,7 @@ module ApplicationHelper @@ -921,7 +921,7 @@ module ApplicationHelper
921 logout_link = link_to(logout_icon.html_safe, { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) 921 logout_link = link_to(logout_icon.html_safe, { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
922 join_result = safe_join( 922 join_result = safe_join(
923 [welcome_span.html_safe, render_environment_features(:usermenu).html_safe, admin_link.html_safe, 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 pending_tasks_count.html_safe, logout_link.html_safe], "") 925 pending_tasks_count.html_safe, logout_link.html_safe], "")
926 join_result 926 join_result
927 end 927 end
app/helpers/boxes_helper.rb
@@ -285,7 +285,7 @@ module BoxesHelper @@ -285,7 +285,7 @@ module BoxesHelper
285 end 285 end
286 286
287 if block.respond_to?(:help) 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 end 289 end
290 290
291 if block.embedable? 291 if block.embedable?
app/helpers/design_helper.rb
@@ -1,50 +0,0 @@ @@ -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,14 +128,14 @@ module FormsHelper
128 counter += 1 128 counter += 1
129 row << item 129 row << item
130 if counter % per_row == 0 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 counter = 0 132 counter = 0
133 row = [] 133 row = []
134 end 134 end
135 end 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 end 139 end
140 140
141 def date_field(name, value, datepicker_options = {}, html_options = {}) 141 def date_field(name, value, datepicker_options = {}, html_options = {})
app/helpers/profile_helper.rb
@@ -11,7 +11,7 @@ module ProfileHelper @@ -11,7 +11,7 @@ module ProfileHelper
11 PERSON_CATEGORIES[:location] = [:address, :address_reference, :zip_code, :city, :state, :district, :country, :nationality] 11 PERSON_CATEGORIES[:location] = [:address, :address_reference, :zip_code, :city, :state, :district, :country, :nationality]
12 PERSON_CATEGORIES[:work] = [:organization, :organization_website, :professional_activity] 12 PERSON_CATEGORIES[:work] = [:organization, :organization_website, :professional_activity]
13 PERSON_CATEGORIES[:study] = [:schooling, :formation, :area_of_study] 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 PERSON_CATEGORIES.merge!(COMMON_CATEGORIES) 15 PERSON_CATEGORIES.merge!(COMMON_CATEGORIES)
16 16
17 ORGANIZATION_CATEGORIES = {} 17 ORGANIZATION_CATEGORIES = {}
@@ -42,7 +42,8 @@ module ProfileHelper @@ -42,7 +42,8 @@ module ProfileHelper
42 :created_at => _('Profile created at'), 42 :created_at => _('Profile created at'),
43 :members_count => _('Members'), 43 :members_count => _('Members'),
44 :privacy_setting => _('Privacy setting'), 44 :privacy_setting => _('Privacy setting'),
45 - :article_tags => _('Tags') 45 + :article_tags => _('Tags'),
  46 + :followed_profiles => _('Following')
46 } 47 }
47 48
48 EXCEPTION = { 49 EXCEPTION = {
@@ -144,6 +145,14 @@ module ProfileHelper @@ -144,6 +145,14 @@ module ProfileHelper
144 link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url) 145 link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url)
145 end 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 def treat_events(events) 156 def treat_events(events)
148 link_to events.published.count, :controller => 'events', :action => 'events' 157 link_to events.published.count, :controller => 'events', :action => 'events'
149 end 158 end
app/jobs/notify_activity_to_profiles_job.rb
@@ -19,8 +19,8 @@ class NotifyActivityToProfilesJob &lt; Struct.new(:tracked_action_id) @@ -19,8 +19,8 @@ class NotifyActivityToProfilesJob &lt; Struct.new(:tracked_action_id)
19 # Notify the user 19 # Notify the user
20 ActionTrackerNotification.create(:profile_id => tracked_action.user.id, :action_tracker_id => tracked_action.id) 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 if tracked_action.user.is_a? Organization 25 if tracked_action.user.is_a? Organization
26 ActionTrackerNotification.connection.execute "insert into action_tracker_notifications(profile_id, action_tracker_id) " + 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,8 +47,8 @@ class Contact
47 content_type: 'text/html', 47 content_type: 'text/html',
48 to: contact.dest.notification_emails, 48 to: contact.dest.notification_emails,
49 reply_to: contact.email, 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 if contact.sender 54 if contact.sender
app/mailers/environment_mailing.rb
@@ -30,7 +30,7 @@ class EnvironmentMailing &lt; Mailing @@ -30,7 +30,7 @@ class EnvironmentMailing &lt; Mailing
30 end 30 end
31 31
32 def signature_message 32 def signature_message
33 - _('Sent by %s.') % source.name 33 + _('Sent by %s.').html_safe % source.name
34 end 34 end
35 35
36 def url 36 def url
app/mailers/mailing.rb
@@ -2,7 +2,8 @@ require_dependency &#39;mailing_job&#39; @@ -2,7 +2,8 @@ require_dependency &#39;mailing_job&#39;
2 2
3 class Mailing < ApplicationRecord 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 attr_accessible :subject, :body, :data 8 attr_accessible :subject, :body, :data
8 9
@@ -23,11 +24,11 @@ class Mailing &lt; ApplicationRecord @@ -23,11 +24,11 @@ class Mailing &lt; ApplicationRecord
23 end 24 end
24 25
25 def generate_from 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 end 28 end
28 29
29 def generate_subject 30 def generate_subject
30 - '[%s] %s' % [source.name, subject] 31 + '[%s] %s'.html_safe % [source.name, subject]
31 end 32 end
32 33
33 def signature_message 34 def signature_message
app/mailers/organization_mailing.rb
@@ -30,7 +30,7 @@ class OrganizationMailing &lt; Mailing @@ -30,7 +30,7 @@ class OrganizationMailing &lt; Mailing
30 end 30 end
31 31
32 def signature_message 32 def signature_message
33 - _('Sent by community %s.') % source.name 33 + _('Sent by community %s.').html_safe % source.name
34 end 34 end
35 35
36 include Rails.application.routes.url_helpers 36 include Rails.application.routes.url_helpers
app/mailers/pending_task_notifier.rb
@@ -12,8 +12,8 @@ class PendingTaskNotifier &lt; ApplicationMailer @@ -12,8 +12,8 @@ class PendingTaskNotifier &lt; ApplicationMailer
12 12
13 mail( 13 mail(
14 to: person.email, 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 end 18 end
19 19
app/mailers/scrap_notifier.rb
@@ -14,8 +14,8 @@ class ScrapNotifier &lt; ApplicationMailer @@ -14,8 +14,8 @@ class ScrapNotifier &lt; ApplicationMailer
14 @url = sender.environment.top_url 14 @url = sender.environment.top_url
15 mail( 15 mail(
16 to: receiver.email, 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 end 20 end
21 end 21 end
app/mailers/task_mailer.rb
@@ -14,7 +14,7 @@ class TaskMailer &lt; ApplicationMailer @@ -14,7 +14,7 @@ class TaskMailer &lt; ApplicationMailer
14 mail( 14 mail(
15 to: task.target.notification_emails.compact, 15 to: task.target.notification_emails.compact,
16 from: self.class.generate_from(task), 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 end 19 end
20 20
@@ -27,7 +27,7 @@ class TaskMailer &lt; ApplicationMailer @@ -27,7 +27,7 @@ class TaskMailer &lt; ApplicationMailer
27 mail( 27 mail(
28 to: task.friend_email, 28 to: task.friend_email,
29 from: self.class.generate_from(task), 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 end 32 end
33 33
@@ -43,7 +43,7 @@ class TaskMailer &lt; ApplicationMailer @@ -43,7 +43,7 @@ class TaskMailer &lt; ApplicationMailer
43 mail_with_template( 43 mail_with_template(
44 to: task.requestor.notification_emails, 44 to: task.requestor.notification_emails,
45 from: self.class.generate_from(task), 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 email_template: task.email_template, 47 email_template: task.email_template,
48 template_params: {:environment => task.requestor.environment, :task => task, :message => @message, :url => @url, :requestor => task.requestor} 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,8 +13,8 @@ class UserMailer &lt; ApplicationMailer
13 13
14 mail( 14 mail(
15 to: user_email, 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 end 19 end
20 20
@@ -30,7 +30,7 @@ class UserMailer &lt; ApplicationMailer @@ -30,7 +30,7 @@ class UserMailer &lt; ApplicationMailer
30 mail_with_template( 30 mail_with_template(
31 from: "#{user.environment.name} <#{user.environment.contact_email}>", 31 from: "#{user.environment.name} <#{user.environment.contact_email}>",
32 to: user.email, 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 template_params: {:environment => user.environment, :activation_code => @activation_code, :redirection => @redirection, :join => @join, :person => user.person, :url => @url}, 34 template_params: {:environment => user.environment, :activation_code => @activation_code, :redirection => @redirection, :join => @join, :person => user.person, :url => @url},
35 email_template: user.environment.email_templates.find_by_template_type(:user_activation), 35 email_template: user.environment.email_templates.find_by_template_type(:user_activation),
36 ) 36 )
@@ -44,8 +44,8 @@ class UserMailer &lt; ApplicationMailer @@ -44,8 +44,8 @@ class UserMailer &lt; ApplicationMailer
44 mail( 44 mail(
45 content_type: 'text/html', 45 content_type: 'text/html',
46 to: user.email, 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 body: @body 49 body: @body
50 ) 50 )
51 end 51 end
@@ -63,8 +63,8 @@ class UserMailer &lt; ApplicationMailer @@ -63,8 +63,8 @@ class UserMailer &lt; ApplicationMailer
63 mail( 63 mail(
64 content_type: 'text/html', 64 content_type: 'text/html',
65 to: user.email, 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 end 69 end
70 70
app/models/abuse_complaint.rb
@@ -25,7 +25,7 @@ class AbuseComplaint &lt; Task @@ -25,7 +25,7 @@ class AbuseComplaint &lt; Task
25 end 25 end
26 26
27 def title 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 end 29 end
30 30
31 def linked_subject 31 def linked_subject
@@ -57,15 +57,15 @@ class AbuseComplaint &lt; Task @@ -57,15 +57,15 @@ class AbuseComplaint &lt; Task
57 end 57 end
58 58
59 def task_activated_message 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 end 61 end
62 62
63 def task_finished_message 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 end 65 end
66 66
67 def target_notification_description 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 end 69 end
70 70
71 def target_notification_message 71 def target_notification_message
app/models/add_member.rb
@@ -22,6 +22,7 @@ class AddMember &lt; Task @@ -22,6 +22,7 @@ class AddMember &lt; Task
22 self.roles = [Profile::Roles.member(organization.environment.id).id] 22 self.roles = [Profile::Roles.member(organization.environment.id).id]
23 end 23 end
24 target.affiliate(requestor, self.roles.select{|r| !r.to_i.zero? }.map{|i| Role.find(i)}) 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 end 26 end
26 27
27 def title 28 def title
@@ -56,7 +57,7 @@ class AddMember &lt; Task @@ -56,7 +57,7 @@ class AddMember &lt; Task
56 def target_notification_description 57 def target_notification_description
57 requestor_email = " (#{requestor.email})" if requestor.may_display_field_to?("email") 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 end 61 end
61 62
62 def target_notification_message 63 def target_notification_message
app/models/article.rb
@@ -13,7 +13,9 @@ class Article &lt; ApplicationRecord @@ -13,7 +13,9 @@ class Article &lt; ApplicationRecord
13 :image_builder, :show_to_followers, :archived, 13 :image_builder, :show_to_followers, :archived,
14 :author, :display_preview, :published_at, :person_followers 14 :author, :display_preview, :published_at, :person_followers
15 15
  16 + extend ActsAsHavingImage::ClassMethods
16 acts_as_having_image 17 acts_as_having_image
  18 +
17 include Noosfero::Plugin::HotSpot 19 include Noosfero::Plugin::HotSpot
18 20
19 SEARCHABLE_FIELDS = { 21 SEARCHABLE_FIELDS = {
@@ -91,7 +93,8 @@ class Article &lt; ApplicationRecord @@ -91,7 +93,8 @@ class Article &lt; ApplicationRecord
91 has_many :article_categorizations_including_virtual, :class_name => 'ArticleCategorization' 93 has_many :article_categorizations_including_virtual, :class_name => 'ArticleCategorization'
92 has_many :categories_including_virtual, :through => :article_categorizations_including_virtual, :source => :category 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 settings_items :display_hits, :type => :boolean, :default => true 99 settings_items :display_hits, :type => :boolean, :default => true
97 settings_items :author_name, :type => :string, :default => "" 100 settings_items :author_name, :type => :string, :default => ""
@@ -242,6 +245,7 @@ class Article &lt; ApplicationRecord @@ -242,6 +245,7 @@ class Article &lt; ApplicationRecord
242 acts_as_taggable 245 acts_as_taggable
243 N_('Tag list') 246 N_('Tag list')
244 247
  248 + extend ActsAsFilesystem::ActsMethods
245 acts_as_filesystem 249 acts_as_filesystem
246 250
247 acts_as_versioned 251 acts_as_versioned
@@ -534,13 +538,13 @@ class Article &lt; ApplicationRecord @@ -534,13 +538,13 @@ class Article &lt; ApplicationRecord
534 538
535 scope :display_filter, lambda {|user, profile| 539 scope :display_filter, lambda {|user, profile|
536 return published if (user.nil? && profile && profile.public?) 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 where( 542 where(
539 [ 543 [
540 "published = ? OR last_changed_by_id = ? OR profile_id = ? OR ? 544 "published = ? OR last_changed_by_id = ? OR profile_id = ? OR ?
541 OR (show_to_followers = ? AND ? AND profile_id IN (?))", true, user.id, user.id, 545 OR (show_to_followers = ? AND ? AND profile_id IN (?))", true, user.id, user.id,
542 profile.nil? ? false : user.has_permission?(:view_private_content, profile), 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,6 +17,7 @@ class Block &lt; ApplicationRecord
17 belongs_to :mirror_block, :class_name => "Block" 17 belongs_to :mirror_block, :class_name => "Block"
18 has_many :observers, :class_name => "Block", :foreign_key => "mirror_block_id" 18 has_many :observers, :class_name => "Block", :foreign_key => "mirror_block_id"
19 19
  20 + extend ActsAsHavingSettings::ClassMethods
20 acts_as_having_settings 21 acts_as_having_settings
21 22
22 scope :enabled, -> { where :enabled => true } 23 scope :enabled, -> { where :enabled => true }
@@ -88,7 +89,7 @@ class Block &lt; ApplicationRecord @@ -88,7 +89,7 @@ class Block &lt; ApplicationRecord
88 end 89 end
89 90
90 def display_to_user?(user) 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 end 93 end
93 94
94 def display_always(context) 95 def display_always(context)
app/models/blog.rb
@@ -2,7 +2,9 @@ class Blog &lt; Folder @@ -2,7 +2,9 @@ class Blog &lt; Folder
2 2
3 attr_accessible :visualization_format 3 attr_accessible :visualization_format
4 4
  5 + extend ActsAsHavingPosts::ClassMethods
5 acts_as_having_posts 6 acts_as_having_posts
  7 +
6 include PostsLimit 8 include PostsLimit
7 9
8 #FIXME This should be used until there is a migration to fix all blogs that 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,6 +21,7 @@ class Category &lt; ApplicationRecord
21 21
22 scope :on_level, -> parent { where :parent_id => parent } 22 scope :on_level, -> parent { where :parent_id => parent }
23 23
  24 + extend ActsAsFilesystem::ActsMethods
24 acts_as_filesystem 25 acts_as_filesystem
25 26
26 has_many :article_categorizations 27 has_many :article_categorizations
@@ -35,6 +36,7 @@ class Category &lt; ApplicationRecord @@ -35,6 +36,7 @@ class Category &lt; ApplicationRecord
35 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person' 36 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person'
36 has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community' 37 has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community'
37 38
  39 + extend ActsAsHavingImage::ClassMethods
38 acts_as_having_image 40 acts_as_having_image
39 41
40 before_save :normalize_display_color 42 before_save :normalize_display_color
app/models/circle.rb 0 → 100644
@@ -0,0 +1,37 @@ @@ -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,6 +38,7 @@ class Comment &lt; ApplicationRecord
38 38
39 validate :article_archived? 39 validate :article_archived?
40 40
  41 + extend ActsAsHavingSettings::ClassMethods
41 acts_as_having_settings 42 acts_as_having_settings
42 43
43 xss_terminate :only => [ :body, :title, :name ], :on => 'validation' 44 xss_terminate :only => [ :body, :title, :name ], :on => 'validation'
app/models/community.rb
@@ -2,7 +2,7 @@ class Community &lt; Organization @@ -2,7 +2,7 @@ class Community &lt; Organization
2 2
3 attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, :resource_type 3 attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, :resource_type
4 attr_accessible :address_reference, :district, :tag_list, :language, :description 4 attr_accessible :address_reference, :district, :tag_list, :language, :description
5 - attr_accessible :requires_email 5 +
6 after_destroy :check_invite_member_for_destroy 6 after_destroy :check_invite_member_for_destroy
7 7
8 def self.type_name 8 def self.type_name
@@ -13,9 +13,6 @@ class Community &lt; Organization @@ -13,9 +13,6 @@ class Community &lt; Organization
13 N_('Language') 13 N_('Language')
14 14
15 settings_items :language 15 settings_items :language
16 - settings_items :requires_email, :type => :boolean  
17 -  
18 - alias_method :requires_email?, :requires_email  
19 16
20 extend SetProfileRegionFromCityState::ClassMethods 17 extend SetProfileRegionFromCityState::ClassMethods
21 set_profile_region_from_city_state 18 set_profile_region_from_city_state
app/models/concerns/acts_as_filesystem.rb 0 → 100644
@@ -0,0 +1,265 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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,6 +12,7 @@ class CreateCommunity &lt; Task
12 attr_accessible :environment, :requestor, :target 12 attr_accessible :environment, :requestor, :target
13 attr_accessible :reject_explanation, :template_id 13 attr_accessible :reject_explanation, :template_id
14 14
  15 + extend ActsAsHavingImage::ClassMethods
15 acts_as_having_image 16 acts_as_having_image
16 17
17 DATA_FIELDS = Community.fields + ['name', 'closed', 'description'] 18 DATA_FIELDS = Community.fields + ['name', 'closed', 'description']
app/models/enterprise.rb
@@ -174,8 +174,4 @@ class Enterprise &lt; Organization @@ -174,8 +174,4 @@ class Enterprise &lt; Organization
174 '' 174 ''
175 end 175 end
176 176
177 - def followed_by? person  
178 - super or self.fans.where(id: person.id).count > 0  
179 - end  
180 -  
181 end 177 end
app/models/environment.rb
@@ -203,6 +203,7 @@ class Environment &lt; ApplicationRecord @@ -203,6 +203,7 @@ class Environment &lt; ApplicationRecord
203 # Relationships and applied behaviour 203 # Relationships and applied behaviour
204 # ################################################# 204 # #################################################
205 205
  206 + extend ActsAsHavingBoxes::ClassMethods
206 acts_as_having_boxes 207 acts_as_having_boxes
207 208
208 after_create do |env| 209 after_create do |env|
@@ -254,7 +255,8 @@ class Environment &lt; ApplicationRecord @@ -254,7 +255,8 @@ class Environment &lt; ApplicationRecord
254 # ################################################# 255 # #################################################
255 256
256 # store the Environment settings as YAML-serialized Hash. 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 # introduce and explain to users something about the signup 261 # introduce and explain to users something about the signup
260 settings_items :signup_intro, :type => String 262 settings_items :signup_intro, :type => String
app/models/event.rb
1 -require 'noosfero/translatable_content'  
2 require 'builder' 1 require 'builder'
3 2
4 class Event < Article 3 class Event < Article
@@ -138,7 +137,7 @@ class Event &lt; Article @@ -138,7 +137,7 @@ class Event &lt; Article
138 false 137 false
139 end 138 end
140 139
141 - include Noosfero::TranslatableContent 140 + include TranslatableContent
142 include MaybeAddHttp 141 include MaybeAddHttp
143 142
144 end 143 end
app/models/favorite_enterprise_person.rb
@@ -7,6 +7,10 @@ class FavoriteEnterprisePerson &lt; ApplicationRecord @@ -7,6 +7,10 @@ class FavoriteEnterprisePerson &lt; ApplicationRecord
7 belongs_to :enterprise 7 belongs_to :enterprise
8 belongs_to :person 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 protected 14 protected
11 15
12 def is_trackable? 16 def is_trackable?
app/models/folder.rb
@@ -10,7 +10,8 @@ class Folder &lt; Article @@ -10,7 +10,8 @@ class Folder &lt; Article
10 errors.add(:parent, "A folder should not belong to a blog.") if parent && parent.blog? 10 errors.add(:parent, "A folder should not belong to a blog.") if parent && parent.blog?
11 end 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 xss_terminate :only => [ :name, :body ], :with => 'white_list', :on => 'validation' 16 xss_terminate :only => [ :name, :body ], :with => 'white_list', :on => 'validation'
16 17
app/models/forum.rb
1 class Forum < Folder 1 class Forum < Folder
2 2
  3 + extend ActsAsHavingPosts::ClassMethods
3 acts_as_having_posts -> { reorder 'updated_at DESC' } 4 acts_as_having_posts -> { reorder 'updated_at DESC' }
  5 +
4 include PostsLimit 6 include PostsLimit
5 7
6 attr_accessible :has_terms_of_use, :terms_of_use, :topic_creation 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,11 +9,22 @@ class Friendship &lt; ApplicationRecord
9 after_create do |friendship| 9 after_create do |friendship|
10 Friendship.update_cache_counter(:friends_count, friendship.person, 1) 10 Friendship.update_cache_counter(:friends_count, friendship.person, 1)
11 Friendship.update_cache_counter(:friends_count, friendship.friend, 1) 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 end 17 end
13 18
14 after_destroy do |friendship| 19 after_destroy do |friendship|
15 Friendship.update_cache_counter(:friends_count, friendship.person, -1) 20 Friendship.update_cache_counter(:friends_count, friendship.person, -1)
16 Friendship.update_cache_counter(:friends_count, friendship.friend, -1) 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 end 28 end
18 29
19 def self.remove_friendship(person1, person2) 30 def self.remove_friendship(person1, person2)
app/models/image.rb
@@ -23,6 +23,7 @@ class Image &lt; ApplicationRecord @@ -23,6 +23,7 @@ class Image &lt; ApplicationRecord
23 23
24 validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n 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 delay_attachment_fu_thumbnails 27 delay_attachment_fu_thumbnails
27 28
28 postgresql_attachment_fu 29 postgresql_attachment_fu
app/models/organization.rb
@@ -2,6 +2,10 @@ @@ -2,6 +2,10 @@
2 class Organization < Profile 2 class Organization < Profile
3 3
4 attr_accessible :moderated_articles, :foundation_year, :contact_person, :acronym, :legal_form, :economic_activity, :management_information, :cnpj, :display_name, :enable_contact_us 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 SEARCH_FILTERS = { 10 SEARCH_FILTERS = {
7 :order => %w[more_recent more_popular more_active], 11 :order => %w[more_recent more_popular more_active],
app/models/person.rb
@@ -10,7 +10,6 @@ class Person &lt; Profile @@ -10,7 +10,6 @@ class Person &lt; Profile
10 :display => %w[compact] 10 :display => %w[compact]
11 } 11 }
12 12
13 -  
14 def self.type_name 13 def self.type_name
15 _('Person') 14 _('Person')
16 end 15 end
@@ -94,6 +93,7 @@ class Person &lt; Profile @@ -94,6 +93,7 @@ class Person &lt; Profile
94 has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article 93 has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article
95 has_many :friendships, :dependent => :destroy 94 has_many :friendships, :dependent => :destroy
96 has_many :friends, :class_name => 'Person', :through => :friendships 95 has_many :friends, :class_name => 'Person', :through => :friendships
  96 + has_many :circles
97 97
98 scope :online, -> { 98 scope :online, -> {
99 joins(:user).where("users.chat_status != '' AND users.chat_status_at >= ?", DateTime.now - User.expires_chat_status_every.minutes) 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,6 +181,33 @@ class Person &lt; Profile
181 end 181 end
182 end 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 def already_request_friendship?(person) 211 def already_request_friendship?(person)
185 person.tasks.where(requestor_id: self.id, type: 'AddFriend', status: Task::Status::ACTIVE).first 212 person.tasks.where(requestor_id: self.id, type: 'AddFriend', status: Task::Status::ACTIVE).first
186 end 213 end
@@ -574,9 +601,12 @@ class Person &lt; Profile @@ -574,9 +601,12 @@ class Person &lt; Profile
574 } 601 }
575 end 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 end 610 end
  611 +
582 end 612 end
app/models/profile.rb
@@ -6,7 +6,7 @@ class Profile &lt; ApplicationRecord @@ -6,7 +6,7 @@ class Profile &lt; ApplicationRecord
6 include ProfileEntity 6 include ProfileEntity
7 7
8 attr_accessible :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time, 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 # use for internationalizable human type names in search facets 11 # use for internationalizable human type names in search facets
12 # reimplement on subclasses 12 # reimplement on subclasses
@@ -89,6 +89,8 @@ class Profile &lt; ApplicationRecord @@ -89,6 +89,8 @@ class Profile &lt; ApplicationRecord
89 } 89 }
90 90
91 acts_as_accessible 91 acts_as_accessible
  92 +
  93 + include Customizable
92 acts_as_customizable 94 acts_as_customizable
93 95
94 include Noosfero::Plugin::HotSpot 96 include Noosfero::Plugin::HotSpot
@@ -184,6 +186,21 @@ class Profile &lt; ApplicationRecord @@ -184,6 +186,21 @@ class Profile &lt; ApplicationRecord
184 Person.members_of(self).by_role(roles) 186 Person.members_of(self).by_role(roles)
185 end 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 acts_as_having_boxes 204 acts_as_having_boxes
188 205
189 acts_as_taggable 206 acts_as_taggable
@@ -202,6 +219,23 @@ class Profile &lt; ApplicationRecord @@ -202,6 +219,23 @@ class Profile &lt; ApplicationRecord
202 scope :more_active, -> { order 'activities_count DESC' } 219 scope :more_active, -> { order 'activities_count DESC' }
203 scope :more_recent, -> { order "created_at DESC" } 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 acts_as_trackable :dependent => :destroy 239 acts_as_trackable :dependent => :destroy
206 240
207 has_many :profile_activities 241 has_many :profile_activities
@@ -214,6 +248,9 @@ class Profile &lt; ApplicationRecord @@ -214,6 +248,9 @@ class Profile &lt; ApplicationRecord
214 248
215 has_many :email_templates, :foreign_key => :owner_id 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 # Although this should be a has_one relation, there are no non-silly names for 254 # Although this should be a has_one relation, there are no non-silly names for
218 # a foreign key on article to reference the template to which it is 255 # a foreign key on article to reference the template to which it is
219 # welcome_page... =P 256 # welcome_page... =P
@@ -228,19 +265,6 @@ class Profile &lt; ApplicationRecord @@ -228,19 +265,6 @@ class Profile &lt; ApplicationRecord
228 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap) 265 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap)
229 end 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 validates_length_of :description, :maximum => 550, :allow_nil => true 268 validates_length_of :description, :maximum => 550, :allow_nil => true
245 269
246 # Valid identifiers must match this format. 270 # Valid identifiers must match this format.
@@ -281,6 +305,7 @@ class Profile &lt; ApplicationRecord @@ -281,6 +305,7 @@ class Profile &lt; ApplicationRecord
281 305
282 has_many :files, :class_name => 'UploadedFile' 306 has_many :files, :class_name => 'UploadedFile'
283 307
  308 + extend ActsAsHavingImage::ClassMethods
284 acts_as_having_image 309 acts_as_having_image
285 310
286 has_many :tasks, :dependent => :destroy, :as => 'target' 311 has_many :tasks, :dependent => :destroy, :as => 'target'
@@ -695,12 +720,13 @@ private :generate_url, :url_options @@ -695,12 +720,13 @@ private :generate_url, :url_options
695 720
696 # Adds a person as member of this Profile. 721 # Adds a person as member of this Profile.
697 def add_member(person, attributes={}) 722 def add_member(person, attributes={})
698 - if self.has_members? 723 + if self.has_members? && !self.secret
699 if self.closed? && members.count > 0 724 if self.closed? && members.count > 0
700 AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person) 725 AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person)
701 else 726 else
702 self.affiliate(person, Profile::Roles.admin(environment.id), attributes) if members.count == 0 727 self.affiliate(person, Profile::Roles.admin(environment.id), attributes) if members.count == 0
703 self.affiliate(person, Profile::Roles.member(environment.id), attributes) 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 end 730 end
705 person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel } 731 person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel }
706 remove_from_suggestion_list person 732 remove_from_suggestion_list person
@@ -983,7 +1009,11 @@ private :generate_url, :url_options @@ -983,7 +1009,11 @@ private :generate_url, :url_options
983 end 1009 end
984 1010
985 def followed_by?(person) 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 end 1017 end
988 1018
989 def display_private_info_to?(user) 1019 def display_private_info_to?(user)
@@ -1009,4 +1039,23 @@ private :generate_url, :url_options @@ -1009,4 +1039,23 @@ private :generate_url, :url_options
1009 suggestion.disable if suggestion 1039 suggestion.disable if suggestion
1010 end 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 end 1061 end
app/models/profile_follower.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -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,7 +17,8 @@ class ProfileSuggestion &lt; ApplicationRecord
17 self.class.generate_profile_suggestions(profile_suggestion.person) 17 self.class.generate_profile_suggestions(profile_suggestion.person)
18 end 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 validate :must_be_a_valid_category, :on => :create 23 validate :must_be_a_valid_category, :on => :create
23 def must_be_a_valid_category 24 def must_be_a_valid_category
app/models/task.rb
@@ -11,7 +11,8 @@ @@ -11,7 +11,8 @@
11 # will need to declare <ttserialize</tt> itself). 11 # will need to declare <ttserialize</tt> itself).
12 class Task < ApplicationRecord 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 module Status 17 module Status
17 # the status of tasks just created 18 # the status of tasks just created
@@ -313,7 +314,7 @@ class Task &lt; ApplicationRecord @@ -313,7 +314,7 @@ class Task &lt; ApplicationRecord
313 where "LOWER(#{field}) LIKE ?", "%#{value.downcase}%" 314 where "LOWER(#{field}) LIKE ?", "%#{value.downcase}%"
314 end 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 self.to(profile).without_spam.pending.of(filter_type).like('data', filter_text) 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,10 +323,17 @@ class Task &lt; ApplicationRecord
322 if profile.person? 323 if profile.person?
323 envs_ids = Environment.all.select{ |env| profile.is_admin?(env) }.map{ |env| "target_id = #{env.id}"}.join(' OR ') 324 envs_ids = Environment.all.select{ |env| profile.is_admin?(env) }.map{ |env| "target_id = #{env.id}"}.join(' OR ')
324 environment_condition = envs_ids.blank? ? nil : "(target_type = 'Environment' AND (#{envs_ids}))" 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 end 333 end
326 profile_condition = "(target_type = 'Profile' AND target_id = #{profile.id})" 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 scope :from_closed_date, -> closed_from { 339 scope :from_closed_date, -> closed_from {
app/models/text_article.rb
1 -require 'noosfero/translatable_content'  
2 -  
3 # a base class for all text article types. 1 # a base class for all text article types.
4 class TextArticle < Article 2 class TextArticle < Article
5 3
@@ -9,7 +7,7 @@ class TextArticle &lt; Article @@ -9,7 +7,7 @@ class TextArticle &lt; Article
9 _('Article') 7 _('Article')
10 end 8 end
11 9
12 - include Noosfero::TranslatableContent 10 + include TranslatableContent
13 11
14 def self.icon_name(article = nil) 12 def self.icon_name(article = nil)
15 if article && !article.parent.nil? && article.parent.kind_of?(Blog) 13 if article && !article.parent.nil? && article.parent.kind_of?(Blog)
app/models/tiny_mce_article.rb
1 -require 'white_list_filter'  
2 -  
3 class TinyMceArticle < TextArticle 1 class TinyMceArticle < TextArticle
4 2
5 def self.short_description 3 def self.short_description
app/models/uploaded_file.rb
@@ -84,6 +84,7 @@ class UploadedFile &lt; Article @@ -84,6 +84,7 @@ class UploadedFile &lt; Article
84 84
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 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 delay_attachment_fu_thumbnails 88 delay_attachment_fu_thumbnails
88 89
89 postgresql_attachment_fu 90 postgresql_attachment_fu
app/views/account/activation_question.html.erb
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 var answer = parseInt(form.answer.value); 3 var answer = parseInt(form.answer.value);
4 var val = form.answer.value; 4 var val = form.answer.value;
5 if (!answer || (val.length != 4) || val > <%= Time.now.year %> || val < 1900) { 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 return false; 7 return false;
8 } else { 8 } else {
9 return true; 9 return true;
@@ -28,9 +28,9 @@ @@ -28,9 +28,9 @@
28 28
29 <p> <strong><%= _('Pay atention! You have only one chance!') %></strong> </p> 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 <%= hidden_field_tag :enterprise_code, params[:enterprise_code] %> 35 <%= hidden_field_tag :enterprise_code, params[:enterprise_code] %>
36 36
app/views/admin_panel/_signup_welcome_text.html.erb
1 <div class='description'> 1 <div class='description'>
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/> 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 </div> 4 </div>
5 5
6 <%= labelled_form_field(_('Subject'), text_field(:environment, :signup_welcome_text_subject, :style => 'width:100%')) %> 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 @@ @@ -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 <li><%= report_abuse(profile, :button) %></li> 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 <%= render_environment_features(:profile_actions) %> 11 <%= render_environment_features(:profile_actions) %>
app/views/blocks/profile_info_actions/_select_circles.html.erb 0 → 100644
@@ -0,0 +1,22 @@ @@ -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,7 +15,7 @@
15 <div id="block-info-description"> 15 <div id="block-info-description">
16 <h2><%= _('Description') %></h2> 16 <h2><%= _('Description') %></h2>
17 <p><%= @block.class.description %></p> 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 </div> 19 </div>
20 20
21 </div> 21 </div>
app/views/circles/_circle_checkbox.html.erb 0 → 100644
@@ -0,0 +1,3 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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,7 +58,7 @@
58 <div id="blog-image-builder"> 58 <div id="blog-image-builder">
59 <%= f.fields_for :image_builder, @article.image do |i| %> 59 <%= f.fields_for :image_builder, @article.image do |i| %>
60 <%= file_field_or_thumbnail(_('Cover image:'), @article.image, i)%> 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 <% end %> 62 <% end %>
63 </div> 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 <%= link_to _('Back'), :action => 'index' %> 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 <p> 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 </p> 5 </p>
6 6
7 <%= form_tag do %> 7 <%= form_tag do %>
8 <%= hidden_field_tag(:confirmation, 1) %> 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 <%= button(:cancel, _("No, I don't want"), :action => 'index') %> 11 <%= button(:cancel, _("No, I don't want"), :action => 'index') %>
12 <% end %> 12 <% end %>
app/views/file_presenter/_image.html.erb
@@ -4,15 +4,15 @@ @@ -4,15 +4,15 @@
4 current_index = images.index(image.encapsulated_file) 4 current_index = images.index(image.encapsulated_file)
5 total_of_images = images.count 5 total_of_images = images.count
6 link_to_previous = if current_index >= 1 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 else 8 else
9 - content_tag('span', _('&laquo; Previous'), :class => 'previous') 9 + content_tag('span', _('&laquo; Previous').html_safe, :class => 'previous')
10 end 10 end
11 11
12 link_to_next = if current_index < total_of_images - 1 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 else 14 else
15 - content_tag('span', _('Next &raquo;'), :class => 'next') 15 + content_tag('span', _('Next &raquo;').html_safe, :class => 'next')
16 end 16 end
17 %> 17 %>
18 18
app/views/followers/_edit_circles_modal.html.erb 0 → 100644
@@ -0,0 +1,14 @@ @@ -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 @@ @@ -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 @@ @@ -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 <div id="remove_friend"> 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 <%= profile_image @friend, :thumb, :class => 'friend_picture' %> 5 <%= profile_image @friend, :thumb, :class => 'friend_picture' %>
6 6
7 <p> 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 </p> 9 </p>
10 10
11 <p> 11 <p>
12 <em> 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 </em> 14 </em>
15 </p> 15 </p>
16 16
app/views/home/welcome.html.erb
1 <% default_message = defined?(default_message) ? default_message : false %> 1 <% default_message = defined?(default_message) ? default_message : false %>
2 2
3 <div id='thanks-for-signing'> 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 <% if environment.has_custom_welcome_screen? && !default_message %> 5 <% if environment.has_custom_welcome_screen? && !default_message %>
6 <%= environment.settings[:signup_welcome_screen_body].html_safe %> 6 <%= environment.settings[:signup_welcome_screen_body].html_safe %>
7 <% else %> 7 <% else %>
@@ -10,21 +10,21 @@ @@ -10,21 +10,21 @@
10 <p><%= _("Firstly, some tips for getting started:") %></p> 10 <p><%= _("Firstly, some tips for getting started:") %></p>
11 <h4><%= _("Confirm your account!") %></h4> 11 <h4><%= _("Confirm your account!") %></h4>
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> 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 <% else %> 14 <% else %>
15 <h4><%= _("Wait for admin approvement!") %></h4> 15 <h4><%= _("Wait for admin approvement!") %></h4>
16 <p><%= _("The administrators will evaluate your signup request for approvement.") %></p> 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 <% end %> 18 <% end %>
19 <h4><%= _("What to do next?") %></h4> 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 (user.present? ? link_to(_('Profile'), {:controller => 'profile', :profile => user.identifier}, :target => '_blank') : 'Profile') %> 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 [user.present? ? link_to(_('Control Panel'), {:controller => 'profile_editor', :profile => user.identifier}, :target => '_blank') : 'Control Panel', 23 [user.present? ? link_to(_('Control Panel'), {:controller => 'profile_editor', :profile => user.identifier}, :target => '_blank') : 'Control Panel',
24 link_to(_('tips'), {:controller => 'doc', :action => 'topic', :section => 'user', :topic => 'editing-person-info'}, :target => '_blank')] %></p> 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 <p><%= _("Start exploring and have fun!") %></p> 27 <p><%= _("Start exploring and have fun!") %></p>
28 <% end %> 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 </div> 30 </div>
app/views/person_notifier/mailer/_create_article.html.erb
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 <td> 5 <td>
6 <p> 6 <p>
7 <span style="font-size: 14px;"><%= link_to activity.user.short_name(20), activity.user.url %></span> 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 <span style="font-size: 10px; color: #929292; float:right;"><%= time_ago_in_words(activity.created_at) %></span> 9 <span style="font-size: 10px; color: #929292; float:right;"><%= time_ago_in_words(activity.created_at) %></span>
10 </p> 10 </p>
11 <p> 11 <p>
app/views/person_notifier/mailer/_new_follower.html.erb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +<%= render :partial => 'default_activity', :locals => { :activity => activity } %>
app/views/person_notifier/mailer/_profile_comments.html.erb
1 <% if activity.comments_count > 2 %> 1 <% if activity.comments_count > 2 %>
2 <div style="font-size: 10px;"> 2 <div style="font-size: 10px;">
3 <% if activity.params['url'].blank? %> 3 <% if activity.params['url'].blank? %>
4 - <%= _("%s comments") % activity.comments_count %> 4 + <%= _("%s comments").html_safe % activity.comments_count %>
5 <% else %> 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 <% end %> 7 <% end %>
8 </div> 8 </div>
9 <% else %> 9 <% else %>
app/views/person_notifier/mailer/content_summary.html.erb
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 <%= link_to @url, :style => "text-decoration: none;" do %> 5 <%= link_to @url, :style => "text-decoration: none;" do %>
6 <span style="font-weight:bold;font-size: 28px;margin: 0;color: white;background-color: #AAAAAA;padding: 5px;"><%= @environment.name %></span> 6 <span style="font-weight:bold;font-size: 28px;margin: 0;color: white;background-color: #AAAAAA;padding: 5px;"><%= @environment.name %></span>
7 <% end %> 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 </div> 9 </div>
10 <div style="margin: 0 20px 20px 20px;border-top:1px solid #e2e2e2;"> 10 <div style="margin: 0 20px 20px 20px;border-top:1px solid #e2e2e2;">
11 <% if @tasks.present? %> 11 <% if @tasks.present? %>
@@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
34 34
35 <div style="color:#444444;font-size:11px;margin-bottom: 20px;"> 35 <div style="color:#444444;font-size:11px;margin-bottom: 20px;">
36 <p style="margin:0"><%= _("Greetings,") %></p> 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 <p style="margin:0"><%= link_to @url, url_for(@url) %></p> 38 <p style="margin:0"><%= link_to @url, url_for(@url) %></p>
39 </div> 39 </div>
40 </div> 40 </div>
app/views/profile/_follow.html.erb 0 → 100644
@@ -0,0 +1,18 @@ @@ -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 @@ @@ -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,5 +13,5 @@
13 <%= button(:add, content_tag('span', _('Add friend')), profile.add_url, :class => 'add-friend', :title => _("Add friend"), :style => 'position: relative;') %> 13 <%= button(:add, content_tag('span', _('Add friend')), profile.add_url, :class => 'add-friend', :title => _("Add friend"), :style => 'position: relative;') %>
14 <% end %> 14 <% end %>
15 <%= button :back, _('Go back'), :back %> 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 <% end %> 17 <% end %>
app/views/profile/_profile_comments.html.erb
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 <% if activity.comments_count > 0 %> 5 <% if activity.comments_count > 0 %>
6 <div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments" > 6 <div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments" >
7 <div class='view-all-comments icon-chat'> 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 </div> 9 </div>
10 </div> 10 </div>
11 <% end %> 11 <% end %>
app/views/profile/_profile_scraps.html.erb
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 <% if scrap.replies.count > 0 %> 23 <% if scrap.replies.count > 0 %>
24 <div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments"> 24 <div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments">
25 <div class='view-all-comments icon-chat'> 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 </div> 27 </div>
28 </div> 28 </div>
29 <% end %> 29 <% end %>
app/views/profile/followed.html.erb 0 → 100644
@@ -0,0 +1,7 @@ @@ -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>