Compare View
Commits (54)
-
- Environments and profiles now return the layout template used. Signed-off-by: Tallys Martins <tallysmartins@gmail.com>
-
Display layout_template on api - Environments and profiles now return the layout template used. @marcosronaldo can you take a look at this? We'll use it on angular theme. See merge request !968
-
Signed-off-by: Gabriel Silva <gabriel93.silva@gmail.com> Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com>
-
Organization_ratings: Show rejected message to admins Show rejected message to admins on organization_ratings plugin. Only environment admins can see the rejected message. Signed-off-by: Gabriel Silva <gabriel93.silva@gmail.com> Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com> See merge request !969
-
analytics: identify bots and filter them out by default This needs riot.js/serializers/i18n-js/js-routes See merge request !735
-
Signed-off-by: Gabriel Silva <gabriel93.silva@gmail.com> Signed-off-by: Luan Guimarães <guimaraesluan@me.com> Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com> Signed-off-by: Omar Junior <omarroinuj@gmail.com> Signed-off-by: Tallys Martins <tallysmartins@gmail.com>
-
Fix work assignment plugin issues on rails 4 - Fixed submissions not being displayed on work assignment page - Fixed email notifications not being sent See merge request !967
-
… recent activities of the profile who owns it
-
Recent activities Adding a plugin "recent_activities" which adds a block that list the recent activities of the profile who owns it: ![76](/uploads/000199ff2aaf4dd40f8f905ce11d884d/76.png) See merge request !972
-
The profile activities returns action trackers and scraps, but the scraps should not be displayed on recent activities block. Only the action tracker records Related to merge request !972
-
spaminator: update mailer to rails4 new scheme See merge request !971
-
api: add and remove members from profile See merge request !977
-
Signed-off-by: Alessandro Caetano <alessandro.caetanob@gmail.com> Signed-off-by: Artur Bersan de Faria <arturbersan@gmail.com> Signed-off-by: Gabriel Silva <gabriel93.silva@gmail.com> Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com> Signed-off-by: Matheus Miranda <matheusmirandalacerda@gmail.com> Signed-off-by: Sabryna Sousa <sabryna.sousa1323@gmail.com> Signed-off-by: Victor Matias Navarro <victor.matias.navarro@gmail.com> Signed-off-by: Vitor Barbosa <vitormga15@gmail.com>
-
Concern needs to be included before first use of setting items
-
Lists and edits followers and circles in control panel of profile that is being viewed instead of logged in person. Also unfollow person in control panel based on current viewed profile instead of current logged in user. This is necessary because sometimes an environment admin is editing someone else's profile and those actions should be performed on that profile instead of admin's profile.
-
Signed-off-by: Gabriel Silva <gabriel93.silva@gmail.com>
-
Profile followers feature Signed-off-by: Alessandro Caetano <alessandro.caetanob@gmail.com> Signed-off-by: Artur Bersan de Faria <arturbersan@gmail.com> Signed-off-by: Gabriel Silva <gabriel93.silva@gmail.com> Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com> Signed-off-by: Matheus Miranda <matheusmirandalacerda@gmail.com> Signed-off-by: Sabryna Sousa <sabryna.sousa1323@gmail.com> Signed-off-by: Victor Matias Navarro <victor.matias.navarro@gmail.com> Signed-off-by: Vitor Barbosa <vitormga15@gmail.com> See merge request !976
-
-Fix undefined 'to_model' when creating a new applicationa -Upgrade doorkeeper to 2.2.0 -Change several html forms to conform with the new version Signed-off-by: Gustavo Jaruga Cruz <darksshades@gmail.com> Signed-off-by: Thiago Ribeiro <thiagitosouza@gmail.com>
Showing
300 changed files
Show diff stats
Too many changes.
To preserve performance only 100 of 300 files displayed.
app/api/entities.rb
... | ... | @@ -124,6 +124,7 @@ module Api |
124 | 124 | expose :type |
125 | 125 | expose :custom_header |
126 | 126 | expose :custom_footer |
127 | + expose :layout_template | |
127 | 128 | expose :permissions do |profile, options| |
128 | 129 | Entities.permissions_for_entity(profile, options[:current_person], |
129 | 130 | :allow_post_content?, :allow_edit?, :allow_destroy?) |
... | ... | @@ -264,6 +265,7 @@ module Api |
264 | 265 | expose :name |
265 | 266 | expose :id |
266 | 267 | expose :description |
268 | + expose :layout_template | |
267 | 269 | expose :settings, if: lambda { |instance, options| options[:is_admin] } |
268 | 270 | end |
269 | 271 | ... | ... |
app/api/helpers.rb
... | ... | @@ -302,12 +302,12 @@ module Api |
302 | 302 | end |
303 | 303 | |
304 | 304 | def cant_be_saved_request!(attribute) |
305 | - message = _("(Invalid request) %s can't be saved") % attribute | |
305 | + message = _("(Invalid request) %s can't be saved").html_safe % attribute | |
306 | 306 | render_api_error!(message, 400) |
307 | 307 | end |
308 | 308 | |
309 | 309 | def bad_request!(attribute) |
310 | - message = _("(Invalid request) %s not given") % attribute | |
310 | + message = _("(Invalid request) %s not given").html_safe % attribute | |
311 | 311 | render_api_error!(message, 400) |
312 | 312 | end |
313 | 313 | ... | ... |
app/api/v1/people.rb
... | ... | @@ -119,6 +119,20 @@ module Api |
119 | 119 | members = select_filtered_collection_of(profile, 'members', params) |
120 | 120 | present members, :with => Entities::Person, :current_person => current_person |
121 | 121 | end |
122 | + | |
123 | + post do | |
124 | + authenticate! | |
125 | + profile = environment.profiles.find_by id: params[:profile_id] | |
126 | + profile.add_member(current_person) rescue forbidden! | |
127 | + {pending: !current_person.is_member_of?(profile)} | |
128 | + end | |
129 | + | |
130 | + delete do | |
131 | + authenticate! | |
132 | + profile = environment.profiles.find_by id: params[:profile_id] | |
133 | + profile.remove_member(current_person) | |
134 | + present current_person, :with => Entities::Person, :current_person => current_person | |
135 | + end | |
122 | 136 | end |
123 | 137 | end |
124 | 138 | end | ... | ... |
app/controllers/admin/categories_controller.rb
... | ... | @@ -44,7 +44,7 @@ class CategoriesController < AdminController |
44 | 44 | if request.post? |
45 | 45 | @category.update!(params[:category]) |
46 | 46 | @saved = true |
47 | - session[:notice] = _("Category %s saved." % @category.name) | |
47 | + session[:notice] = _("Category %s saved." % @category.name).html_safe | |
48 | 48 | redirect_to :action => 'index' |
49 | 49 | end |
50 | 50 | rescue Exception => e | ... | ... |
app/controllers/application_controller.rb
... | ... | @@ -14,6 +14,20 @@ class ApplicationController < ActionController::Base |
14 | 14 | before_filter :redirect_to_current_user |
15 | 15 | |
16 | 16 | before_filter :set_session_theme |
17 | + | |
18 | + # FIXME: only include necessary methods | |
19 | + include ApplicationHelper | |
20 | + | |
21 | + # concerns | |
22 | + include PermissionCheck | |
23 | + include CustomDesign | |
24 | + include NeedsProfile | |
25 | + | |
26 | + # implementations | |
27 | + include FindByContents | |
28 | + include Noosfero::Plugin::HotSpot | |
29 | + include SearchTermHelper | |
30 | + | |
17 | 31 | def set_session_theme |
18 | 32 | if params[:theme] |
19 | 33 | session[:theme] = environment.theme_ids.include?(params[:theme]) ? params[:theme] : nil |
... | ... | @@ -48,7 +62,6 @@ class ApplicationController < ActionController::Base |
48 | 62 | end |
49 | 63 | end |
50 | 64 | |
51 | - include ApplicationHelper | |
52 | 65 | layout :get_layout |
53 | 66 | def get_layout |
54 | 67 | return false if request.format == :js or request.xhr? |
... | ... | @@ -74,9 +87,6 @@ class ApplicationController < ActionController::Base |
74 | 87 | helper :document |
75 | 88 | helper :language |
76 | 89 | |
77 | - include DesignHelper | |
78 | - include PermissionCheck | |
79 | - | |
80 | 90 | before_filter :set_locale |
81 | 91 | def set_locale |
82 | 92 | FastGettext.available_locales = environment.available_locales |
... | ... | @@ -89,8 +99,6 @@ class ApplicationController < ActionController::Base |
89 | 99 | end |
90 | 100 | end |
91 | 101 | |
92 | - include NeedsProfile | |
93 | - | |
94 | 102 | attr_reader :environment |
95 | 103 | |
96 | 104 | # declares that the given <tt>actions</tt> cannot be accessed by other HTTP |
... | ... | @@ -107,6 +115,10 @@ class ApplicationController < ActionController::Base |
107 | 115 | |
108 | 116 | protected |
109 | 117 | |
118 | + def accept_only_post | |
119 | + return render_not_found if !request.post? | |
120 | + end | |
121 | + | |
110 | 122 | def verified_request? |
111 | 123 | super || form_authenticity_token == request.headers['X-XSRF-TOKEN'] |
112 | 124 | end |
... | ... | @@ -151,8 +163,6 @@ class ApplicationController < ActionController::Base |
151 | 163 | end |
152 | 164 | end |
153 | 165 | |
154 | - include Noosfero::Plugin::HotSpot | |
155 | - | |
156 | 166 | # FIXME this filter just loads @plugins to children controllers and helpers |
157 | 167 | def init_noosfero_plugins |
158 | 168 | plugins |
... | ... | @@ -184,9 +194,6 @@ class ApplicationController < ActionController::Base |
184 | 194 | end |
185 | 195 | end |
186 | 196 | |
187 | - include SearchTermHelper | |
188 | - include FindByContents | |
189 | - | |
190 | 197 | def find_suggestions(query, context, asset, options={}) |
191 | 198 | plugins.dispatch_first(:find_suggestions, query, context, asset, options) |
192 | 199 | end | ... | ... |
app/controllers/box_organizer_controller.rb
... | ... | @@ -109,7 +109,7 @@ class BoxOrganizerController < ApplicationController |
109 | 109 | def show_block_type_info |
110 | 110 | type = params[:type] |
111 | 111 | if type.blank? || !available_blocks.map(&:name).include?(type) |
112 | - raise ArgumentError.new("Type %s is not allowed. Go away." % type) | |
112 | + raise ArgumentError.new("Type %s is not allowed. Go away.".html_safe % type) | |
113 | 113 | end |
114 | 114 | @block = type.constantize.new |
115 | 115 | @block.box = Box.new(:owner => boxes_holder) |
... | ... | @@ -122,7 +122,7 @@ class BoxOrganizerController < ApplicationController |
122 | 122 | |
123 | 123 | def new_block(type, box) |
124 | 124 | if !available_blocks.map(&:name).include?(type) |
125 | - raise ArgumentError.new("Type %s is not allowed. Go away." % type) | |
125 | + raise ArgumentError.new("Type %s is not allowed. Go away.".html_safe % type) | |
126 | 126 | end |
127 | 127 | block = type.constantize.new |
128 | 128 | box.blocks << block | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -0,0 +1,43 @@ |
1 | +class FollowersController < MyProfileController | |
2 | + | |
3 | + before_action :only_for_person, :only => :index | |
4 | + before_action :accept_only_post, :only => [:update_category] | |
5 | + | |
6 | + def index | |
7 | + @followed_people = profile.followed_profiles.order(:type) | |
8 | + @profile_types = {_('All profiles') => nil}.merge(Circle.profile_types).to_a | |
9 | + | |
10 | + if params['filter'].present? | |
11 | + @followed_people = @followed_people.where(:type => params['filter']) | |
12 | + @active_filter = params['filter'] | |
13 | + end | |
14 | + | |
15 | + @followed_people = @followed_people.paginate(:per_page => 15, :page => params[:npage]) | |
16 | + end | |
17 | + | |
18 | + def set_category_modal | |
19 | + followed_profile = Profile.find(params[:followed_profile_id]) | |
20 | + circles = Circle.where(:person => profile, :profile_type => followed_profile.class.name) | |
21 | + render :partial => 'followers/edit_circles_modal', :locals => { :circles => circles, :followed_profile => followed_profile } | |
22 | + end | |
23 | + | |
24 | + def update_category | |
25 | + followed_profile = Profile.find_by(:id => params["followed_profile_id"]) | |
26 | + | |
27 | + selected_circles = params[:circles].map{ |circle_name, circle_id| Circle.find_by(:id => circle_id) }.select{ |c| c.present? } | |
28 | + | |
29 | + if followed_profile | |
30 | + profile.update_profile_circles(followed_profile, selected_circles) | |
31 | + render :text => _("Circles of %s updated successfully") % followed_profile.name, :status => 200 | |
32 | + else | |
33 | + render :text => _("Error: No profile to follow."), :status => 400 | |
34 | + end | |
35 | + end | |
36 | + | |
37 | + protected | |
38 | + | |
39 | + def only_for_person | |
40 | + render_not_found unless profile.person? | |
41 | + end | |
42 | + | |
43 | +end | ... | ... |
app/controllers/public/account_controller.rb
... | ... | @@ -205,7 +205,7 @@ class AccountController < ApplicationController |
205 | 205 | if params[:value].blank? |
206 | 206 | @change_password.errors[:base] << _('Can not recover user password with blank value.') |
207 | 207 | else |
208 | - @change_password.errors[:base] << _('Could not find any user with %s equal to "%s".') % [fields_label, params[:value]] | |
208 | + @change_password.errors[:base] << _('Could not find any user with %s equal to "%s".').html_safe % [fields_label, params[:value]] | |
209 | 209 | end |
210 | 210 | rescue ActiveRecord::RecordInvalid |
211 | 211 | @change_password.errors[:base] << _('Could not perform password recovery for the user.') | ... | ... |
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -128,9 +128,9 @@ class ContentViewerController < ApplicationController |
128 | 128 | end |
129 | 129 | |
130 | 130 | unless @page.display_to?(user) |
131 | - if !profile.visible? || profile.secret? || (user && user.follows?(profile)) || user.blank? | |
131 | + if !profile.visible? || profile.secret? || (user && profile.in_social_circle?(user)) || user.blank? | |
132 | 132 | render_access_denied |
133 | - else #!profile.public? | |
133 | + else | |
134 | 134 | private_profile_partial_parameters |
135 | 135 | render :template => 'profile/_private_profile', :status => 403, :formats => [:html] |
136 | 136 | end | ... | ... |
app/controllers/public/profile_controller.rb
... | ... | @@ -3,7 +3,9 @@ class ProfileController < PublicController |
3 | 3 | needs_profile |
4 | 4 | before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add] |
5 | 5 | before_filter :store_location, :only => [:join, :join_not_logged, :report_abuse, :send_mail] |
6 | - before_filter :login_required, :only => [:add, :join, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity, :send_mail] | |
6 | + before_filter :login_required, :only => [:add, :join, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity, :send_mail, :follow, :unfollow] | |
7 | + before_filter :allow_followers?, :only => [:follow, :unfollow] | |
8 | + before_filter :accept_only_post, :only => [:follow, :unfollow] | |
7 | 9 | |
8 | 10 | helper TagsHelper |
9 | 11 | helper ActionTrackerHelper |
... | ... | @@ -42,8 +44,8 @@ class ProfileController < PublicController |
42 | 44 | feed_writer = FeedWriter.new |
43 | 45 | data = feed_writer.write( |
44 | 46 | tagged, |
45 | - :title => _("%s's contents tagged with \"%s\"") % [profile.name, @tag], | |
46 | - :description => _("%s's contents tagged with \"%s\"") % [profile.name, @tag], | |
47 | + :title => _("%s's contents tagged with \"%s\"").html_safe % [profile.name, @tag], | |
48 | + :description => _("%s's contents tagged with \"%s\"").html_safe % [profile.name, @tag], | |
47 | 49 | :link => url_for(profile.url) |
48 | 50 | ) |
49 | 51 | render :text => data, :content_type => "text/xml" |
... | ... | @@ -65,6 +67,14 @@ class ProfileController < PublicController |
65 | 67 | end |
66 | 68 | end |
67 | 69 | |
70 | + def following | |
71 | + @followed_people = profile.followed_profiles.paginate(:per_page => per_page, :page => params[:npage], :total_entries => profile.followed_profiles.count) | |
72 | + end | |
73 | + | |
74 | + def followed | |
75 | + @followed_by = profile.followers.paginate(:per_page => per_page, :page => params[:npage], :total_entries => profile.followers.count) | |
76 | + end | |
77 | + | |
68 | 78 | def members |
69 | 79 | if is_cache_expired?(profile.members_cache_key(params)) |
70 | 80 | sort = (params[:sort] == 'desc') ? params[:sort] : 'asc' |
... | ... | @@ -88,7 +98,7 @@ class ProfileController < PublicController |
88 | 98 | |
89 | 99 | def join_modal |
90 | 100 | profile.add_member(user) |
91 | - session[:notice] = _('%s administrator still needs to accept you as member.') % profile.name | |
101 | + session[:notice] = _('%s administrator still needs to accept you as member.').html_safe % profile.name | |
92 | 102 | redirect_to :action => :index |
93 | 103 | end |
94 | 104 | |
... | ... | @@ -98,12 +108,12 @@ class ProfileController < PublicController |
98 | 108 | |
99 | 109 | profile.add_member(user) |
100 | 110 | if !profile.members.include?(user) |
101 | - render :text => {:message => _('%s administrator still needs to accept you as member.') % profile.name}.to_json | |
111 | + render :text => {:message => _('%s administrator still needs to accept you as member.').html_safe % profile.name}.to_json | |
102 | 112 | else |
103 | - render :text => {:message => _('You just became a member of %s.') % profile.name}.to_json | |
113 | + render :text => {:message => _('You just became a member of %s.').html_safe % profile.name}.to_json | |
104 | 114 | end |
105 | 115 | else |
106 | - render :text => {:message => _('You are already a member of %s.') % profile.name}.to_json | |
116 | + render :text => {:message => _('You are already a member of %s.').html_safe % profile.name}.to_json | |
107 | 117 | end |
108 | 118 | end |
109 | 119 | |
... | ... | @@ -125,7 +135,7 @@ class ProfileController < PublicController |
125 | 135 | render :text => current_person.leave(profile, params[:reload]) |
126 | 136 | end |
127 | 137 | else |
128 | - render :text => {:message => _('You are not a member of %s.') % profile.name}.to_json | |
138 | + render :text => {:message => _('You are not a member of %s.').html_safe % profile.name}.to_json | |
129 | 139 | end |
130 | 140 | end |
131 | 141 | |
... | ... | @@ -145,12 +155,41 @@ class ProfileController < PublicController |
145 | 155 | # FIXME this shouldn't be in Person model? |
146 | 156 | if !user.memberships.include?(profile) |
147 | 157 | AddFriend.create!(:person => user, :friend => profile) |
148 | - render :text => _('%s still needs to accept being your friend.') % profile.name | |
158 | + render :text => _('%s still needs to accept being your friend.').html_safe % profile.name | |
149 | 159 | else |
150 | - render :text => _('You are already a friend of %s.') % profile.name | |
160 | + render :text => _('You are already a friend of %s.').html_safe % profile.name | |
161 | + end | |
162 | + end | |
163 | + | |
164 | + def follow | |
165 | + if profile.followed_by?(current_person) | |
166 | + render :text => _("You are already following %s.") % profile.name, :status => 400 | |
167 | + else | |
168 | + selected_circles = params[:circles].map{ |circle_name, circle_id| Circle.find_by(:id => circle_id) }.select{ |c| c.present? } | |
169 | + if selected_circles.present? | |
170 | + current_person.follow(profile, selected_circles) | |
171 | + render :text => _("You are now following %s") % profile.name, :status => 200 | |
172 | + else | |
173 | + render :text => _("Select at least one circle to follow %s.") % profile.name, :status => 400 | |
174 | + end | |
151 | 175 | end |
152 | 176 | end |
153 | 177 | |
178 | + def find_profile_circles | |
179 | + circles = Circle.where(:person => current_person, :profile_type => profile.class.name) | |
180 | + render :partial => 'blocks/profile_info_actions/circles', :locals => { :circles => circles, :profile_types => Circle.profile_types.to_a } | |
181 | + end | |
182 | + | |
183 | + def unfollow | |
184 | + follower = params[:follower_id].present? ? Person.find_by(id: params[:follower_id]) : current_person | |
185 | + | |
186 | + if follower && follower.follows?(profile) | |
187 | + follower.unfollow(profile) | |
188 | + end | |
189 | + redirect_url = params["redirect_to"] ? params["redirect_to"] : profile.url | |
190 | + redirect_to redirect_url | |
191 | + end | |
192 | + | |
154 | 193 | def check_friendship |
155 | 194 | unless logged_in? |
156 | 195 | render :text => '' |
... | ... | @@ -178,7 +217,7 @@ class ProfileController < PublicController |
178 | 217 | def unblock |
179 | 218 | if current_user.person.is_admin?(profile.environment) |
180 | 219 | profile.unblock |
181 | - session[:notice] = _("You have unblocked %s successfully. ") % profile.name | |
220 | + session[:notice] = _("You have unblocked %s successfully. ").html_safe % profile.name | |
182 | 221 | redirect_to :controller => 'profile', :action => 'index' |
183 | 222 | else |
184 | 223 | message = _('You are not allowed to unblock enterprises in this environment.') |
... | ... | @@ -437,4 +476,8 @@ class ProfileController < PublicController |
437 | 476 | [:image, :domains, :preferred_domain, :environment] |
438 | 477 | end |
439 | 478 | |
479 | + def allow_followers? | |
480 | + render_not_found unless profile.allow_followers? | |
481 | + end | |
482 | + | |
440 | 483 | end | ... | ... |
app/helpers/action_tracker_helper.rb
... | ... | @@ -14,13 +14,23 @@ module ActionTrackerHelper |
14 | 14 | } |
15 | 15 | end |
16 | 16 | |
17 | + def new_follower_description ta | |
18 | + n_('has 1 new follower:<br />%{name}', 'has %{num} new followers:<br />%{name}', ta.get_follower_name.size).html_safe % { | |
19 | + num: ta.get_follower_name.size, | |
20 | + name: safe_join(ta.collect_group_with_index(:follower_name) do |n,i| | |
21 | + link_to image_tag(ta.get_follower_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/person-icon.png")), | |
22 | + ta.get_follower_url[i], title: n | |
23 | + end) | |
24 | + } | |
25 | + end | |
26 | + | |
17 | 27 | def join_community_description ta |
18 | - n_('has joined 1 community:<br />%{name}'.html_safe, 'has joined %{num} communities:<br />%{name}'.html_safe, ta.get_resource_name.size) % { | |
28 | + n_('has joined 1 community:<br />%{name}', 'has joined %{num} communities:<br />%{name}', ta.get_resource_name.size).html_safe % { | |
19 | 29 | num: ta.get_resource_name.size, |
20 | - name: ta.collect_group_with_index(:resource_name) do |n,i| | |
30 | + name: safe_join(ta.collect_group_with_index(:resource_name) do |n,i| | |
21 | 31 | link = link_to image_tag(ta.get_resource_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/community-icon.png")), |
22 | 32 | ta.get_resource_url[i], title: n |
23 | - end.join.html_safe | |
33 | + end) | |
24 | 34 | } |
25 | 35 | end |
26 | 36 | |
... | ... | @@ -68,9 +78,9 @@ module ActionTrackerHelper |
68 | 78 | end |
69 | 79 | |
70 | 80 | def favorite_enterprise_description ta |
71 | - _('favorited enterprise %{title}') % { | |
81 | + (_('favorited enterprise %{title}') % { | |
72 | 82 | title: link_to(truncate(ta.get_enterprise_name), ta.get_enterprise_url), |
73 | - } | |
83 | + }).html_safe | |
74 | 84 | end |
75 | 85 | |
76 | 86 | end | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -880,7 +880,7 @@ module ApplicationHelper |
880 | 880 | link_to_all = link_to(content_tag('strong', _('See all')), :controller => 'memberships', :profile => user.identifier) |
881 | 881 | end |
882 | 882 | link = list.map do |element| |
883 | - link_to(content_tag('strong', _('<span>Manage</span> %s') % element.short_name(25)), element.admin_url, :class => "icon-menu-"+element.class.identification.underscore, :title => _('Manage %s') % element.short_name) | |
883 | + link_to(content_tag('strong', _('<span>Manage</span> %s').html_safe % element.short_name(25)), element.admin_url, :class => "icon-menu-"+element.class.identification.underscore, :title => _('Manage %s').html_safe % element.short_name) | |
884 | 884 | end |
885 | 885 | if link_to_all |
886 | 886 | link << link_to_all |
... | ... | @@ -921,7 +921,7 @@ module ApplicationHelper |
921 | 921 | logout_link = link_to(logout_icon.html_safe, { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) |
922 | 922 | join_result = safe_join( |
923 | 923 | [welcome_span.html_safe, render_environment_features(:usermenu).html_safe, admin_link.html_safe, |
924 | - manage_enterprises.html_safe, manage_communities.html_safe, ctrl_panel_link.html_safe, | |
924 | + manage_enterprises, manage_communities, ctrl_panel_link.html_safe, | |
925 | 925 | pending_tasks_count.html_safe, logout_link.html_safe], "") |
926 | 926 | join_result |
927 | 927 | end | ... | ... |
app/helpers/forms_helper.rb
... | ... | @@ -128,14 +128,14 @@ module FormsHelper |
128 | 128 | counter += 1 |
129 | 129 | row << item |
130 | 130 | if counter % per_row == 0 |
131 | - rows << content_tag('tr', row.join("\n")) | |
131 | + rows << content_tag('tr', row.join("\n").html_safe) | |
132 | 132 | counter = 0 |
133 | 133 | row = [] |
134 | 134 | end |
135 | 135 | end |
136 | - rows << content_tag('tr', row.join("\n")) | |
136 | + rows << content_tag('tr', row.join("\n").html_safe) | |
137 | 137 | |
138 | - content_tag('table',rows.join("\n")) | |
138 | + content_tag('table',rows.join("\n").html_safe) | |
139 | 139 | end |
140 | 140 | |
141 | 141 | def date_field(name, value, datepicker_options = {}, html_options = {}) | ... | ... |
app/helpers/profile_helper.rb
... | ... | @@ -11,7 +11,7 @@ module ProfileHelper |
11 | 11 | PERSON_CATEGORIES[:location] = [:address, :address_reference, :zip_code, :city, :state, :district, :country, :nationality] |
12 | 12 | PERSON_CATEGORIES[:work] = [:organization, :organization_website, :professional_activity] |
13 | 13 | PERSON_CATEGORIES[:study] = [:schooling, :formation, :area_of_study] |
14 | - PERSON_CATEGORIES[:network] = [:friends, :communities, :enterprises] | |
14 | + PERSON_CATEGORIES[:network] = [:friends, :followers, :followed_profiles, :communities, :enterprises] | |
15 | 15 | PERSON_CATEGORIES.merge!(COMMON_CATEGORIES) |
16 | 16 | |
17 | 17 | ORGANIZATION_CATEGORIES = {} |
... | ... | @@ -42,7 +42,8 @@ module ProfileHelper |
42 | 42 | :created_at => _('Profile created at'), |
43 | 43 | :members_count => _('Members'), |
44 | 44 | :privacy_setting => _('Privacy setting'), |
45 | - :article_tags => _('Tags') | |
45 | + :article_tags => _('Tags'), | |
46 | + :followed_profiles => _('Following') | |
46 | 47 | } |
47 | 48 | |
48 | 49 | EXCEPTION = { |
... | ... | @@ -144,6 +145,14 @@ module ProfileHelper |
144 | 145 | link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url) |
145 | 146 | end |
146 | 147 | |
148 | + def treat_followers(followers) | |
149 | + link_to(profile.followers.count, {:action=>"followed", :controller=>"profile", :profile=>"#{profile.identifier}"}) | |
150 | + end | |
151 | + | |
152 | + def treat_followed_profiles(followed_profiles) | |
153 | + link_to(profile.followed_profiles.count, {:action=>"following", :controller=>"profile", :profile=>"#{profile.identifier}"}) | |
154 | + end | |
155 | + | |
147 | 156 | def treat_events(events) |
148 | 157 | link_to events.published.count, :controller => 'events', :action => 'events' |
149 | 158 | end | ... | ... |
app/jobs/notify_activity_to_profiles_job.rb
... | ... | @@ -19,8 +19,8 @@ class NotifyActivityToProfilesJob < Struct.new(:tracked_action_id) |
19 | 19 | # Notify the user |
20 | 20 | ActionTrackerNotification.create(:profile_id => tracked_action.user.id, :action_tracker_id => tracked_action.id) |
21 | 21 | |
22 | - # Notify all friends | |
23 | - ActionTrackerNotification.connection.execute("insert into action_tracker_notifications(profile_id, action_tracker_id) select f.friend_id, #{tracked_action.id} from friendships as f where person_id=#{tracked_action.user.id} and f.friend_id not in (select atn.profile_id from action_tracker_notifications as atn where atn.action_tracker_id = #{tracked_action.id})") | |
22 | + # Notify all followers | |
23 | + ActionTrackerNotification.connection.execute("INSERT INTO action_tracker_notifications(profile_id, action_tracker_id) SELECT DISTINCT c.person_id, #{tracked_action.id} FROM profiles_circles AS p JOIN circles as c ON c.id = p.circle_id WHERE p.profile_id = #{tracked_action.user.id} AND (c.person_id NOT IN (SELECT atn.profile_id FROM action_tracker_notifications AS atn WHERE atn.action_tracker_id = #{tracked_action.id}))") | |
24 | 24 | |
25 | 25 | if tracked_action.user.is_a? Organization |
26 | 26 | ActionTrackerNotification.connection.execute "insert into action_tracker_notifications(profile_id, action_tracker_id) " + | ... | ... |
app/mailers/contact.rb
... | ... | @@ -47,8 +47,8 @@ class Contact |
47 | 47 | content_type: 'text/html', |
48 | 48 | to: contact.dest.notification_emails, |
49 | 49 | reply_to: contact.email, |
50 | - subject: "[#{contact.dest.short_name(30)}] " + contact.subject, | |
51 | - from: "#{contact.name} <#{contact.dest.environment.noreply_email}>" | |
50 | + subject: "[#{contact.dest.short_name(30)}] #{contact.subject}".html_safe, | |
51 | + from: "#{contact.name} <#{contact.dest.environment.noreply_email}>".html_safe | |
52 | 52 | } |
53 | 53 | |
54 | 54 | if contact.sender | ... | ... |
app/mailers/environment_mailing.rb
app/mailers/mailing.rb
... | ... | @@ -2,7 +2,8 @@ require_dependency 'mailing_job' |
2 | 2 | |
3 | 3 | class Mailing < ApplicationRecord |
4 | 4 | |
5 | - acts_as_having_settings :field => :data | |
5 | + extend ActsAsHavingSettings::ClassMethods | |
6 | + acts_as_having_settings field: :data | |
6 | 7 | |
7 | 8 | attr_accessible :subject, :body, :data |
8 | 9 | |
... | ... | @@ -23,11 +24,11 @@ class Mailing < ApplicationRecord |
23 | 24 | end |
24 | 25 | |
25 | 26 | def generate_from |
26 | - "#{source.name} <#{if source.is_a? Environment then source.noreply_email else source.contact_email end}>" | |
27 | + "#{source.name} <#{if source.is_a? Environment then source.noreply_email else source.contact_email end}>".html_safe | |
27 | 28 | end |
28 | 29 | |
29 | 30 | def generate_subject |
30 | - '[%s] %s' % [source.name, subject] | |
31 | + '[%s] %s'.html_safe % [source.name, subject] | |
31 | 32 | end |
32 | 33 | |
33 | 34 | def signature_message | ... | ... |
app/mailers/organization_mailing.rb
app/mailers/pending_task_notifier.rb
... | ... | @@ -12,8 +12,8 @@ class PendingTaskNotifier < ApplicationMailer |
12 | 12 | |
13 | 13 | mail( |
14 | 14 | to: person.email, |
15 | - from: "#{person.environment.name} <#{person.environment.noreply_email}>", | |
16 | - subject: _("[%s] Pending tasks") % person.environment.name | |
15 | + from: "#{person.environment.name} <#{person.environment.noreply_email}>".html_safe, | |
16 | + subject: _("[%s] Pending tasks").html_safe % person.environment.name | |
17 | 17 | ) |
18 | 18 | end |
19 | 19 | ... | ... |
app/mailers/scrap_notifier.rb
... | ... | @@ -14,8 +14,8 @@ class ScrapNotifier < ApplicationMailer |
14 | 14 | @url = sender.environment.top_url |
15 | 15 | mail( |
16 | 16 | to: receiver.email, |
17 | - from: "#{sender.environment.name} <#{sender.environment.noreply_email}>", | |
18 | - subject: _("[%s] You received a scrap!") % [sender.environment.name] | |
17 | + from: "#{sender.environment.name} <#{sender.environment.noreply_email}>".html_safe, | |
18 | + subject: _("[%s] You received a scrap!").html_safe % [sender.environment.name] | |
19 | 19 | ) |
20 | 20 | end |
21 | 21 | end | ... | ... |
app/mailers/task_mailer.rb
... | ... | @@ -14,7 +14,7 @@ class TaskMailer < ApplicationMailer |
14 | 14 | mail( |
15 | 15 | to: task.target.notification_emails.compact, |
16 | 16 | from: self.class.generate_from(task), |
17 | - subject: "[%s] %s" % [task.environment.name, task.target_notification_description] | |
17 | + subject: "[%s] %s".html_safe % [task.environment.name, task.target_notification_description] | |
18 | 18 | ) |
19 | 19 | end |
20 | 20 | |
... | ... | @@ -27,7 +27,7 @@ class TaskMailer < ApplicationMailer |
27 | 27 | mail( |
28 | 28 | to: task.friend_email, |
29 | 29 | from: self.class.generate_from(task), |
30 | - subject: '[%s] %s' % [ task.requestor.environment.name, task.target_notification_description ] | |
30 | + subject: '[%s] %s'.html_safe % [ task.requestor.environment.name, task.target_notification_description ] | |
31 | 31 | ) |
32 | 32 | end |
33 | 33 | |
... | ... | @@ -43,7 +43,7 @@ class TaskMailer < ApplicationMailer |
43 | 43 | mail_with_template( |
44 | 44 | to: task.requestor.notification_emails, |
45 | 45 | from: self.class.generate_from(task), |
46 | - subject: '[%s] %s' % [task.requestor.environment.name, task.target_notification_description], | |
46 | + subject: '[%s] %s'.html_safe % [task.requestor.environment.name, task.target_notification_description], | |
47 | 47 | email_template: task.email_template, |
48 | 48 | template_params: {:environment => task.requestor.environment, :task => task, :message => @message, :url => @url, :requestor => task.requestor} |
49 | 49 | ) | ... | ... |
app/mailers/user_mailer.rb
... | ... | @@ -13,8 +13,8 @@ class UserMailer < ApplicationMailer |
13 | 13 | |
14 | 14 | mail( |
15 | 15 | to: user_email, |
16 | - from: "#{user.environment.name} <#{user.environment.contact_email}>", | |
17 | - subject: _("[%{environment}] Welcome to %{environment} mail!") % { :environment => user.environment.name } | |
16 | + from: "#{user.environment.name} <#{user.environment.contact_email}>".html_safe, | |
17 | + subject: _("[%{environment}] Welcome to %{environment} mail!").html_safe % { :environment => user.environment.name } | |
18 | 18 | ) |
19 | 19 | end |
20 | 20 | |
... | ... | @@ -30,7 +30,7 @@ class UserMailer < ApplicationMailer |
30 | 30 | mail_with_template( |
31 | 31 | from: "#{user.environment.name} <#{user.environment.contact_email}>", |
32 | 32 | to: user.email, |
33 | - subject: _("[%s] Activate your account") % [user.environment.name], | |
33 | + subject: _("[%s] Activate your account").html_safe % [user.environment.name], | |
34 | 34 | template_params: {:environment => user.environment, :activation_code => @activation_code, :redirection => @redirection, :join => @join, :person => user.person, :url => @url}, |
35 | 35 | email_template: user.environment.email_templates.find_by_template_type(:user_activation), |
36 | 36 | ) |
... | ... | @@ -44,8 +44,8 @@ class UserMailer < ApplicationMailer |
44 | 44 | mail( |
45 | 45 | content_type: 'text/html', |
46 | 46 | to: user.email, |
47 | - from: "#{user.environment.name} <#{user.environment.contact_email}>", | |
48 | - subject: email_subject.blank? ? _("Welcome to environment %s") % [user.environment.name] : email_subject, | |
47 | + from: "#{user.environment.name} <#{user.environment.contact_email}>".html_safe, | |
48 | + subject: email_subject.blank? ? _("Welcome to environment %s").html_safe % [user.environment.name] : email_subject, | |
49 | 49 | body: @body |
50 | 50 | ) |
51 | 51 | end |
... | ... | @@ -63,8 +63,8 @@ class UserMailer < ApplicationMailer |
63 | 63 | mail( |
64 | 64 | content_type: 'text/html', |
65 | 65 | to: user.email, |
66 | - from: "#{user.environment.name} <#{user.environment.contact_email}>", | |
67 | - subject: _("[%s] What about grow up your network?") % user.environment.name | |
66 | + from: "#{user.environment.name} <#{user.environment.contact_email}>".html_safe, | |
67 | + subject: _("[%s] What about grow up your network?").html_safe % user.environment.name | |
68 | 68 | ) |
69 | 69 | end |
70 | 70 | ... | ... |
app/models/abuse_complaint.rb
... | ... | @@ -25,7 +25,7 @@ class AbuseComplaint < Task |
25 | 25 | end |
26 | 26 | |
27 | 27 | def title |
28 | - abuse_reports.count > 1 ? (_('Abuse complaint (%s)') % abuse_reports.count) :_('Abuse complaint') | |
28 | + abuse_reports.count > 1 ? (_('Abuse complaint (%s)').html_safe % abuse_reports.count) :_('Abuse complaint') | |
29 | 29 | end |
30 | 30 | |
31 | 31 | def linked_subject |
... | ... | @@ -57,15 +57,15 @@ class AbuseComplaint < Task |
57 | 57 | end |
58 | 58 | |
59 | 59 | def task_activated_message |
60 | - _('Your profile was reported by the users of %s due to inappropriate behavior. The administrators of the environment are now reviewing the report. To solve this misunderstanding, please contact the administrators.') % environment.name | |
60 | + _('Your profile was reported by the users of %s due to inappropriate behavior. The administrators of the environment are now reviewing the report. To solve this misunderstanding, please contact the administrators.').html_safe % environment.name | |
61 | 61 | end |
62 | 62 | |
63 | 63 | def task_finished_message |
64 | - _('Your profile was disabled by the administrators of %s due to inappropriate behavior. To solve this misunderstanding please contact them.') % environment.name | |
64 | + _('Your profile was disabled by the administrators of %s due to inappropriate behavior. To solve this misunderstanding please contact them.').html_safe % environment.name | |
65 | 65 | end |
66 | 66 | |
67 | 67 | def target_notification_description |
68 | - _('%s was reported due to inappropriate behavior.') % reported.name | |
68 | + _('%s was reported due to inappropriate behavior.').html_safe % reported.name | |
69 | 69 | end |
70 | 70 | |
71 | 71 | def target_notification_message | ... | ... |
app/models/add_member.rb
... | ... | @@ -22,6 +22,7 @@ class AddMember < Task |
22 | 22 | self.roles = [Profile::Roles.member(organization.environment.id).id] |
23 | 23 | end |
24 | 24 | target.affiliate(requestor, self.roles.select{|r| !r.to_i.zero? }.map{|i| Role.find(i)}) |
25 | + person.follow(organization, Circle.find_or_create_by(:person => person, :name =>_('memberships'), :profile_type => 'Community')) | |
25 | 26 | end |
26 | 27 | |
27 | 28 | def title |
... | ... | @@ -56,7 +57,7 @@ class AddMember < Task |
56 | 57 | def target_notification_description |
57 | 58 | requestor_email = " (#{requestor.email})" if requestor.may_display_field_to?("email") |
58 | 59 | |
59 | - _("%{requestor}%{requestor_email} wants to be a member of '%{organization}'.") % {:requestor => requestor.name, :requestor_email => requestor_email, :organization => organization.name} | |
60 | + _("%{requestor}%{requestor_email} wants to be a member of '%{organization}'.").html_safe % {:requestor => requestor.name, :requestor_email => requestor_email, :organization => organization.name} | |
60 | 61 | end |
61 | 62 | |
62 | 63 | def target_notification_message | ... | ... |
app/models/article.rb
... | ... | @@ -13,7 +13,9 @@ class Article < ApplicationRecord |
13 | 13 | :image_builder, :show_to_followers, :archived, |
14 | 14 | :author, :display_preview, :published_at, :person_followers |
15 | 15 | |
16 | + extend ActsAsHavingImage::ClassMethods | |
16 | 17 | acts_as_having_image |
18 | + | |
17 | 19 | include Noosfero::Plugin::HotSpot |
18 | 20 | |
19 | 21 | SEARCHABLE_FIELDS = { |
... | ... | @@ -91,7 +93,8 @@ class Article < ApplicationRecord |
91 | 93 | has_many :article_categorizations_including_virtual, :class_name => 'ArticleCategorization' |
92 | 94 | has_many :categories_including_virtual, :through => :article_categorizations_including_virtual, :source => :category |
93 | 95 | |
94 | - acts_as_having_settings :field => :setting | |
96 | + extend ActsAsHavingSettings::ClassMethods | |
97 | + acts_as_having_settings field: :setting | |
95 | 98 | |
96 | 99 | settings_items :display_hits, :type => :boolean, :default => true |
97 | 100 | settings_items :author_name, :type => :string, :default => "" |
... | ... | @@ -242,6 +245,7 @@ class Article < ApplicationRecord |
242 | 245 | acts_as_taggable |
243 | 246 | N_('Tag list') |
244 | 247 | |
248 | + extend ActsAsFilesystem::ActsMethods | |
245 | 249 | acts_as_filesystem |
246 | 250 | |
247 | 251 | acts_as_versioned |
... | ... | @@ -534,13 +538,13 @@ class Article < ApplicationRecord |
534 | 538 | |
535 | 539 | scope :display_filter, lambda {|user, profile| |
536 | 540 | return published if (user.nil? && profile && profile.public?) |
537 | - return [] if user.nil? || (profile && !profile.public? && !user.follows?(profile)) | |
541 | + return [] if user.nil? || (profile && !profile.public? && !profile.in_social_circle?(user)) | |
538 | 542 | where( |
539 | 543 | [ |
540 | 544 | "published = ? OR last_changed_by_id = ? OR profile_id = ? OR ? |
541 | 545 | OR (show_to_followers = ? AND ? AND profile_id IN (?))", true, user.id, user.id, |
542 | 546 | profile.nil? ? false : user.has_permission?(:view_private_content, profile), |
543 | - true, (profile.nil? ? true : user.follows?(profile)), ( profile.nil? ? (user.friends.select('profiles.id')) : [profile.id]) | |
547 | + true, (profile.nil? ? true : profile.in_social_circle?(user)), ( profile.nil? ? (user.friends.select('profiles.id')) : [profile.id]) | |
544 | 548 | ] |
545 | 549 | ) |
546 | 550 | } | ... | ... |
app/models/block.rb
... | ... | @@ -17,6 +17,7 @@ class Block < ApplicationRecord |
17 | 17 | belongs_to :mirror_block, :class_name => "Block" |
18 | 18 | has_many :observers, :class_name => "Block", :foreign_key => "mirror_block_id" |
19 | 19 | |
20 | + extend ActsAsHavingSettings::ClassMethods | |
20 | 21 | acts_as_having_settings |
21 | 22 | |
22 | 23 | scope :enabled, -> { where :enabled => true } |
... | ... | @@ -88,7 +89,7 @@ class Block < ApplicationRecord |
88 | 89 | end |
89 | 90 | |
90 | 91 | def display_to_user?(user) |
91 | - display_user == 'all' || (user.nil? && display_user == 'not_logged') || (user && display_user == 'logged') || (user && display_user == 'followers' && user.follows?(owner)) | |
92 | + display_user == 'all' || (user.nil? && display_user == 'not_logged') || (user && display_user == 'logged') || (user && display_user == 'followers' && owner.in_social_circle?(user)) | |
92 | 93 | end |
93 | 94 | |
94 | 95 | def display_always(context) | ... | ... |
app/models/blog.rb
app/models/category.rb
... | ... | @@ -21,6 +21,7 @@ class Category < ApplicationRecord |
21 | 21 | |
22 | 22 | scope :on_level, -> parent { where :parent_id => parent } |
23 | 23 | |
24 | + extend ActsAsFilesystem::ActsMethods | |
24 | 25 | acts_as_filesystem |
25 | 26 | |
26 | 27 | has_many :article_categorizations |
... | ... | @@ -35,6 +36,7 @@ class Category < ApplicationRecord |
35 | 36 | has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person' |
36 | 37 | has_many :communities, :through => :profile_categorizations, :source => :profile, :class_name => 'Community' |
37 | 38 | |
39 | + extend ActsAsHavingImage::ClassMethods | |
38 | 40 | acts_as_having_image |
39 | 41 | |
40 | 42 | before_save :normalize_display_color | ... | ... |
... | ... | @@ -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
app/models/community.rb
... | ... | @@ -2,7 +2,7 @@ class Community < Organization |
2 | 2 | |
3 | 3 | attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, :resource_type |
4 | 4 | attr_accessible :address_reference, :district, :tag_list, :language, :description |
5 | - attr_accessible :requires_email | |
5 | + | |
6 | 6 | after_destroy :check_invite_member_for_destroy |
7 | 7 | |
8 | 8 | def self.type_name |
... | ... | @@ -13,9 +13,6 @@ class Community < Organization |
13 | 13 | N_('Language') |
14 | 14 | |
15 | 15 | settings_items :language |
16 | - settings_items :requires_email, :type => :boolean | |
17 | - | |
18 | - alias_method :requires_email?, :requires_email | |
19 | 16 | |
20 | 17 | extend SetProfileRegionFromCityState::ClassMethods |
21 | 18 | set_profile_region_from_city_state | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -0,0 +1,55 @@ |
1 | +module DelayedAttachmentFu | |
2 | + | |
3 | + module ClassMethods | |
4 | + def delay_attachment_fu_thumbnails | |
5 | + include DelayedAttachmentFu::InstanceMethods | |
6 | + after_create do |file| | |
7 | + if file.thumbnailable? | |
8 | + Delayed::Job.enqueue CreateThumbnailsJob.new(file.class.name, file.id) | |
9 | + end | |
10 | + end | |
11 | + end | |
12 | + end | |
13 | + | |
14 | + module InstanceMethods | |
15 | + # skip processing with RMagick | |
16 | + def process_attachment | |
17 | + end | |
18 | + | |
19 | + def after_process_attachment | |
20 | + save_to_storage | |
21 | + @temp_paths.clear | |
22 | + @saved_attachment = nil | |
23 | + run_callbacks :after_attachment_saved | |
24 | + end | |
25 | + | |
26 | + def create_thumbnails | |
27 | + if thumbnailable? | |
28 | + self.class.with_image(full_filename) do |img| | |
29 | + self.width = img.columns | |
30 | + self.height = img.rows | |
31 | + self.save! | |
32 | + end | |
33 | + self.class.attachment_options[:thumbnails].each do |suffix, size| | |
34 | + self.create_or_update_thumbnail(self.full_filename, suffix, size) | |
35 | + end | |
36 | + self.thumbnails_processed = true | |
37 | + self.save! | |
38 | + end | |
39 | + end | |
40 | + | |
41 | + def public_filename(size=nil) | |
42 | + force, size = true, nil if size == :uploaded | |
43 | + if !self.thumbnailable? || self.thumbnails_processed || force | |
44 | + super size | |
45 | + else | |
46 | + size ||= :thumb | |
47 | + '/images/icons-app/image-loading-%s.png' % size | |
48 | + end | |
49 | + end | |
50 | + | |
51 | + | |
52 | + end | |
53 | +end | |
54 | + | |
55 | + | ... | ... |
app/models/concerns/set_profile_region_from_city_state.rb
0 → 100644
... | ... | @@ -0,0 +1,44 @@ |
1 | +module SetProfileRegionFromCityState | |
2 | + | |
3 | + module ClassMethods | |
4 | + def set_profile_region_from_city_state | |
5 | + before_save :region_from_city_and_state | |
6 | + | |
7 | + include InstanceMethods | |
8 | + alias_method_chain :city=, :region | |
9 | + alias_method_chain :state=, :region | |
10 | + end | |
11 | + end | |
12 | + | |
13 | + module InstanceMethods | |
14 | + include Noosfero::Plugin::HotSpot | |
15 | + | |
16 | + def city_with_region=(value) | |
17 | + self.city_without_region = value | |
18 | + @change_region = true | |
19 | + end | |
20 | + | |
21 | + def state_with_region=(value) | |
22 | + self.state_without_region = value | |
23 | + @change_region = true | |
24 | + end | |
25 | + | |
26 | + def region_from_city_and_state | |
27 | + if @change_region | |
28 | + self.region = nil | |
29 | + state = search_region(State, self.state) | |
30 | + self.region = search_region(City.where(:parent_id => state.id), self.city) if state | |
31 | + end | |
32 | + end | |
33 | + | |
34 | + private | |
35 | + | |
36 | + def search_region(scope, query) | |
37 | + return nil if !query | |
38 | + query = query.downcase.strip | |
39 | + scope.where(['lower(name)=? OR lower(abbreviation)=? OR lower(acronym)=?', query, query, query]).first | |
40 | + end | |
41 | + | |
42 | + end | |
43 | + | |
44 | +end | ... | ... |
... | ... | @@ -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 < Task |
12 | 12 | attr_accessible :environment, :requestor, :target |
13 | 13 | attr_accessible :reject_explanation, :template_id |
14 | 14 | |
15 | + extend ActsAsHavingImage::ClassMethods | |
15 | 16 | acts_as_having_image |
16 | 17 | |
17 | 18 | DATA_FIELDS = Community.fields + ['name', 'closed', 'description'] | ... | ... |
app/models/enterprise.rb
app/models/environment.rb
... | ... | @@ -200,6 +200,7 @@ class Environment < ApplicationRecord |
200 | 200 | # Relationships and applied behaviour |
201 | 201 | # ################################################# |
202 | 202 | |
203 | + extend ActsAsHavingBoxes::ClassMethods | |
203 | 204 | acts_as_having_boxes |
204 | 205 | |
205 | 206 | after_create do |env| |
... | ... | @@ -251,7 +252,8 @@ class Environment < ApplicationRecord |
251 | 252 | # ################################################# |
252 | 253 | |
253 | 254 | # store the Environment settings as YAML-serialized Hash. |
254 | - acts_as_having_settings :field => :settings | |
255 | + extend ActsAsHavingSettings::ClassMethods | |
256 | + acts_as_having_settings field: :settings | |
255 | 257 | |
256 | 258 | # introduce and explain to users something about the signup |
257 | 259 | settings_items :signup_intro, :type => String | ... | ... |
app/models/event.rb
1 | -require 'noosfero/translatable_content' | |
2 | 1 | require 'builder' |
3 | 2 | |
4 | 3 | class Event < Article |
... | ... | @@ -138,7 +137,7 @@ class Event < Article |
138 | 137 | false |
139 | 138 | end |
140 | 139 | |
141 | - include Noosfero::TranslatableContent | |
140 | + include TranslatableContent | |
142 | 141 | include MaybeAddHttp |
143 | 142 | |
144 | 143 | end | ... | ... |
app/models/favorite_enterprise_person.rb
... | ... | @@ -7,6 +7,10 @@ class FavoriteEnterprisePerson < ApplicationRecord |
7 | 7 | belongs_to :enterprise |
8 | 8 | belongs_to :person |
9 | 9 | |
10 | + after_create do |favorite| | |
11 | + favorite.person.follow(favorite.enterprise, Circle.find_or_create_by(:person => favorite.person, :name =>_('favorites'), :profile_type => 'Enterprise')) | |
12 | + end | |
13 | + | |
10 | 14 | protected |
11 | 15 | |
12 | 16 | def is_trackable? | ... | ... |
app/models/folder.rb
... | ... | @@ -10,7 +10,8 @@ class Folder < Article |
10 | 10 | errors.add(:parent, "A folder should not belong to a blog.") if parent && parent.blog? |
11 | 11 | end |
12 | 12 | |
13 | - acts_as_having_settings :field => :setting | |
13 | + extend ActsAsHavingSettings::ClassMethods | |
14 | + acts_as_having_settings field: :setting | |
14 | 15 | |
15 | 16 | xss_terminate :only => [ :name, :body ], :with => 'white_list', :on => 'validation' |
16 | 17 | ... | ... |
app/models/forum.rb
app/models/friendship.rb
... | ... | @@ -9,11 +9,19 @@ class Friendship < ApplicationRecord |
9 | 9 | after_create do |friendship| |
10 | 10 | Friendship.update_cache_counter(:friends_count, friendship.person, 1) |
11 | 11 | Friendship.update_cache_counter(:friends_count, friendship.friend, 1) |
12 | + | |
13 | + circles = friendship.group.blank? ? ['friendships'] : friendship.group.split(',').map(&:strip) | |
14 | + circles.each do |circle| | |
15 | + friendship.person.follow(friendship.friend, Circle.find_or_create_by(:person => friendship.person, :name => circle, :profile_type => 'Person')) | |
16 | + end | |
12 | 17 | end |
13 | 18 | |
14 | 19 | after_destroy do |friendship| |
15 | 20 | Friendship.update_cache_counter(:friends_count, friendship.person, -1) |
16 | 21 | Friendship.update_cache_counter(:friends_count, friendship.friend, -1) |
22 | + | |
23 | + circle = Circle.find_by(:person => friendship.person, :name => (friendship.group.blank? ? 'friendships': friendship.group) ) | |
24 | + friendship.person.remove_profile_from_circle(friendship.friend, circle) if circle | |
17 | 25 | end |
18 | 26 | |
19 | 27 | def self.remove_friendship(person1, person2) | ... | ... |
app/models/image.rb
... | ... | @@ -23,6 +23,7 @@ class Image < ApplicationRecord |
23 | 23 | |
24 | 24 | validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n |
25 | 25 | |
26 | + extend DelayedAttachmentFu::ClassMethods | |
26 | 27 | delay_attachment_fu_thumbnails |
27 | 28 | |
28 | 29 | postgresql_attachment_fu | ... | ... |
app/models/organization.rb
... | ... | @@ -2,6 +2,10 @@ |
2 | 2 | class Organization < Profile |
3 | 3 | |
4 | 4 | attr_accessible :moderated_articles, :foundation_year, :contact_person, :acronym, :legal_form, :economic_activity, :management_information, :cnpj, :display_name, :enable_contact_us |
5 | + attr_accessible :requires_email | |
6 | + | |
7 | + settings_items :requires_email, type: :boolean | |
8 | + alias_method :requires_email?, :requires_email | |
5 | 9 | |
6 | 10 | SEARCH_FILTERS = { |
7 | 11 | :order => %w[more_recent more_popular more_active], | ... | ... |
app/models/person.rb
... | ... | @@ -8,7 +8,6 @@ class Person < Profile |
8 | 8 | :display => %w[compact] |
9 | 9 | } |
10 | 10 | |
11 | - | |
12 | 11 | def self.type_name |
13 | 12 | _('Person') |
14 | 13 | end |
... | ... | @@ -93,6 +92,7 @@ class Person < Profile |
93 | 92 | has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article |
94 | 93 | has_many :friendships, :dependent => :destroy |
95 | 94 | has_many :friends, :class_name => 'Person', :through => :friendships |
95 | + has_many :circles | |
96 | 96 | |
97 | 97 | scope :online, -> { |
98 | 98 | joins(:user).where("users.chat_status != '' AND users.chat_status_at >= ?", DateTime.now - User.expires_chat_status_every.minutes) |
... | ... | @@ -200,6 +200,33 @@ class Person < Profile |
200 | 200 | end |
201 | 201 | end |
202 | 202 | |
203 | + def follow(profile, circles) | |
204 | + circles = [circles] unless circles.is_a?(Array) | |
205 | + circles.each do |new_circle| | |
206 | + ProfileFollower.create(profile: profile, circle: new_circle) | |
207 | + end | |
208 | + end | |
209 | + | |
210 | + def update_profile_circles(profile, new_circles) | |
211 | + profile_circles = ProfileFollower.with_profile(profile).with_follower(self).map(&:circle) | |
212 | + circles_to_add = new_circles - profile_circles | |
213 | + circles_to_remove = profile_circles - new_circles | |
214 | + circles_to_add.each do |new_circle| | |
215 | + ProfileFollower.create(profile: profile, circle: new_circle) | |
216 | + end | |
217 | + | |
218 | + ProfileFollower.where('circle_id IN (?) AND profile_id = ?', | |
219 | + circles_to_remove.map(&:id), profile.id).destroy_all | |
220 | + end | |
221 | + | |
222 | + def unfollow(profile) | |
223 | + ProfileFollower.with_follower(self).with_profile(profile).destroy_all | |
224 | + end | |
225 | + | |
226 | + def remove_profile_from_circle(profile, circle) | |
227 | + ProfileFollower.with_profile(profile).with_circle(circle).destroy_all | |
228 | + end | |
229 | + | |
203 | 230 | def already_request_friendship?(person) |
204 | 231 | person.tasks.where(requestor_id: self.id, type: 'AddFriend', status: Task::Status::ACTIVE).first |
205 | 232 | end |
... | ... | @@ -580,9 +607,12 @@ class Person < Profile |
580 | 607 | person.has_permission?(:manage_friends, self) |
581 | 608 | end |
582 | 609 | |
583 | - protected | |
610 | + def followed_profiles | |
611 | + Profile.followed_by self | |
612 | + end | |
584 | 613 | |
585 | - def followed_by?(profile) | |
586 | - self == profile || self.is_a_friend?(profile) | |
614 | + def in_social_circle?(person) | |
615 | + self.is_a_friend?(person) || super | |
587 | 616 | end |
617 | + | |
588 | 618 | end | ... | ... |
app/models/profile.rb
... | ... | @@ -5,7 +5,7 @@ class Profile < ApplicationRecord |
5 | 5 | |
6 | 6 | attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time, |
7 | 7 | :redirection_after_login, :custom_url_redirection, |
8 | - :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification | |
8 | + :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification, :allow_followers | |
9 | 9 | |
10 | 10 | # use for internationalizable human type names in search facets |
11 | 11 | # reimplement on subclasses |
... | ... | @@ -88,6 +88,8 @@ class Profile < ApplicationRecord |
88 | 88 | } |
89 | 89 | |
90 | 90 | acts_as_accessible |
91 | + | |
92 | + include Customizable | |
91 | 93 | acts_as_customizable |
92 | 94 | |
93 | 95 | include Noosfero::Plugin::HotSpot |
... | ... | @@ -185,6 +187,21 @@ class Profile < ApplicationRecord |
185 | 187 | Person.members_of(self).by_role(roles) |
186 | 188 | end |
187 | 189 | |
190 | + extend ActsAsHavingSettings::ClassMethods | |
191 | + acts_as_having_settings field: :data | |
192 | + | |
193 | + def settings | |
194 | + data | |
195 | + end | |
196 | + | |
197 | + settings_items :redirect_l10n, :type => :boolean, :default => false | |
198 | + settings_items :public_content, :type => :boolean, :default => true | |
199 | + settings_items :description | |
200 | + settings_items :fields_privacy, :type => :hash, :default => {} | |
201 | + settings_items :email_suggestions, :type => :boolean, :default => false | |
202 | + settings_items :profile_admin_mail_notification, :type => :boolean, :default => true | |
203 | + | |
204 | + extend ActsAsHavingBoxes::ClassMethods | |
188 | 205 | acts_as_having_boxes |
189 | 206 | |
190 | 207 | acts_as_taggable |
... | ... | @@ -203,6 +220,23 @@ class Profile < ApplicationRecord |
203 | 220 | scope :more_active, -> { order 'activities_count DESC' } |
204 | 221 | scope :more_recent, -> { order "created_at DESC" } |
205 | 222 | |
223 | + scope :followed_by, -> person{ | |
224 | + distinct.select('profiles.*'). | |
225 | + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id'). | |
226 | + joins('left join circles ON circles.id = profiles_circles.circle_id'). | |
227 | + where('circles.person_id = ?', person.id) | |
228 | + } | |
229 | + | |
230 | + scope :in_circle, -> circle{ | |
231 | + distinct.select('profiles.*'). | |
232 | + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id'). | |
233 | + joins('left join circles ON circles.id = profiles_circles.circle_id'). | |
234 | + where('circles.id = ?', circle.id) | |
235 | + } | |
236 | + | |
237 | + settings_items :allow_followers, :type => :boolean, :default => true | |
238 | + alias_method :allow_followers?, :allow_followers | |
239 | + | |
206 | 240 | acts_as_trackable :dependent => :destroy |
207 | 241 | |
208 | 242 | has_many :profile_activities |
... | ... | @@ -215,6 +249,9 @@ class Profile < ApplicationRecord |
215 | 249 | |
216 | 250 | has_many :email_templates, :foreign_key => :owner_id |
217 | 251 | |
252 | + has_many :profile_followers | |
253 | + has_many :followers, -> { uniq }, :class_name => 'Person', :through => :profile_followers, :source => :person | |
254 | + | |
218 | 255 | # Although this should be a has_one relation, there are no non-silly names for |
219 | 256 | # a foreign key on article to reference the template to which it is |
220 | 257 | # welcome_page... =P |
... | ... | @@ -231,19 +268,6 @@ class Profile < ApplicationRecord |
231 | 268 | scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap) |
232 | 269 | end |
233 | 270 | |
234 | - acts_as_having_settings :field => :data | |
235 | - | |
236 | - def settings | |
237 | - data | |
238 | - end | |
239 | - | |
240 | - settings_items :redirect_l10n, :type => :boolean, :default => false | |
241 | - settings_items :public_content, :type => :boolean, :default => true | |
242 | - settings_items :description | |
243 | - settings_items :fields_privacy, :type => :hash, :default => {} | |
244 | - settings_items :email_suggestions, :type => :boolean, :default => false | |
245 | - settings_items :profile_admin_mail_notification, :type => :boolean, :default => true | |
246 | - | |
247 | 271 | validates_length_of :description, :maximum => 550, :allow_nil => true |
248 | 272 | |
249 | 273 | # Valid identifiers must match this format. |
... | ... | @@ -285,6 +309,7 @@ class Profile < ApplicationRecord |
285 | 309 | |
286 | 310 | has_many :files, :class_name => 'UploadedFile' |
287 | 311 | |
312 | + extend ActsAsHavingImage::ClassMethods | |
288 | 313 | acts_as_having_image |
289 | 314 | |
290 | 315 | has_many :tasks, :dependent => :destroy, :as => 'target' |
... | ... | @@ -758,12 +783,13 @@ private :generate_url, :url_options |
758 | 783 | |
759 | 784 | # Adds a person as member of this Profile. |
760 | 785 | def add_member(person, attributes={}) |
761 | - if self.has_members? | |
786 | + if self.has_members? && !self.secret | |
762 | 787 | if self.closed? && members.count > 0 |
763 | 788 | AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person) |
764 | 789 | else |
765 | 790 | self.affiliate(person, Profile::Roles.admin(environment.id), attributes) if members.count == 0 |
766 | 791 | self.affiliate(person, Profile::Roles.member(environment.id), attributes) |
792 | + person.follow(self, Circle.find_or_create_by(:person => person, :name =>_('memberships'), :profile_type => 'Community')) | |
767 | 793 | end |
768 | 794 | person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel } |
769 | 795 | remove_from_suggestion_list person |
... | ... | @@ -1107,7 +1133,11 @@ private :generate_url, :url_options |
1107 | 1133 | end |
1108 | 1134 | |
1109 | 1135 | def followed_by?(person) |
1110 | - person.is_member_of?(self) | |
1136 | + (person == self) || (person.in? self.followers) | |
1137 | + end | |
1138 | + | |
1139 | + def in_social_circle?(person) | |
1140 | + (person == self) || (person.is_member_of?(self)) | |
1111 | 1141 | end |
1112 | 1142 | |
1113 | 1143 | def display_private_info_to?(user) |
... | ... | @@ -1148,4 +1178,8 @@ private :generate_url, :url_options |
1148 | 1178 | def allow_destroy?(person = nil) |
1149 | 1179 | person.kind_of?(Profile) && person.has_permission?('destroy_profile', self) |
1150 | 1180 | end |
1181 | + | |
1182 | + def in_circle?(circle, follower) | |
1183 | + ProfileFollower.with_follower(follower).with_circle(circle).with_profile(self).present? | |
1184 | + end | |
1151 | 1185 | end | ... | ... |
... | ... | @@ -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 < ApplicationRecord |
17 | 17 | self.class.generate_profile_suggestions(profile_suggestion.person) |
18 | 18 | end |
19 | 19 | |
20 | - acts_as_having_settings :field => :categories | |
20 | + extend ActsAsHavingSettings::ClassMethods | |
21 | + acts_as_having_settings field: :categories | |
21 | 22 | |
22 | 23 | validate :must_be_a_valid_category, :on => :create |
23 | 24 | def must_be_a_valid_category | ... | ... |
app/models/task.rb
... | ... | @@ -11,7 +11,8 @@ |
11 | 11 | # will need to declare <ttserialize</tt> itself). |
12 | 12 | class Task < ApplicationRecord |
13 | 13 | |
14 | - acts_as_having_settings :field => :data | |
14 | + extend ActsAsHavingSettings::ClassMethods | |
15 | + acts_as_having_settings field: :data | |
15 | 16 | |
16 | 17 | module Status |
17 | 18 | # the status of tasks just created | ... | ... |
app/models/text_article.rb
1 | -require 'noosfero/translatable_content' | |
2 | - | |
3 | 1 | # a base class for all text article types. |
4 | 2 | class TextArticle < Article |
5 | 3 | |
... | ... | @@ -9,7 +7,7 @@ class TextArticle < Article |
9 | 7 | _('Article') |
10 | 8 | end |
11 | 9 | |
12 | - include Noosfero::TranslatableContent | |
10 | + include TranslatableContent | |
13 | 11 | |
14 | 12 | def self.icon_name(article = nil) |
15 | 13 | if article && !article.parent.nil? && article.parent.kind_of?(Blog) | ... | ... |
app/models/tiny_mce_article.rb
app/models/uploaded_file.rb
... | ... | @@ -84,6 +84,7 @@ class UploadedFile < Article |
84 | 84 | |
85 | 85 | validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of %{size}").sub('%{size}', self.max_size.to_humanreadable).fix_i18n |
86 | 86 | |
87 | + extend DelayedAttachmentFu::ClassMethods | |
87 | 88 | delay_attachment_fu_thumbnails |
88 | 89 | |
89 | 90 | postgresql_attachment_fu | ... | ... |
app/views/account/activation_question.html.erb
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | var answer = parseInt(form.answer.value); |
4 | 4 | var val = form.answer.value; |
5 | 5 | if (!answer || (val.length != 4) || val > <%= Time.now.year %> || val < 1900) { |
6 | - alert(<%= (_('The year must be between %d and %d') % [1900, Time.now.year]).inspect %>); | |
6 | + alert(<%= (_('The year must be between %d and %d').html_safe % [1900, Time.now.year]).inspect %>); | |
7 | 7 | return false; |
8 | 8 | } else { |
9 | 9 | return true; |
... | ... | @@ -28,9 +28,9 @@ |
28 | 28 | |
29 | 29 | <p> <strong><%= _('Pay atention! You have only one chance!') %></strong> </p> |
30 | 30 | |
31 | - <p><%= _("This is a question to know if you really are part of this enterprise. Pay atention because you have only one chance to answer right and activate your enterprise. If you answer wrong you will not be able to activate the enterprise automaticaly and must get in touch with the admins of %s by email or phone.") % environment.name %> </p> | |
31 | + <p><%= _("This is a question to know if you really are part of this enterprise. Pay atention because you have only one chance to answer right and activate your enterprise. If you answer wrong you will not be able to activate the enterprise automaticaly and must get in touch with the admins of %s by email or phone.").html_safe % environment.name %> </p> | |
32 | 32 | |
33 | - <%= ApplicationHelper::NoosferoFormBuilder::output_field(@question == :foundation_year ? (_("What year your enterprise was founded? It must have 4 digits, eg 1990. %s") % environment.tip_message_enterprise_activation_question) : _('What is the CNPJ of your enterprise?'), text_field_tag(:answer, nil, :id => 'enterprise-activation-answer')) %> | |
33 | + <%= ApplicationHelper::NoosferoFormBuilder::output_field(@question == :foundation_year ? (_("What year your enterprise was founded? It must have 4 digits, eg 1990. %s").html_safe % environment.tip_message_enterprise_activation_question) : _('What is the CNPJ of your enterprise?'), text_field_tag(:answer, nil, :id => 'enterprise-activation-answer')) %> | |
34 | 34 | |
35 | 35 | <%= hidden_field_tag :enterprise_code, params[:enterprise_code] %> |
36 | 36 | ... | ... |
app/views/admin_panel/_signup_welcome_text.html.erb
1 | 1 | <div class='description'> |
2 | 2 | <%= _('This text will be sent to new users if the feature "Send welcome e-mail to new users" is enabled on environment.') %><br/><br/> |
3 | - <%= _('Including %s on body, it will be replaced by the real name of the e-mail recipient.') % content_tag('code', '{user_name}') %> | |
3 | + <%= _('Including %s on body, it will be replaced by the real name of the e-mail recipient.').html_safe % content_tag('code', '{user_name}') %> | |
4 | 4 | </div> |
5 | 5 | |
6 | 6 | <%= labelled_form_field(_('Subject'), text_field(:environment, :signup_welcome_text_subject, :style => 'width:100%')) %> | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +<div class="circles" id='circles-list'> | |
2 | + | |
3 | + <%= form_for :circles, :url => {:controller => 'profile', :action => 'follow'}, :html => {:id => "follow-circles-form"} do |f|%> | |
4 | + <%= render partial: "blocks/profile_info_actions/select_circles", :locals => {:circles => circles, :followed_profile => profile, :follower => current_person } %> | |
5 | + | |
6 | + <div id="circle-actions"> | |
7 | + <%= submit_button :ok, _("Follow") %> | |
8 | + <input type="button" value="<%= _("Cancel") %>" id="cancel-set-circle" class="button with-text icon-cancel"/> | |
9 | + </div> | |
10 | + <% end %> | |
11 | +</div> | ... | ... |
app/views/blocks/profile_info_actions/_common.html.erb
1 | 1 | <li><%= report_abuse(profile, :button) %></li> |
2 | +<% if logged_in? && (user != profile) && profile.allow_followers? %> | |
3 | + <li> | |
4 | + <% follow = user.follows?(profile) %> | |
5 | + <%= button(:unfollow, content_tag('span', _('Unfollow')), {:profile => profile.identifier, :controller => 'profile', :action => 'unfollow'}, :method => :post, :id => 'action-unfollow', :title => _("Unfollow"), :style => follow ? "" : "display: none;") %> | |
6 | + <%= button(:ok, content_tag('span', _('Follow')), {:profile => profile.identifier, :controller => 'profile', :action => 'find_profile_circles'}, :id => 'action-follow', :title => _("Follow"), :style => follow ? "display: none;" : "") %> | |
7 | + <div id="circles-container" style="display: none;"> | |
8 | + </div> | |
9 | + </li> | |
10 | +<% end %> | |
2 | 11 | <%= render_environment_features(:profile_actions) %> | ... | ... |
app/views/blocks/profile_info_actions/_select_circles.html.erb
0 → 100644
... | ... | @@ -0,0 +1,22 @@ |
1 | +<div class="circles" id='circles-list'> | |
2 | + <p><%= _("Select the circles for %s") % followed_profile.name %></p> | |
3 | + <div id="circles-checkboxes"> | |
4 | + <% circles.each do |circle| %> | |
5 | + <div class="circle"> | |
6 | + <%= labelled_check_box circle.name, "circles[#{circle.name}]", circle.id, followed_profile.in_circle?(circle, follower) %> | |
7 | + </div> | |
8 | + <% end %> | |
9 | + </div> | |
10 | + | |
11 | + <%= button(:add, _('New Circle'), '#', :id => "new-circle") %> | |
12 | + | |
13 | + <div id="new-circle-form" style="display: none;"> | |
14 | + <%= labelled_text_field _('Circle name') , 'circle[name]', "",:id => 'text-field-name-new-circle'%> | |
15 | + <%= hidden_field_tag('circle[profile_type]', followed_profile.class.name) %> | |
16 | + | |
17 | + <%= button_bar do %> | |
18 | + <%= button(:save, _('Create'), {:profile => follower.identifier, :controller => 'circles', :action => 'xhr_create'}, :id => "new-circle-submit") %> | |
19 | + <%= button(:cancel, _('Cancel'), '#', :id => "new-circle-cancel") %> | |
20 | + <% end %> | |
21 | + </div> | |
22 | +</div> | ... | ... |
... | ... | @@ -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 %> | ... | ... |
... | ... | @@ -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/cms/_blog.html.erb
... | ... | @@ -58,7 +58,7 @@ |
58 | 58 | <div id="blog-image-builder"> |
59 | 59 | <%= f.fields_for :image_builder, @article.image do |i| %> |
60 | 60 | <%= file_field_or_thumbnail(_('Cover image:'), @article.image, i)%> |
61 | - <%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %> | |
61 | + <%= _("Max size: %s (.jpg, .gif, .png)").html_safe % Image.max_size.to_humanreadable %> | |
62 | 62 | <% end %> |
63 | 63 | </div> |
64 | 64 | ... | ... |
app/views/enterprise_validation/view_processed.html.erb
app/views/favorite_enterprises/add.html.erb
1 | -<h1><%= _('Adding %s as a favorite enterprise') % @favorite_enterprise.name %></h1> | |
1 | +<h1><%= _('Adding %s as a favorite enterprise').html_safe % @favorite_enterprise.name %></h1> | |
2 | 2 | |
3 | 3 | <p> |
4 | -<%= _('Are you sure you want to add %s as your favorite enterprise?') % @favorite_enterprise.name %> | |
4 | +<%= _('Are you sure you want to add %s as your favorite enterprise?').html_safe % @favorite_enterprise.name %> | |
5 | 5 | </p> |
6 | 6 | |
7 | 7 | <%= form_tag do %> |
8 | 8 | <%= hidden_field_tag(:confirmation, 1) %> |
9 | 9 | |
10 | - <%= submit_button(:ok, _("Yes, I am sure"), :title => _("I want to add %s as a favorite enterprise") % @favorite_enterprise.name) %> | |
10 | + <%= submit_button(:ok, _("Yes, I am sure"), :title => _("I want to add %s as a favorite enterprise").html_safe % @favorite_enterprise.name) %> | |
11 | 11 | <%= button(:cancel, _("No, I don't want"), :action => 'index') %> |
12 | 12 | <% end %> | ... | ... |
app/views/file_presenter/_image.html.erb
... | ... | @@ -4,15 +4,15 @@ |
4 | 4 | current_index = images.index(image.encapsulated_file) |
5 | 5 | total_of_images = images.count |
6 | 6 | link_to_previous = if current_index >= 1 |
7 | - link_to(_('« Previous'), images[current_index - 1].view_url, :class => 'previous') | |
7 | + link_to(_('« Previous').html_safe, images[current_index - 1].view_url, :class => 'previous') | |
8 | 8 | else |
9 | - content_tag('span', _('« Previous'), :class => 'previous') | |
9 | + content_tag('span', _('« Previous').html_safe, :class => 'previous') | |
10 | 10 | end |
11 | 11 | |
12 | 12 | link_to_next = if current_index < total_of_images - 1 |
13 | - link_to(_('Next »'), images[current_index + 1].view_url, :class => 'next') | |
13 | + link_to(_('Next »').html_safe, images[current_index + 1].view_url, :class => 'next') | |
14 | 14 | else |
15 | - content_tag('span', _('Next »'), :class => 'next') | |
15 | + content_tag('span', _('Next »').html_safe, :class => 'next') | |
16 | 16 | end |
17 | 17 | %> |
18 | 18 | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +<div id="manage_followed people"> | |
2 | + | |
3 | +<h1><%= _("%s is following") % profile.name %></h1> | |
4 | + | |
5 | +<% cache_timeout(profile.manage_friends_cache_key(params), 4.hours) do %> | |
6 | + <% if @followed_people.empty? %> | |
7 | + <p> | |
8 | + <em> | |
9 | + <%= _("You don't follow anybody yet.") %> | |
10 | + </em> | |
11 | + </p> | |
12 | + <% end %> | |
13 | + | |
14 | + <%= button_bar do %> | |
15 | + <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> | |
16 | + <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %> | |
17 | + <% end %> | |
18 | + | |
19 | + <%= labelled_select(_('Profile type')+': ', :filter_profile_type, :last, :first, @active_filter, @profile_types, :id => "profile-type-filter") %> | |
20 | + | |
21 | + <%= render :partial => 'profile_list', :locals => { :profiles => @followed_people } %> | |
22 | + | |
23 | + <br style="clear:both" /> | |
24 | + <%= pagination_links @followed_people, :param_name => 'npage' %> | |
25 | +<% end %> | |
26 | + | |
27 | +</div> | ... | ... |
app/views/friends/remove.html.erb
1 | 1 | <div id="remove_friend"> |
2 | 2 | |
3 | -<h1><%= _('Removing friend: %s') % @friend.name %></h1> | |
3 | +<h1><%= _('Removing friend: %s').html_safe % @friend.name %></h1> | |
4 | 4 | |
5 | 5 | <%= profile_image @friend, :thumb, :class => 'friend_picture' %> |
6 | 6 | |
7 | 7 | <p> |
8 | -<%= _('Are you sure you want to remove %s from your friends list?') % @friend.name %> | |
8 | +<%= _('Are you sure you want to remove %s from your friends list?').html_safe % @friend.name %> | |
9 | 9 | </p> |
10 | 10 | |
11 | 11 | <p> |
12 | 12 | <em> |
13 | -<%= _('Note that %s will still have you as a friend, unless he/she also wants to remove you from his/her friend list.') % @friend.name %> | |
13 | +<%= _('Note that %s will still have you as a friend, unless he/she also wants to remove you from his/her friend list.').html_safe % @friend.name %> | |
14 | 14 | </em> |
15 | 15 | </p> |
16 | 16 | ... | ... |
app/views/home/welcome.html.erb
1 | 1 | <% default_message = defined?(default_message) ? default_message : false %> |
2 | 2 | |
3 | 3 | <div id='thanks-for-signing'> |
4 | - <h1><%= _("Welcome to %s!") % environment.name %></h1> | |
4 | + <h1><%= _("Welcome to %s!").html_safe % environment.name %></h1> | |
5 | 5 | <% if environment.has_custom_welcome_screen? && !default_message %> |
6 | 6 | <%= environment.settings[:signup_welcome_screen_body].html_safe %> |
7 | 7 | <% else %> |
... | ... | @@ -10,21 +10,21 @@ |
10 | 10 | <p><%= _("Firstly, some tips for getting started:") %></p> |
11 | 11 | <h4><%= _("Confirm your account!") %></h4> |
12 | 12 | <p><%= _("You should receive a welcome email from us shortly. Please take a second to follow the link within to confirm your account.") %></p> |
13 | - <p><%= _("You won't appear as %s until your account is confirmed.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p> | |
13 | + <p><%= _("You won't appear as %s until your account is confirmed.").html_safe % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p> | |
14 | 14 | <% else %> |
15 | 15 | <h4><%= _("Wait for admin approvement!") %></h4> |
16 | 16 | <p><%= _("The administrators will evaluate your signup request for approvement.") %></p> |
17 | - <p><%= _("You won't appear as %s until your account is approved.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p> | |
17 | + <p><%= _("You won't appear as %s until your account is approved.").html_safe % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p> | |
18 | 18 | <% end %> |
19 | 19 | <h4><%= _("What to do next?") %></h4> |
20 | - <p><%= _("Access your %s and see your face on the network!") % | |
20 | + <p><%= _("Access your %s and see your face on the network!").html_safe % | |
21 | 21 | (user.present? ? link_to(_('Profile'), {:controller => 'profile', :profile => user.identifier}, :target => '_blank') : 'Profile') %> |
22 | - <%= _("You can also explore your %s to customize your profile. Here are some %s on what you can do there.") % | |
22 | + <%= _("You can also explore your %s to customize your profile. Here are some %s on what you can do there.").html_safe % | |
23 | 23 | [user.present? ? link_to(_('Control Panel'), {:controller => 'profile_editor', :profile => user.identifier}, :target => '_blank') : 'Control Panel', |
24 | 24 | link_to(_('tips'), {:controller => 'doc', :action => 'topic', :section => 'user', :topic => 'editing-person-info'}, :target => '_blank')] %></p> |
25 | - <p><%= _("%s your Gmail, Yahoo and Hotmail contacts!") % link_to(_('Invite and find'), {:controller => 'doc', :action => 'topic', :section => 'user', :topic => 'invite-contacts'}, :target => '_blank') %></p> | |
26 | - <p><%= _("Learn the guidelines. Read the %s for more details on how to use this social network!") % link_to(_('Documentation'), {:controller => 'doc'}, :target => '_blank') %></p> | |
25 | + <p><%= _("%s your Gmail, Yahoo and Hotmail contacts!").html_safe % link_to(_('Invite and find'), {:controller => 'doc', :action => 'topic', :section => 'user', :topic => 'invite-contacts'}, :target => '_blank') %></p> | |
26 | + <p><%= _("Learn the guidelines. Read the %s for more details on how to use this social network!").html_safe % link_to(_('Documentation'), {:controller => 'doc'}, :target => '_blank') %></p> | |
27 | 27 | <p><%= _("Start exploring and have fun!") %></p> |
28 | 28 | <% end %> |
29 | - <%= render :partial => 'shared/template_welcome_page', :locals => {:template => @person_template, :header => _("What can I do as a %s?")} %> | |
29 | + <%= render :partial => 'shared/template_welcome_page', :locals => {:template => @person_template, :header => _("What can I do as a %s?").html_safe} %> | |
30 | 30 | </div> | ... | ... |
app/views/person_notifier/mailer/_create_article.html.erb
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | <td> |
6 | 6 | <p> |
7 | 7 | <span style="font-size: 14px;"><%= link_to activity.user.short_name(20), activity.user.url %></span> |
8 | - <span style="font-size: 14px;"><%= _("has published on community %s") % link_to(activity.target.profile.short_name(20), activity.target.profile.url, :style => "color: #333; font-weight: bold; text-decoration: none;") if activity.target.profile.is_a?(Community) %></span> | |
8 | + <span style="font-size: 14px;"><%= _("has published on community %s").html_safe % link_to(activity.target.profile.short_name(20), activity.target.profile.url, :style => "color: #333; font-weight: bold; text-decoration: none;") if activity.target.profile.is_a?(Community) %></span> | |
9 | 9 | <span style="font-size: 10px; color: #929292; float:right;"><%= time_ago_in_words(activity.created_at) %></span> |
10 | 10 | </p> |
11 | 11 | <p> | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +<%= render :partial => 'default_activity', :locals => { :activity => activity } %> | ... | ... |
app/views/person_notifier/mailer/_profile_comments.html.erb
1 | 1 | <% if activity.comments_count > 2 %> |
2 | 2 | <div style="font-size: 10px;"> |
3 | 3 | <% if activity.params['url'].blank? %> |
4 | - <%= _("%s comments") % activity.comments_count %> | |
4 | + <%= _("%s comments").html_safe % activity.comments_count %> | |
5 | 5 | <% else %> |
6 | - <%= link_to(_("View all %s comments") % activity.comments_count, activity.params['url']) %> | |
6 | + <%= link_to(_("View all %s comments").html_safe % activity.comments_count, activity.params['url']) %> | |
7 | 7 | <% end %> |
8 | 8 | </div> |
9 | 9 | <% else %> | ... | ... |
app/views/person_notifier/mailer/content_summary.html.erb
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | <%= link_to @url, :style => "text-decoration: none;" do %> |
6 | 6 | <span style="font-weight:bold;font-size: 28px;margin: 0;color: white;background-color: #AAAAAA;padding: 5px;"><%= @environment.name %></span> |
7 | 7 | <% end %> |
8 | - <span style="font-weight:bold;color: #333;font-size:19px;margin-left: 8px;"><%= _("%s's Notifications") % @profile.name %></h3> | |
8 | + <span style="font-weight:bold;color: #333;font-size:19px;margin-left: 8px;"><%= _("%s's Notifications").html_safe % @profile.name %></h3> | |
9 | 9 | </div> |
10 | 10 | <div style="margin: 0 20px 20px 20px;border-top:1px solid #e2e2e2;"> |
11 | 11 | <% if @tasks.present? %> |
... | ... | @@ -34,7 +34,7 @@ |
34 | 34 | |
35 | 35 | <div style="color:#444444;font-size:11px;margin-bottom: 20px;"> |
36 | 36 | <p style="margin:0"><%= _("Greetings,") %></p> |
37 | - <p style="margin:0"><%= _('%s team.') % @environment.name %></p> | |
37 | + <p style="margin:0"><%= _('%s team.').html_safe % @environment.name %></p> | |
38 | 38 | <p style="margin:0"><%= link_to @url, url_for(@url) %></p> |
39 | 39 | </div> |
40 | 40 | </div> | ... | ... |
... | ... | @@ -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 %> | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +<%= render :partial => 'default_activity', :locals => { :activity => activity, :tab_action => tab_action } %> | ... | ... |
app/views/profile/_private_profile.html.erb
... | ... | @@ -13,5 +13,5 @@ |
13 | 13 | <%= button(:add, content_tag('span', _('Add friend')), profile.add_url, :class => 'add-friend', :title => _("Add friend"), :style => 'position: relative;') %> |
14 | 14 | <% end %> |
15 | 15 | <%= button :back, _('Go back'), :back %> |
16 | - <%= button :home, _("Go to %s home page") % environment.name, :controller => 'home' %> | |
16 | + <%= button :home, _("Go to %s home page").html_safe % environment.name, :controller => 'home' %> | |
17 | 17 | <% end %> | ... | ... |
app/views/profile/_profile_comments.html.erb
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | <% if activity.comments_count > 0 %> |
6 | 6 | <div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments" > |
7 | 7 | <div class='view-all-comments icon-chat'> |
8 | - <%= link_to(n_('View comment', "View all %s comments", activity.comments_count) % activity.comments_count, :profile => profile.identifier, :controller => 'profile', :action => 'more_comments', :activity => activity, :comment_page => (1)) %> | |
8 | + <%= link_to(n_('View comment', "View all %s comments".html_safe, activity.comments_count) % activity.comments_count, :profile => profile.identifier, :controller => 'profile', :action => 'more_comments', :activity => activity, :comment_page => (1)) %> | |
9 | 9 | </div> |
10 | 10 | </div> |
11 | 11 | <% end %> | ... | ... |
app/views/profile/_profile_scraps.html.erb
... | ... | @@ -23,7 +23,7 @@ |
23 | 23 | <% if scrap.replies.count > 0 %> |
24 | 24 | <div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments"> |
25 | 25 | <div class='view-all-comments icon-chat'> |
26 | - <%= link_to(n_('View comment', "View all %s comments", scrap.replies.count) % scrap.replies.count, :profile => profile.identifier, :controller => 'profile', :action => 'more_replies', :activity => activity, :comment_page => (1)) %> | |
26 | + <%= link_to(n_('View comment', "View all %s comments".html_safe, scrap.replies.count) % scrap.replies.count, :profile => profile.identifier, :controller => 'profile', :action => 'more_replies', :activity => activity, :comment_page => (1)) %> | |
27 | 27 | </div> |
28 | 28 | </div> |
29 | 29 | <% end %> | ... | ... |
app/views/profile_editor/edit.html.erb
1 | -<h1><%= _('Profile settings for %s') % profile.name %></h1> | |
1 | +<h1><%= _('Profile settings for %s').html_safe % profile.name %></h1> | |
2 | 2 | |
3 | 3 | <%= error_messages_for :profile_data %> |
4 | 4 | |
... | ... | @@ -18,14 +18,16 @@ |
18 | 18 | </div> |
19 | 19 | <div id="profile_change_picture"> |
20 | 20 | <%= f.fields_for :image_builder, @profile.image do |i| %> |
21 | - <%= file_field_or_thumbnail(_('Image:'), @profile.image, i) %><%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %> | |
21 | + <%= file_field_or_thumbnail(_('Image:'), @profile.image, i) %><%= _("Max size: %s (.jpg, .gif, .png)").html_safe % Image.max_size.to_humanreadable %> | |
22 | 22 | <% end %> |
23 | 23 | </div> |
24 | 24 | |
25 | 25 | <h2><%= _('Privacy options') %></h2> |
26 | - | |
26 | + <div> | |
27 | + <%= labelled_check_box _("Allow other users to follow me"), 'profile_data[allow_followers]', true, @profile.allow_followers?, :class => "person-can-be-followed" %> | |
28 | + </div> | |
27 | 29 | <% if profile.person? %> |
28 | - <div> | |
30 | + <div id="profile_allow_follows"> | |
29 | 31 | <%= labelled_radio_button _('Public — show my contents to all internet users').html_safe, 'profile_data[public_profile]', true, @profile.public_profile? %> |
30 | 32 | </div> |
31 | 33 | <div> | ... | ... |
app/views/profile_editor/index.html.erb
... | ... | @@ -72,6 +72,11 @@ |
72 | 72 | |
73 | 73 | <%= control_panel_button(_('Email Templates'), 'email-templates', :controller => :profile_email_templates) if profile.organization? %> |
74 | 74 | |
75 | + <% if profile.person? %> | |
76 | + <%= control_panel_button(_('Manage followed profiles'), 'edit-profile', :controller => :followers) %> | |
77 | + <%= control_panel_button(_('Manage circles'), 'edit-profile-group', :controller => :circles) %> | |
78 | + <% end %> | |
79 | + | |
75 | 80 | <% @plugins.dispatch(:control_panel_buttons).each do |button| %> |
76 | 81 | <%= control_panel_button(button[:title], button[:icon], button[:url], button[:html_options]) %> |
77 | 82 | <% end %> | ... | ... |
app/views/search/_search_content.html.erb
1 | 1 | <div id='search-content'> |
2 | 2 | <div class='total'> |
3 | - <%= n_('Total of 1 result', 'Total of %s results', @searches[@asset][:results].total_entries) % @searches[@asset][:results].total_entries.inspect %> | |
3 | + <%= n_('Total of 1 result', 'Total of %s results'.html_safe, @searches[@asset][:results].total_entries) % @searches[@asset][:results].total_entries.inspect %> | |
4 | 4 | </div> |
5 | 5 | |
6 | 6 | <%= display_results(@searches, @asset) %> | ... | ... |