Compare View

switch
from
...
to
 
Commits (128)
Showing 341 changed files   Show diff stats

Too many changes.

To preserve performance only 100 of 341 files displayed.

app/api/app.rb
@@ -54,6 +54,7 @@ module Api @@ -54,6 +54,7 @@ module Api
54 mount V1::Blocks 54 mount V1::Blocks
55 mount V1::Profiles 55 mount V1::Profiles
56 mount V1::Activities 56 mount V1::Activities
  57 + mount V1::Roles
57 58
58 # hook point which allow plugins to add Grape::API extensions to Api::App 59 # hook point which allow plugins to add Grape::API extensions to Api::App
59 #finds for plugins which has api mount points classes defined (the class should extends Grape::API) 60 #finds for plugins which has api mount points classes defined (the class should extends Grape::API)
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
@@ -281,5 +302,12 @@ module Api @@ -281,5 +302,12 @@ module Api
281 type_map.first.represent(activity.target) unless type_map.nil? 302 type_map.first.represent(activity.target) unless type_map.nil?
282 end 303 end
283 end 304 end
  305 +
  306 + class Role < Entity
  307 + root 'roles', 'role'
  308 + expose :id
  309 + expose :name
  310 + expose :key
  311 + end
284 end 312 end
285 end 313 end
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
@@ -124,8 +124,8 @@ module Api @@ -124,8 +124,8 @@ module Api
124 present_partial article, with: Entities::Article, params: params, current_person: current_person 124 present_partial article, with: Entities::Article, params: params, current_person: current_person
125 end 125 end
126 126
127 - def present_articles_for_asset(asset, method = 'articles')  
128 - articles = find_articles(asset, method) 127 + def present_articles_for_asset(asset, method_or_relation = 'articles')
  128 + articles = find_articles(asset, method_or_relation)
129 present_articles(articles) 129 present_articles(articles)
130 end 130 end
131 131
@@ -133,8 +133,8 @@ module Api @@ -133,8 +133,8 @@ module Api
133 present_partial paginate(articles), :with => Entities::Article, :params => params, current_person: current_person 133 present_partial paginate(articles), :with => Entities::Article, :params => params, current_person: current_person
134 end 134 end
135 135
136 - def find_articles(asset, method = 'articles')  
137 - articles = select_filtered_collection_of(asset, method, params) 136 + def find_articles(asset, method_or_relation = 'articles')
  137 + articles = select_filtered_collection_of(asset, method_or_relation, params)
138 if current_person.present? 138 if current_person.present?
139 articles = articles.display_filter(current_person, nil) 139 articles = articles.display_filter(current_person, nil)
140 else 140 else
@@ -143,14 +143,17 @@ module Api @@ -143,14 +143,17 @@ module Api
143 articles 143 articles
144 end 144 end
145 145
146 - def find_task(asset, id)  
147 - task = asset.tasks.find(id) 146 + def find_task(asset, method_or_relation, id)
  147 + task = is_a_relation?(method_or_relation) ? method_or_relation : asset.send(method_or_relation)
  148 + task = task.find_by_id(id)
  149 + not_found! if task.blank?
148 current_person.has_permission?(task.permission, asset) ? task : forbidden! 150 current_person.has_permission?(task.permission, asset) ? task : forbidden!
149 end 151 end
150 152
151 def post_task(asset, params) 153 def post_task(asset, params)
152 klass_type= params[:content_type].nil? ? 'Task' : params[:content_type] 154 klass_type= params[:content_type].nil? ? 'Task' : params[:content_type]
153 return forbidden! unless klass_type.constantize <= Task 155 return forbidden! unless klass_type.constantize <= Task
  156 + return forbidden! if !current_person.has_permission?(:perform_task, asset)
154 157
155 task = klass_type.constantize.new(params[:task]) 158 task = klass_type.constantize.new(params[:task])
156 task.requestor_id = current_person.id 159 task.requestor_id = current_person.id
@@ -163,15 +166,24 @@ module Api @@ -163,15 +166,24 @@ module Api
163 present_partial task, :with => Entities::Task 166 present_partial task, :with => Entities::Task
164 end 167 end
165 168
166 - def present_task(asset)  
167 - task = find_task(asset, params[:id]) 169 + def find_tasks(asset, method_or_relation = 'tasks')
  170 + return forbidden! if !current_person.has_permission?(:perform_task, asset)
  171 + tasks = select_filtered_collection_of(asset, method_or_relation, params)
  172 + tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)}
  173 + tasks
  174 + end
  175 +
  176 + def present_task(asset, method_or_relation = 'tasks')
  177 + task = find_task(asset, method_or_relation, params[:id])
168 present_partial task, :with => Entities::Task 178 present_partial task, :with => Entities::Task
169 end 179 end
170 180
171 - def present_tasks(asset)  
172 - tasks = select_filtered_collection_of(asset, 'tasks', params)  
173 - tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)}  
174 - return forbidden! if tasks.empty? && !current_person.has_permission?(:perform_task, asset) 181 + def present_tasks_for_asset(asset, method_or_relation = 'tasks')
  182 + tasks = find_tasks(asset, method_or_relation)
  183 + present_tasks(tasks)
  184 + end
  185 +
  186 + def present_tasks(tasks)
175 present_partial tasks, :with => Entities::Task 187 present_partial tasks, :with => Entities::Task
176 end 188 end
177 189
@@ -180,7 +192,6 @@ module Api @@ -180,7 +192,6 @@ module Api
180 conditions = {} 192 conditions = {}
181 from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from] 193 from_date = DateTime.parse(parsed_params.delete(:from)) if parsed_params[:from]
182 until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until] 194 until_date = DateTime.parse(parsed_params.delete(:until)) if parsed_params[:until]
183 -  
184 conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil? 195 conditions[:type] = parse_content_type(parsed_params.delete(:content_type)) unless parsed_params[:content_type].nil?
185 196
186 conditions[:created_at] = period(from_date, until_date) if from_date || until_date 197 conditions[:created_at] = period(from_date, until_date) if from_date || until_date
@@ -190,7 +201,7 @@ module Api @@ -190,7 +201,7 @@ module Api
190 end 201 end
191 202
192 # changing make_order_with_parameters to avoid sql injection 203 # changing make_order_with_parameters to avoid sql injection
193 - def make_order_with_parameters(object, method, params) 204 + def make_order_with_parameters(object, method_or_relation, params)
194 order = "created_at DESC" 205 order = "created_at DESC"
195 unless params[:order].blank? 206 unless params[:order].blank?
196 if params[:order].include? '\'' or params[:order].include? '"' 207 if params[:order].include? '\'' or params[:order].include? '"'
@@ -199,9 +210,9 @@ module Api @@ -199,9 +210,9 @@ module Api
199 order = 'RANDOM()' 210 order = 'RANDOM()'
200 else 211 else
201 field_name, direction = params[:order].split(' ') 212 field_name, direction = params[:order].split(' ')
202 - assoc = object.class.reflect_on_association(method.to_sym)  
203 - if !field_name.blank? and assoc  
204 - if assoc.klass.attribute_names.include? field_name 213 + assoc_class = extract_associated_classname(method_or_relation)
  214 + if !field_name.blank? and assoc_class
  215 + if assoc_class.attribute_names.include? field_name
205 if direction.present? and ['ASC','DESC'].include? direction.upcase 216 if direction.present? and ['ASC','DESC'].include? direction.upcase
206 order = "#{field_name} #{direction.upcase}" 217 order = "#{field_name} #{direction.upcase}"
207 end 218 end
@@ -212,12 +223,14 @@ module Api @@ -212,12 +223,14 @@ module Api
212 return order 223 return order
213 end 224 end
214 225
215 - def make_timestamp_with_parameters_and_method(params, method) 226 + def make_timestamp_with_parameters_and_method(params, method_or_relation)
216 timestamp = nil 227 timestamp = nil
217 if params[:timestamp] 228 if params[:timestamp]
218 datetime = DateTime.parse(params[:timestamp]) 229 datetime = DateTime.parse(params[:timestamp])
219 - table_name = method.to_s.singularize.camelize.constantize.table_name  
220 - timestamp = "#{table_name}.updated_at >= '#{datetime}'" 230 + table_name = extract_associated_tablename(method_or_relation)
  231 + assoc_class = extract_associated_classname(method_or_relation)
  232 + date_atrr = assoc_class.attribute_names.include?('updated_at') ? 'updated_at' : 'created_at'
  233 + timestamp = "#{table_name}.#{date_atrr} >= '#{datetime}'"
221 end 234 end
222 235
223 timestamp 236 timestamp
@@ -242,12 +255,12 @@ module Api @@ -242,12 +255,12 @@ module Api
242 end 255 end
243 end 256 end
244 257
245 - def select_filtered_collection_of(object, method, params) 258 + def select_filtered_collection_of(object, method_or_relation, params)
246 conditions = make_conditions_with_parameter(params) 259 conditions = make_conditions_with_parameter(params)
247 - order = make_order_with_parameters(object,method,params)  
248 - timestamp = make_timestamp_with_parameters_and_method(params, method) 260 + order = make_order_with_parameters(object,method_or_relation,params)
  261 + timestamp = make_timestamp_with_parameters_and_method(params, method_or_relation)
249 262
250 - objects = object.send(method) 263 + objects = is_a_relation?(method_or_relation) ? method_or_relation : object.send(method_or_relation)
251 objects = by_reference(objects, params) 264 objects = by_reference(objects, params)
252 objects = by_categories(objects, params) 265 objects = by_categories(objects, params)
253 266
@@ -302,12 +315,12 @@ module Api @@ -302,12 +315,12 @@ module Api
302 end 315 end
303 316
304 def cant_be_saved_request!(attribute) 317 def cant_be_saved_request!(attribute)
305 - message = _("(Invalid request) %s can't be saved") % attribute 318 + message = _("(Invalid request) %s can't be saved").html_safe % attribute
306 render_api_error!(message, 400) 319 render_api_error!(message, 400)
307 end 320 end
308 321
309 def bad_request!(attribute) 322 def bad_request!(attribute)
310 - message = _("(Invalid request) %s not given") % attribute 323 + message = _("(Invalid request) %s not given").html_safe % attribute
311 render_api_error!(message, 400) 324 render_api_error!(message, 400)
312 end 325 end
313 326
@@ -393,10 +406,27 @@ module Api @@ -393,10 +406,27 @@ module Api
393 end 406 end
394 private 407 private
395 408
  409 + def extract_associated_tablename(method_or_relation)
  410 + extract_associated_classname(method_or_relation).table_name
  411 + end
  412 +
  413 + def extract_associated_classname(method_or_relation)
  414 + if is_a_relation?(method_or_relation)
  415 + method_or_relation.blank? ? '' : method_or_relation.first.class
  416 + else
  417 + method_or_relation.to_s.singularize.camelize.constantize
  418 + end
  419 + end
  420 +
  421 + def is_a_relation?(method_or_relation)
  422 + method_or_relation.kind_of?(ActiveRecord::Relation)
  423 + end
  424 +
  425 +
396 def parser_params(params) 426 def parser_params(params)
397 parsed_params = {} 427 parsed_params = {}
398 params.map do |k,v| 428 params.map do |k,v|
399 - parsed_params[k.to_sym] = v if DEFAULT_ALLOWED_PARAMETERS.include?(k.to_sym) 429 + parsed_params[k.to_sym] = v if ALLOWED_PARAMETERS.include?(k.to_sym)
400 end 430 end
401 parsed_params 431 parsed_params
402 end 432 end
app/api/v1/people.rb
@@ -119,6 +119,20 @@ module Api @@ -119,6 +119,20 @@ module Api
119 members = select_filtered_collection_of(profile, 'members', params) 119 members = select_filtered_collection_of(profile, 'members', params)
120 present members, :with => Entities::Person, :current_person => current_person 120 present members, :with => Entities::Person, :current_person => current_person
121 end 121 end
  122 +
  123 + post do
  124 + authenticate!
  125 + profile = environment.profiles.find_by id: params[:profile_id]
  126 + profile.add_member(current_person) rescue forbidden!
  127 + {pending: !current_person.is_member_of?(profile)}
  128 + end
  129 +
  130 + delete do
  131 + authenticate!
  132 + profile = environment.profiles.find_by id: params[:profile_id]
  133 + profile.remove_member(current_person)
  134 + present current_person, :with => Entities::Person, :current_person => current_person
  135 + end
122 end 136 end
123 end 137 end
124 end 138 end
app/api/v1/roles.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +module Api
  2 + module V1
  3 + class Roles < Grape::API
  4 + before { authenticate! }
  5 +
  6 + MAX_PER_PAGE = 50
  7 +
  8 + resource :profiles do
  9 + segment "/:profile_id" do
  10 + resource :roles do
  11 +
  12 + paginate max_per_page: MAX_PER_PAGE
  13 + get do
  14 + profile = environment.profiles.find(params[:profile_id])
  15 + return forbidden! unless profile.kind_of?(Organization)
  16 + roles = Profile::Roles.organization_roles(profile.environment.id, profile.id)
  17 + present_partial paginate(roles), with: Entities::Role
  18 + end
  19 +
  20 + end
  21 + end
  22 + end
  23 + end
  24 + end
  25 +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,27 @@ module Api @@ -17,15 +18,27 @@ 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 + begin
  35 + task.update(params[:task])
  36 + task.send(action, current_person) if (task.status == Task::Status::ACTIVE)
  37 + present_partial task, :with => Entities::Task
  38 + rescue Exception => ex
  39 + render_api_error!(ex.message, 500)
  40 + end
  41 + end
29 end 42 end
30 end 43 end
31 44
@@ -36,7 +49,8 @@ module Api @@ -36,7 +49,8 @@ module Api
36 resource :tasks do 49 resource :tasks do
37 get do 50 get do
38 profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) 51 profile = environment.send(kind.pluralize).find(params["#{kind}_id"])
39 - present_tasks(profile) 52 + tasks = Task.to(profile)
  53 + present_tasks_for_asset(profile, tasks)
40 end 54 end
41 55
42 get ':id' do 56 get ':id' do
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
@@ -14,6 +14,20 @@ class ApplicationController &lt; ActionController::Base @@ -14,6 +14,20 @@ class ApplicationController &lt; ActionController::Base
14 before_filter :redirect_to_current_user 14 before_filter :redirect_to_current_user
15 15
16 before_filter :set_session_theme 16 before_filter :set_session_theme
  17 +
  18 + # FIXME: only include necessary methods
  19 + include ApplicationHelper
  20 +
  21 + # concerns
  22 + include PermissionCheck
  23 + include CustomDesign
  24 + include NeedsProfile
  25 +
  26 + # implementations
  27 + include FindByContents
  28 + include Noosfero::Plugin::HotSpot
  29 + include SearchTermHelper
  30 +
17 def set_session_theme 31 def set_session_theme
18 if params[:theme] 32 if params[:theme]
19 session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil 33 session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil
@@ -48,7 +62,6 @@ class ApplicationController &lt; ActionController::Base @@ -48,7 +62,6 @@ class ApplicationController &lt; ActionController::Base
48 end 62 end
49 end 63 end
50 64
51 - include ApplicationHelper  
52 layout :get_layout 65 layout :get_layout
53 def get_layout 66 def get_layout
54 return false if request.format == :js or request.xhr? 67 return false if request.format == :js or request.xhr?
@@ -74,9 +87,6 @@ class ApplicationController &lt; ActionController::Base @@ -74,9 +87,6 @@ class ApplicationController &lt; ActionController::Base
74 helper :document 87 helper :document
75 helper :language 88 helper :language
76 89
77 - include DesignHelper  
78 - include PermissionCheck  
79 -  
80 before_filter :set_locale 90 before_filter :set_locale
81 def set_locale 91 def set_locale
82 FastGettext.available_locales = environment.available_locales 92 FastGettext.available_locales = environment.available_locales
@@ -89,8 +99,6 @@ class ApplicationController &lt; ActionController::Base @@ -89,8 +99,6 @@ class ApplicationController &lt; ActionController::Base
89 end 99 end
90 end 100 end
91 101
92 - include NeedsProfile  
93 -  
94 attr_reader :environment 102 attr_reader :environment
95 103
96 # declares that the given <tt>actions</tt> cannot be accessed by other HTTP 104 # declares that the given <tt>actions</tt> cannot be accessed by other HTTP
@@ -107,6 +115,10 @@ class ApplicationController &lt; ActionController::Base @@ -107,6 +115,10 @@ class ApplicationController &lt; ActionController::Base
107 115
108 protected 116 protected
109 117
  118 + def accept_only_post
  119 + return render_not_found if !request.post?
  120 + end
  121 +
110 def verified_request? 122 def verified_request?
111 super || form_authenticity_token == request.headers['X-XSRF-TOKEN'] 123 super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
112 end 124 end
@@ -151,8 +163,6 @@ class ApplicationController &lt; ActionController::Base @@ -151,8 +163,6 @@ class ApplicationController &lt; ActionController::Base
151 end 163 end
152 end 164 end
153 165
154 - include Noosfero::Plugin::HotSpot  
155 -  
156 # FIXME this filter just loads @plugins to children controllers and helpers 166 # FIXME this filter just loads @plugins to children controllers and helpers
157 def init_noosfero_plugins 167 def init_noosfero_plugins
158 plugins 168 plugins
@@ -184,9 +194,6 @@ class ApplicationController &lt; ActionController::Base @@ -184,9 +194,6 @@ class ApplicationController &lt; ActionController::Base
184 end 194 end
185 end 195 end
186 196
187 - include SearchTermHelper  
188 - include FindByContents  
189 -  
190 def find_suggestions(query, context, asset, options={}) 197 def find_suggestions(query, context, asset, options={})
191 plugins.dispatch_first(:find_suggestions, query, context, asset, options) 198 plugins.dispatch_first(:find_suggestions, query, context, asset, options)
192 end 199 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,169 @@ @@ -0,0 +1,169 @@
  1 +module AuthenticatedSystem
  2 +
  3 + protected
  4 +
  5 + extend ActiveSupport::Concern
  6 +
  7 + included do
  8 + if self < ActionController::Base
  9 + around_filter :user_set_current
  10 + before_filter :override_user
  11 + before_filter :login_from_cookie
  12 + end
  13 +
  14 + # Inclusion hook to make #current_user and #logged_in?
  15 + # available as ActionView helper methods.
  16 + helper_method :current_user, :logged_in?
  17 + end
  18 +
  19 + # Returns true or false if the user is logged in.
  20 + # Preloads @current_user with the user model if they're logged in.
  21 + def logged_in?
  22 + current_user != nil
  23 + end
  24 +
  25 + # Accesses the current user from the session.
  26 + def current_user user_id = session[:user]
  27 + @current_user ||= begin
  28 + user = User.find_by id: user_id if user_id
  29 + user.session = session if user
  30 + User.current = user
  31 + user
  32 + end
  33 + end
  34 +
  35 + # Store the given user in the session.
  36 + def current_user=(new_user)
  37 + if new_user.nil?
  38 + session.delete(:user)
  39 + else
  40 + session[:user] = new_user.id
  41 + new_user.session = session
  42 + new_user.register_login
  43 + end
  44 + @current_user = User.current = new_user
  45 + end
  46 +
  47 + # See impl. from http://stackoverflow.com/a/2513456/670229
  48 + def user_set_current
  49 + User.current = current_user
  50 + yield
  51 + ensure
  52 + # to address the thread variable leak issues in Puma/Thin webserver
  53 + User.current = nil
  54 + end
  55 +
  56 + # Check if the user is authorized.
  57 + #
  58 + # Override this method in your controllers if you want to restrict access
  59 + # to only a few actions or if you want to check if the user
  60 + # has the correct rights.
  61 + #
  62 + # Example:
  63 + #
  64 + # # only allow nonbobs
  65 + # def authorize?
  66 + # current_user.login != "bob"
  67 + # end
  68 + def authorized?
  69 + true
  70 + end
  71 +
  72 + # Filter method to enforce a login requirement.
  73 + #
  74 + # To require logins for all actions, use this in your controllers:
  75 + #
  76 + # before_filter :login_required
  77 + #
  78 + # To require logins for specific actions, use this in your controllers:
  79 + #
  80 + # before_filter :login_required, :only => [ :edit, :update ]
  81 + #
  82 + # To skip this in a subclassed controller:
  83 + #
  84 + # skip_before_filter :login_required
  85 + #
  86 + def login_required
  87 + username, passwd = get_auth_data
  88 + if username && passwd
  89 + self.current_user ||= User.authenticate(username, passwd) || nil
  90 + end
  91 + if logged_in? && authorized?
  92 + true
  93 + else
  94 + if params[:require_login_popup]
  95 + render :json => { :require_login_popup => true }
  96 + else
  97 + access_denied
  98 + end
  99 + end
  100 + end
  101 +
  102 + # Redirect as appropriate when an access request fails.
  103 + #
  104 + # The default action is to redirect to the login screen.
  105 + #
  106 + # Override this method in your controllers if you want to have special
  107 + # behavior in case the user is not authorized
  108 + # to access the requested action. For example, a popup window might
  109 + # simply close itself.
  110 + def access_denied
  111 + respond_to do |accepts|
  112 + accepts.html do
  113 + if request.xhr?
  114 + render :text => _('Access denied'), :status => 401
  115 + else
  116 + store_location
  117 + redirect_to :controller => '/account', :action => 'login'
  118 + end
  119 + end
  120 + accepts.xml do
  121 + headers["Status"] = "Unauthorized"
  122 + headers["WWW-Authenticate"] = %(Basic realm="Web Password")
  123 + render :text => "Could't authenticate you", :status => '401 Unauthorized'
  124 + end
  125 + end
  126 + false
  127 + end
  128 +
  129 + # Store the URI of the current request in the session.
  130 + #
  131 + # We can return to this location by calling #redirect_back_or_default.
  132 + def store_location(location = request.url)
  133 + session[:return_to] = location
  134 + end
  135 +
  136 + # Redirect to the URI stored by the most recent store_location call or
  137 + # to the passed default.
  138 + def redirect_back_or_default(default)
  139 + if session[:return_to]
  140 + redirect_to(session.delete(:return_to))
  141 + else
  142 + redirect_to(default)
  143 + end
  144 + end
  145 +
  146 + def override_user
  147 + return if params[:override_user].blank?
  148 + return unless logged_in? and user.is_admin? environment
  149 + @current_user = nil
  150 + current_user params[:override_user]
  151 + end
  152 +
  153 + # When called with before_filter :login_from_cookie will check for an :auth_token
  154 + # cookie and log the user back in if apropriate
  155 + def login_from_cookie
  156 + return if cookies[:auth_token].blank? or logged_in?
  157 + user = User.where(remember_token: cookies[:auth_token]).first
  158 + self.current_user = user if user and user.remember_token?
  159 + end
  160 +
  161 + private
  162 + @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
  163 + # gets BASIC auth info
  164 + def get_auth_data
  165 + auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
  166 + auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
  167 + return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
  168 + end
  169 +end
app/controllers/concerns/custom_design.rb 0 → 100644
@@ -0,0 +1,50 @@ @@ -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_user.person.is_admin?(profile.environment) 218 if current_user.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.')
@@ -437,4 +476,8 @@ class ProfileController &lt; PublicController @@ -437,4 +476,8 @@ class ProfileController &lt; PublicController
437 [:image, :domains, :preferred_domain, :environment] 476 [:image, :domains, :preferred_domain, :environment]
438 end 477 end
439 478
  479 + def allow_followers?
  480 + render_not_found unless profile.allow_followers?
  481 + end
  482 +
440 end 483 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/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/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
@@ -200,6 +200,7 @@ class Environment &lt; ApplicationRecord @@ -200,6 +200,7 @@ class Environment &lt; ApplicationRecord
200 # Relationships and applied behaviour 200 # Relationships and applied behaviour
201 # ################################################# 201 # #################################################
202 202
  203 + extend ActsAsHavingBoxes::ClassMethods
203 acts_as_having_boxes 204 acts_as_having_boxes
204 205
205 after_create do |env| 206 after_create do |env|
@@ -251,7 +252,8 @@ class Environment &lt; ApplicationRecord @@ -251,7 +252,8 @@ class Environment &lt; ApplicationRecord
251 # ################################################# 252 # #################################################
252 253
253 # store the Environment settings as YAML-serialized Hash. 254 # store the Environment settings as YAML-serialized Hash.
254 - acts_as_having_settings :field => :settings 255 + extend ActsAsHavingSettings::ClassMethods
  256 + acts_as_having_settings field: :settings
255 257
256 # introduce and explain to users something about the signup 258 # introduce and explain to users something about the signup
257 settings_items :signup_intro, :type => String 259 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/person.rb
@@ -8,7 +8,6 @@ class Person &lt; Profile @@ -8,7 +8,6 @@ class Person &lt; Profile
8 :display => %w[compact] 8 :display => %w[compact]
9 } 9 }
10 10
11 -  
12 def self.type_name 11 def self.type_name
13 _('Person') 12 _('Person')
14 end 13 end
@@ -93,6 +92,7 @@ class Person &lt; Profile @@ -93,6 +92,7 @@ class Person &lt; Profile
93 has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article 92 has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article
94 has_many :friendships, :dependent => :destroy 93 has_many :friendships, :dependent => :destroy
95 has_many :friends, :class_name => 'Person', :through => :friendships 94 has_many :friends, :class_name => 'Person', :through => :friendships
  95 + has_many :circles
96 96
97 scope :online, -> { 97 scope :online, -> {
98 joins(:user).where("users.chat_status != '' AND users.chat_status_at >= ?", DateTime.now - User.expires_chat_status_every.minutes) 98 joins(:user).where("users.chat_status != '' AND users.chat_status_at >= ?", DateTime.now - User.expires_chat_status_every.minutes)
@@ -200,6 +200,33 @@ class Person &lt; Profile @@ -200,6 +200,33 @@ class Person &lt; Profile
200 end 200 end
201 end 201 end
202 202
  203 + def follow(profile, circles)
  204 + circles = [circles] unless circles.is_a?(Array)
  205 + circles.each do |new_circle|
  206 + ProfileFollower.create(profile: profile, circle: new_circle)
  207 + end
  208 + end
  209 +
  210 + def update_profile_circles(profile, new_circles)
  211 + profile_circles = ProfileFollower.with_profile(profile).with_follower(self).map(&:circle)
  212 + circles_to_add = new_circles - profile_circles
  213 + circles_to_remove = profile_circles - new_circles
  214 + circles_to_add.each do |new_circle|
  215 + ProfileFollower.create(profile: profile, circle: new_circle)
  216 + end
  217 +
  218 + ProfileFollower.where('circle_id IN (?) AND profile_id = ?',
  219 + circles_to_remove.map(&:id), profile.id).destroy_all
  220 + end
  221 +
  222 + def unfollow(profile)
  223 + ProfileFollower.with_follower(self).with_profile(profile).destroy_all
  224 + end
  225 +
  226 + def remove_profile_from_circle(profile, circle)
  227 + ProfileFollower.with_profile(profile).with_circle(circle).destroy_all
  228 + end
  229 +
203 def already_request_friendship?(person) 230 def already_request_friendship?(person)
204 person.tasks.where(requestor_id: self.id, type: 'AddFriend', status: Task::Status::ACTIVE).first 231 person.tasks.where(requestor_id: self.id, type: 'AddFriend', status: Task::Status::ACTIVE).first
205 end 232 end
@@ -580,9 +607,12 @@ class Person &lt; Profile @@ -580,9 +607,12 @@ class Person &lt; Profile
580 person.has_permission?(:manage_friends, self) 607 person.has_permission?(:manage_friends, self)
581 end 608 end
582 609
583 - protected 610 + def followed_profiles
  611 + Profile.followed_by self
  612 + end
584 613
585 - def followed_by?(profile)  
586 - self == profile || self.is_a_friend?(profile) 614 + def in_social_circle?(person)
  615 + self.is_a_friend?(person) || super
587 end 616 end
  617 +
588 end 618 end
app/models/profile.rb
@@ -5,7 +5,7 @@ class Profile &lt; ApplicationRecord @@ -5,7 +5,7 @@ class Profile &lt; ApplicationRecord
5 5
6 attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time, 6 attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time,
7 :redirection_after_login, :custom_url_redirection, 7 :redirection_after_login, :custom_url_redirection,
8 - :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification 8 + :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification, :allow_followers
9 9
10 # use for internationalizable human type names in search facets 10 # use for internationalizable human type names in search facets
11 # reimplement on subclasses 11 # reimplement on subclasses
@@ -52,6 +52,9 @@ class Profile &lt; ApplicationRecord @@ -52,6 +52,9 @@ class Profile &lt; ApplicationRecord
52 def self.organization_custom_roles(env_id, profile_id) 52 def self.organization_custom_roles(env_id, profile_id)
53 all_roles(env_id).where('profile_id = ?', profile_id) 53 all_roles(env_id).where('profile_id = ?', profile_id)
54 end 54 end
  55 + def self.organization_roles(env_id, profile_id)
  56 + all_roles(env_id).where("profile_id = ? or key like 'profile_%'", profile_id)
  57 + end
55 def self.all_roles(env_id) 58 def self.all_roles(env_id)
56 Role.where(environment_id: env_id) 59 Role.where(environment_id: env_id)
57 end 60 end
@@ -88,6 +91,8 @@ class Profile &lt; ApplicationRecord @@ -88,6 +91,8 @@ class Profile &lt; ApplicationRecord
88 } 91 }
89 92
90 acts_as_accessible 93 acts_as_accessible
  94 +
  95 + include Customizable
91 acts_as_customizable 96 acts_as_customizable
92 97
93 include Noosfero::Plugin::HotSpot 98 include Noosfero::Plugin::HotSpot
@@ -185,6 +190,21 @@ class Profile &lt; ApplicationRecord @@ -185,6 +190,21 @@ class Profile &lt; ApplicationRecord
185 Person.members_of(self).by_role(roles) 190 Person.members_of(self).by_role(roles)
186 end 191 end
187 192
  193 + extend ActsAsHavingSettings::ClassMethods
  194 + acts_as_having_settings field: :data
  195 +
  196 + def settings
  197 + data
  198 + end
  199 +
  200 + settings_items :redirect_l10n, :type => :boolean, :default => false
  201 + settings_items :public_content, :type => :boolean, :default => true
  202 + settings_items :description
  203 + settings_items :fields_privacy, :type => :hash, :default => {}
  204 + settings_items :email_suggestions, :type => :boolean, :default => false
  205 + settings_items :profile_admin_mail_notification, :type => :boolean, :default => true
  206 +
  207 + extend ActsAsHavingBoxes::ClassMethods
188 acts_as_having_boxes 208 acts_as_having_boxes
189 209
190 acts_as_taggable 210 acts_as_taggable
@@ -203,6 +223,23 @@ class Profile &lt; ApplicationRecord @@ -203,6 +223,23 @@ class Profile &lt; ApplicationRecord
203 scope :more_active, -> { order 'activities_count DESC' } 223 scope :more_active, -> { order 'activities_count DESC' }
204 scope :more_recent, -> { order "created_at DESC" } 224 scope :more_recent, -> { order "created_at DESC" }
205 225
  226 + scope :followed_by, -> person{
  227 + distinct.select('profiles.*').
  228 + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id').
  229 + joins('left join circles ON circles.id = profiles_circles.circle_id').
  230 + where('circles.person_id = ?', person.id)
  231 + }
  232 +
  233 + scope :in_circle, -> circle{
  234 + distinct.select('profiles.*').
  235 + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id').
  236 + joins('left join circles ON circles.id = profiles_circles.circle_id').
  237 + where('circles.id = ?', circle.id)
  238 + }
  239 +
  240 + settings_items :allow_followers, :type => :boolean, :default => true
  241 + alias_method :allow_followers?, :allow_followers
  242 +
206 acts_as_trackable :dependent => :destroy 243 acts_as_trackable :dependent => :destroy
207 244
208 has_many :profile_activities 245 has_many :profile_activities
@@ -215,6 +252,9 @@ class Profile &lt; ApplicationRecord @@ -215,6 +252,9 @@ class Profile &lt; ApplicationRecord
215 252
216 has_many :email_templates, :foreign_key => :owner_id 253 has_many :email_templates, :foreign_key => :owner_id
217 254
  255 + has_many :profile_followers
  256 + has_many :followers, -> { uniq }, :class_name => 'Person', :through => :profile_followers, :source => :person
  257 +
218 # Although this should be a has_one relation, there are no non-silly names for 258 # Although this should be a has_one relation, there are no non-silly names for
219 # a foreign key on article to reference the template to which it is 259 # a foreign key on article to reference the template to which it is
220 # welcome_page... =P 260 # welcome_page... =P
@@ -231,19 +271,6 @@ class Profile &lt; ApplicationRecord @@ -231,19 +271,6 @@ class Profile &lt; ApplicationRecord
231 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap) 271 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap)
232 end 272 end
233 273
234 - acts_as_having_settings :field => :data  
235 -  
236 - def settings  
237 - data  
238 - end  
239 -  
240 - settings_items :redirect_l10n, :type => :boolean, :default => false  
241 - settings_items :public_content, :type => :boolean, :default => true  
242 - settings_items :description  
243 - settings_items :fields_privacy, :type => :hash, :default => {}  
244 - settings_items :email_suggestions, :type => :boolean, :default => false  
245 - settings_items :profile_admin_mail_notification, :type => :boolean, :default => true  
246 -  
247 validates_length_of :description, :maximum => 550, :allow_nil => true 274 validates_length_of :description, :maximum => 550, :allow_nil => true
248 275
249 # Valid identifiers must match this format. 276 # Valid identifiers must match this format.
@@ -285,6 +312,7 @@ class Profile &lt; ApplicationRecord @@ -285,6 +312,7 @@ class Profile &lt; ApplicationRecord
285 312
286 has_many :files, :class_name => 'UploadedFile' 313 has_many :files, :class_name => 'UploadedFile'
287 314
  315 + extend ActsAsHavingImage::ClassMethods
288 acts_as_having_image 316 acts_as_having_image
289 317
290 has_many :tasks, :dependent => :destroy, :as => 'target' 318 has_many :tasks, :dependent => :destroy, :as => 'target'
@@ -758,12 +786,13 @@ private :generate_url, :url_options @@ -758,12 +786,13 @@ private :generate_url, :url_options
758 786
759 # Adds a person as member of this Profile. 787 # Adds a person as member of this Profile.
760 def add_member(person, attributes={}) 788 def add_member(person, attributes={})
761 - if self.has_members? 789 + if self.has_members? && !self.secret
762 if self.closed? && members.count > 0 790 if self.closed? && members.count > 0
763 AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person) 791 AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person)
764 else 792 else
765 self.affiliate(person, Profile::Roles.admin(environment.id), attributes) if members.count == 0 793 self.affiliate(person, Profile::Roles.admin(environment.id), attributes) if members.count == 0
766 self.affiliate(person, Profile::Roles.member(environment.id), attributes) 794 self.affiliate(person, Profile::Roles.member(environment.id), attributes)
  795 + person.follow(self, Circle.find_or_create_by(:person => person, :name =>_('memberships'), :profile_type => 'Community'))
767 end 796 end
768 person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel } 797 person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel }
769 remove_from_suggestion_list person 798 remove_from_suggestion_list person
@@ -1107,7 +1136,11 @@ private :generate_url, :url_options @@ -1107,7 +1136,11 @@ private :generate_url, :url_options
1107 end 1136 end
1108 1137
1109 def followed_by?(person) 1138 def followed_by?(person)
1110 - person.is_member_of?(self) 1139 + (person == self) || (person.in? self.followers)
  1140 + end
  1141 +
  1142 + def in_social_circle?(person)
  1143 + (person == self) || (person.is_member_of?(self))
1111 end 1144 end
1112 1145
1113 def display_private_info_to?(user) 1146 def display_private_info_to?(user)
@@ -1148,4 +1181,8 @@ private :generate_url, :url_options @@ -1148,4 +1181,8 @@ private :generate_url, :url_options
1148 def allow_destroy?(person = nil) 1181 def allow_destroy?(person = nil)
1149 person.kind_of?(Profile) && person.has_permission?('destroy_profile', self) 1182 person.kind_of?(Profile) && person.has_permission?('destroy_profile', self)
1150 end 1183 end
  1184 +
  1185 + def in_circle?(circle, follower)
  1186 + ProfileFollower.with_follower(follower).with_circle(circle).with_profile(self).present?
  1187 + end
1151 end 1188 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>