Commit 6adc8bc231ef3c5692621c8bec01ea6411c83f03

Authored by Rodrigo Souto
2 parents 46f13390 c9eccb09

Merge branch 'master' into rails3

Conflicts:
	app/controllers/my_profile/memberships_controller.rb
	app/helpers/application_helper.rb
	app/helpers/boxes_helper.rb
	app/models/article.rb
	app/models/category.rb
	app/models/change_password.rb
	app/models/comment.rb
	app/models/environment.rb
	app/models/event.rb
	app/models/profile.rb
	app/models/recent_documents_block.rb
	app/models/region.rb
	app/models/task.rb
	app/models/uploaded_file.rb
	app/views/account/forgot_password.html.erb
	app/views/events/_events_by_day.html.erb
	app/views/profile/_person_profile.html.erb
	app/views/shared/_manage_enterprises.html.erb
	config/environment.rb
	config/initializers/plugins.rb
	config/routes.rb
	db/schema.rb
	debian/control
	lib/noosfero/plugin.rb
	test/unit/article_test.rb
	test/unit/category_test.rb
	test/unit/change_password_test.rb
	test/unit/cms_helper_test.rb
	test/unit/comment_test.rb
	test/unit/content_viewer_helper_test.rb
	test/unit/event_test.rb
	test/unit/folder_helper_test.rb
	test/unit/person_test.rb
	test/unit/price_detail_test.rb
	test/unit/profile_helper_test.rb
	test/unit/recent_documents_block_test.rb
	test/unit/scrap_test.rb
	test/unit/user_mailer_test.rb
	vendor/plugins/action_tracker_has_comments/init.rb
	vendor/plugins/delayed_job/lib/delayed/worker.rb
	vendor/plugins/noosfero_caching/init.rb
Showing 566 changed files with 74620 additions and 59234 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 566 files displayed.

AUTHORS
... ... @@ -44,6 +44,7 @@ Antonio Terceiro <terceiro@colivre.coop.br>
44 44 Aurelio A. Heckert <aurelio@colivre.coop.br>
45 45 Braulio Bhavamitra <brauliobo@gmail.com>
46 46 Bráulio Bhavamitra <brauliobo@gmail.com>
  47 +Braulio Bhavamitra <braulio@eita.org.br>
47 48 Caio <caio.csalgado@gmail.com>
48 49 Caio + Diego + Pedro + João <caio.csalgado@gmail.com>
49 50 Caio Formiga <caio.formiga@gmail.com>
... ... @@ -110,6 +111,7 @@ Diego Martinez &lt;diegoamc90@gmail.com&gt;
110 111 Diego Martinez <diego@diego-K55A.(none)>
111 112 Diego + Renan <renanteruoc@gmail.com>
112 113 Fernanda Lopes <nanda.listas+psl@gmail.com>
  114 +Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br>
113 115 Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)>
114 116 Grazieno Pellegrino <grazieno@gmail.com>
115 117 Isaac Canan <isaac@intelletto.com.br>
... ... @@ -147,6 +149,8 @@ João M. M. Silva + Rafael Manzo &lt;jaodsilv@linux.ime.usp.br&gt;
147 149 João M. M. Silva + Renan Teruo <jaodsilv@linux.ime.usp.br>
148 150 Joenio Costa <joenio@colivre.coop.br>
149 151 Josef Spillner <josef.spillner@tu-dresden.de>
  152 +Junior Silva <juniorsilva1001@gmail.com>
  153 +Junior Silva <juniorsilva7@juniorsilva-Aspire-5750Z.(none)>
150 154 Keilla Menezes <keilla@colivre.coop.br>
151 155 Larissa Reis <larissa@colivre.coop.br>
152 156 Larissa Reis <reiss.larissa@gmail.com>
... ... @@ -158,6 +162,7 @@ Lucas Melo &lt;lucaspradomelo@gmail.com&gt;
158 162 Luis David Aguilar Carlos <ludwig9003@gmail.com>
159 163 Martín Olivera <molivera@solar.org.ar>
160 164 Moises Machado <moises@colivre.coop.br>
  165 +Naíla Alves <naila@colivre.coop.br>
161 166 Nanda Lopes <nanda.listas+psl@gmail.com>
162 167 Paulo Meirelles + Alessandro Palmeira + João M. M. da Silva <paulo@softwarelivre.org>
163 168 Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org>
... ... @@ -187,12 +192,15 @@ Renan Teruo + Diego Araujo &lt;renanteruoc@gmail.com&gt;
187 192 Renan Teruo + Diego Araújo <renanteruoc@gmail.com>
188 193 Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com>
189 194 Renan Teruo + Rafael Manzo <renanteruoc@gmail.com>
  195 +Rodrigo Souto <diguliu@gmail.com>
190 196 Rodrigo Souto <rodrigo@colivre.coop.br>
191 197 Ronny Kursawe <kursawe.ronny@googlemail.com>
192 198 root <root@debian.sdr.serpro>
193 199 Samuel R. C. Vale <srcvale@holoscopio.com>
194 200 Valessio Brito <valessio@gmail.com>
195 201 vfcosta <vfcosta@gmail.com>
  202 +Victor Costa <vfcosta@gmail.com>
  203 +Vinicius Cubas Brand <viniciuscb@gmail.com>
196 204 Visita <visita@debian.(none)>
197 205 Yann Lugrin <yann.lugrin@liquid-concept.ch>
198 206  
... ...
COPYRIGHT
... ... @@ -4,8 +4,9 @@ Copyright (c) 2007-2009,
4 4 Cáritas Brasileira <http://www.caritasbrasileira.org/>
5 5 Copyright (c) 2007-2009,
6 6 Ynternet.org Foundation <http://www.ynternet.org/>
7   -Copyright (c) 2008-2009,
  7 +Copyright (c) 2008-2013,
8 8 Colivre <http://www.colivre.coop.br/>
  9 +Copyright (c) the Noosfero contributors. See AUTHORS
9 10  
10 11 This program is free software: you can redistribute it and/or modify
11 12 it under the terms of the GNU Affero General Public License as published by
... ...
HACKING
... ... @@ -52,3 +52,12 @@ If you write such script for your own OS, *please* share it with us at the
52 52 development mailing list so that we can include it in the official repository.
53 53 This way other people using the same OS will have to put less effort to develop
54 54 Noosfero.
  55 +
  56 +== Submitting your changes back
  57 +
  58 +For now please read:
  59 +
  60 +- Coding conventions
  61 + http://noosfero.org/Development/CodingConventions
  62 +- Patch guidelines
  63 + http://noosfero.org/Development/PatchGuidelines
... ...
HACKING.rails235
... ... @@ -1,13 +0,0 @@
1   -This is a draft of how to create a environment to Rails 2.3.5 to Noosfero
2   -development.
3   -
4   -Install dependencies:
5   -
6   -gem install rails -v 2.3.5
7   -gem install i18n
8   -gem install will_paginate -v 2.3.12
9   -gem install cucumber
10   -
11   -Creating initial environment:
12   -
13   -rake db:schema:load
INSTALL.chat
... ... @@ -6,7 +6,7 @@ To configure XMPP/BOSH in Noosfero you need:
6 6 * SystemTimer - http://ph7spot.com/musings/system-timer
7 7 * Pidgin data files - http://www.pidgin.im/
8 8  
9   -If you use Debian Wheezy:
  9 +If you use Debian 6.0 (squeeze):
10 10  
11 11 # apt-get install librestclient-ruby pidgin-data ruby1.8-dev
12 12 # gem install SystemTimer
... ...
README
1   -noosfero - a web-based social platform
  1 +Noosfero - a web-based social platform
2 2 ======================================
3 3  
4   -:: About the project
  4 +http://www.noosfero.org/
5 5  
6   -Homepage: http://www.noosfero.org/
  6 +Documentation
  7 +-------------
7 8  
8   -:: Authors and copyright
  9 +The following documentation is available:
9 10  
10   -Authors: see file AUTHORS
11   -Copyright information: see file COPYRIGHT
12   -Full license text; see file COPYING
  11 +File Purpose
  12 +~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  13 +INSTALL install instructions
  14 +INSTALL.awstats install instructions - access statistics service
  15 +INSTALL.chat install instructions - chat service
  16 +INSTALL.email install instructions - email service
  17 +INSTALL.multitenancy install instructions - multiple sites
  18 +INSTALL.varnish install instructions - varnish HTTP caching (recommended)
  19 +HACKING development instruction
  20 +RELEASING instructions for doing releases
  21 +doc/noosfero/* user documentation (available through the app itself)
  22 +
  23 +
  24 +Authors and copyright
  25 +---------------------
  26 +
  27 +Authorship and copyright information is available in the files listed below.
  28 +
  29 +File Purpose
  30 +~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  31 +AUTHORS list of authors (updated at each release)
  32 +COPYRIGHT Copyright statement for the project
  33 +COPYING Full text of the project license
... ...
app/controllers/admin/features_controller.rb
... ... @@ -2,7 +2,7 @@ class FeaturesController &lt; AdminController
2 2 protect 'edit_environment_features', :environment
3 3  
4 4 def index
5   - @features = Environment.available_features
  5 + @features = Environment.available_features.sort_by{|k,v|v}
6 6 end
7 7  
8 8 post_only :update
... ...
app/controllers/admin/users_controller.rb
... ... @@ -4,12 +4,52 @@ class UsersController &lt; AdminController
4 4  
5 5 protect 'manage_environment_users', :environment
6 6  
  7 + include UsersHelper
  8 +
7 9 def index
  10 + @filter = params[:filter]
  11 + scope = environment.people.no_templates
  12 + if @filter == 'admin_users'
  13 + scope = scope.admins
  14 + elsif @filter == 'activated_users'
  15 + scope = scope.activated
  16 + elsif @filter == 'deactivated_users'
  17 + scope = scope.deactivated
  18 + end
  19 + @q = params[:q]
  20 + @collection = find_by_contents(:people, scope, @q, {:per_page => per_page, :page => params[:npage]})[:results]
  21 + end
  22 +
  23 + def set_admin_role
  24 + person = environment.people.find(params[:id])
  25 + environment.add_admin(person)
  26 + redirect_to :action => :index, :q => params[:q], :filter => params[:filter]
  27 + end
  28 +
  29 + def reset_admin_role
  30 + person = environment.people.find(params[:id])
  31 + environment.remove_admin(person)
  32 + redirect_to :action => :index, :q => params[:q], :filter => params[:filter]
  33 + end
  34 +
  35 + def activate
  36 + person = environment.people.find(params[:id])
  37 + person.user.activate
  38 + redirect_to :action => :index, :q => params[:q], :filter => params[:filter]
  39 + end
  40 +
  41 + def deactivate
  42 + person = environment.people.find(params[:id])
  43 + person.user.deactivate
  44 + redirect_to :action => :index, :q => params[:q], :filter => params[:filter]
  45 + end
  46 +
  47 + def download
8 48 respond_to do |format|
9 49 format.html
10 50 format.xml do
11   - @users = User.find(:all, :conditions => {:environment_id => environment.id}, :include => [:person])
12   - send_data @users.to_xml(
  51 + users = User.find(:all, :conditions => {:environment_id => environment.id}, :include => [:person])
  52 + send_data users.to_xml(
13 53 :skip_types => true,
14 54 :only => %w[email login created_at updated_at],
15 55 :include => { :person => {:only => %w[name updated_at created_at address birth_date contact_phone identifier lat lng] } }),
... ... @@ -45,4 +85,10 @@ class UsersController &lt; AdminController
45 85 end
46 86 end
47 87  
  88 + private
  89 +
  90 + def per_page
  91 + 10
  92 + end
  93 +
48 94 end
... ...
app/controllers/application_controller.rb
... ... @@ -5,7 +5,7 @@ class ApplicationController &lt; ActionController::Base
5 5  
6 6 before_filter :setup_multitenancy
7 7 before_filter :detect_stuff_by_domain
8   - before_filter :init_noosfero_plugins
  8 + before_filter :init_noosfero_plugins_controller_filters
9 9 before_filter :allow_cross_domain_access
10 10  
11 11 def allow_cross_domain_access
... ... @@ -24,8 +24,12 @@ class ApplicationController &lt; ActionController::Base
24 24 include ApplicationHelper
25 25 layout :get_layout
26 26 def get_layout
27   - prepend_view_path('public/' + theme_path)
28   - theme_option(:layout) || 'application'
  27 + theme_layout = theme_option(:layout)
  28 + if theme_layout
  29 + theme_view_file('layouts/'+theme_layout) || theme_layout
  30 + else
  31 + 'application'
  32 + end
29 33 end
30 34  
31 35 def log_processing
... ... @@ -128,22 +132,21 @@ class ApplicationController &lt; ActionController::Base
128 132  
129 133 include Noosfero::Plugin::HotSpot
130 134  
131   - def init_noosfero_plugins
132   - plugins.each do |plugin|
133   - prepend_view_path(plugin.class.view_path)
134   - end
135   - init_noosfero_plugins_controller_filters
136   - end
137   -
138 135 # This is a generic method that initialize any possible filter defined by a
139 136 # plugin to the current controller being initialized.
140 137 def init_noosfero_plugins_controller_filters
141 138 plugins.each do |plugin|
142 139 filters = plugin.send(self.class.name.underscore + '_filters')
143 140 filters = [filters] if !filters.kind_of?(Array)
  141 + controller_filters = self.class.filter_chain.map {|c| c.method }
144 142 filters.each do |plugin_filter|
145   - self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {}))
146   - self.class.send(:define_method, plugin.class.name.underscore + '_' + plugin_filter[:method_name], plugin_filter[:block])
  143 + filter_method = plugin.class.name.underscore.gsub('/','_') + '_' + plugin_filter[:method_name]
  144 + unless controller_filters.include?(filter_method)
  145 + self.class.send(plugin_filter[:type], filter_method, (plugin_filter[:options] || {}))
  146 + self.class.send(:define_method, filter_method) do
  147 + instance_eval(&plugin_filter[:block]) if environment.plugin_enabled?(plugin.class)
  148 + end
  149 + end
147 150 end
148 151 end
149 152 end
... ... @@ -173,17 +176,16 @@ class ApplicationController &lt; ActionController::Base
173 176 end
174 177  
175 178 def find_by_contents(asset, scope, query, paginate_options={:page => 1}, options={})
176   - scope = scope.send(options[:filter]) if options[:filter]
177   -
178   - @plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) ||
  179 + plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) ||
179 180 fallback_find_by_contents(asset, scope, query, paginate_options, options)
180 181 end
181 182  
182 183 private
183 184  
184 185 def fallback_find_by_contents(asset, scope, query, paginate_options, options)
185   - return {:results => scope.paginate(paginate_options)} if query.blank?
186   - {:results => scope.like_search(query).paginate(paginate_options)}
  186 + scope = scope.like_search(query) unless query.blank?
  187 + scope = scope.send(options[:filter]) unless options[:filter].blank?
  188 + {:results => scope.paginate(paginate_options)}
187 189 end
188 190  
189 191 end
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -73,6 +73,7 @@ class CmsController &lt; MyProfileController
73 73 refuse_blocks
74 74 record_coming
75 75 if request.post?
  76 + @article.image = nil if params[:remove_image] == 'true'
76 77 @article.last_changed_by = user
77 78 if @article.update_attributes(params[:article])
78 79 if !continue
... ... @@ -144,10 +145,15 @@ class CmsController &lt; MyProfileController
144 145  
145 146 post_only :set_home_page
146 147 def set_home_page
147   - @article = profile.articles.find(params[:id])
148   - profile.home_page = @article
149   - profile.save(false)
150   - session[:notice] = _('"%s" configured as home page.') % @article.name
  148 + article = params[:id].nil? ? nil : profile.articles.find(params[:id])
  149 + profile.update_attribute(:home_page, article)
  150 +
  151 + if article.nil?
  152 + session[:notice] = _('Homepage reseted.')
  153 + else
  154 + session[:notice] = _('"%s" configured as homepage.') % article.name
  155 + end
  156 +
151 157 redirect_to (request.referer || profile.url)
152 158 end
153 159  
... ... @@ -267,7 +273,10 @@ class CmsController &lt; MyProfileController
267 273 @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url)
268 274 @task = SuggestArticle.new(params[:task])
269 275 if request.post?
270   - @task.target = profile
  276 + @task.target = profile
  277 + @task.ip_address = request.remote_ip
  278 + @task.user_agent = request.user_agent
  279 + @task.referrer = request.referrer
271 280 if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save
272 281 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.')
273 282 redirect_to @back_to
... ...
app/controllers/my_profile/manage_products_controller.rb
... ... @@ -6,10 +6,10 @@ class ManageProductsController &lt; ApplicationController
6 6 before_filter :login_required, :except => [:show]
7 7 before_filter :create_product?, :only => [:new]
8 8  
9   - protected
  9 + protected
10 10  
11 11 def check_environment_feature
12   - if profile.environment.enabled?('disable_products_for_enterprises')
  12 + unless profile.environment.enabled?('products_for_enterprises')
13 13 render_not_found
14 14 return
15 15 end
... ...
app/controllers/my_profile/memberships_controller.rb
1 1 class MembershipsController < MyProfileController
2 2  
3 3 protect 'manage_memberships', :profile
4   -
  4 +
5 5 def index
6   - @memberships = profile.memberships
  6 + @roles = environment.roles.select do |role|
  7 + ra = profile.role_assignments.find_by_role_id(role.id)
  8 + ra.present? && ra.resource_type == 'Profile'
  9 + end
  10 + @filter = params[:filter_type].blank? ? nil : params[:filter_type]
  11 + begin
  12 + @memberships = @filter.nil? ? profile.memberships : profile.memberships_by_role(environment.roles.find(@filter))
  13 + rescue ActiveRecord::RecordNotFound
  14 + @memberships = []
  15 + end
7 16 end
8 17  
9 18 def new_community
10 19 @community = Community.new(params[:community])
11 20 @community.environment = environment
  21 + @back_to = params[:back_to] || url_for(:action => 'index')
12 22 if request.post? && @community.valid?
13 23 @community = Community.create_after_moderation(user, params[:community].merge({:environment => environment}))
14   - redirect_to :action => 'index'
  24 + redirect_to @back_to
15 25 return
16 26 end
17 27 end
... ...
app/controllers/my_profile/profile_design_controller.rb
... ... @@ -38,7 +38,7 @@ class ProfileDesignController &lt; BoxOrganizerController
38 38 end
39 39  
40 40 # product block exclusive for enterprises in environments that permits it
41   - if profile.enterprise? && !profile.environment.enabled?('disable_products_for_enterprises')
  41 + if profile.enterprise? && profile.environment.enabled?('products_for_enterprises')
42 42 blocks << ProductsBlock
43 43 end
44 44  
... ...
app/controllers/my_profile/profile_editor_controller.rb
... ... @@ -4,7 +4,7 @@ class ProfileEditorController &lt; MyProfileController
4 4 protect 'destroy_profile', :profile, :only => [:destroy_profile]
5 5  
6 6 def index
7   - @pending_tasks = Task.to(profile).pending.select{|i| user.has_permission?(i.permission, profile)}
  7 + @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)}
8 8 end
9 9  
10 10 helper :profile
... ...
app/controllers/my_profile/spam_controller.rb
... ... @@ -14,9 +14,15 @@ class SpamController &lt; MyProfileController
14 14 if params[:remove_comment]
15 15 profile.comments_received.find(params[:remove_comment]).destroy
16 16 end
  17 + if params[:remove_task]
  18 + Task.to(profile).find_by_id(params[:remove_task]).destroy
  19 + end
17 20 if params[:mark_comment_as_ham]
18 21 profile.comments_received.find(params[:mark_comment_as_ham]).ham!
19 22 end
  23 + if params[:mark_task_as_ham] && (t = Task.to(profile).find_by_id(params[:mark_task_as_ham]))
  24 + t.ham!
  25 + end
20 26 if request.xhr?
21 27 json_response(true)
22 28 else
... ... @@ -28,7 +34,8 @@ class SpamController &lt; MyProfileController
28 34 return
29 35 end
30 36  
31   - @spam = profile.comments_received.spam.paginate({:page => params[:page]})
  37 + @comment_spam = profile.comments_received.spam.paginate({:page => params[:comments_page]})
  38 + @task_spam = Task.to(profile).spam.paginate({:page => params[:tasks_page]})
32 39 end
33 40  
34 41 protected
... ...
app/controllers/my_profile/tasks_controller.rb
... ... @@ -4,12 +4,12 @@ class TasksController &lt; MyProfileController
4 4  
5 5 def index
6 6 @filter = params[:filter_type].blank? ? nil : params[:filter_type]
7   - @tasks = Task.to(profile).pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
  7 + @tasks = Task.to(profile).without_spam.pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
8 8 @failed = params ? params[:failed] : {}
9 9 end
10 10  
11 11 def processed
12   - @tasks = Task.to(profile).closed.sort_by(&:created_at)
  12 + @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at)
13 13 end
14 14  
15 15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ]
... ... @@ -57,7 +57,7 @@ class TasksController &lt; MyProfileController
57 57 end
58 58  
59 59 def list_requested
60   - @tasks = Task.find_all_by_requestor_id(profile.id)
  60 + @tasks = Task.without_spam.find_all_by_requestor_id(profile.id)
61 61 end
62 62  
63 63 def ticket_details
... ...
app/controllers/public/account_controller.rb
... ... @@ -141,22 +141,34 @@ class AccountController &lt; ApplicationController
141 141 end
142 142 end
143 143  
144   - # The user requests a password change. She forgot her old password.
145   - #
146   - # Posts back.
  144 + include ForgotPasswordHelper
  145 + helper :forgot_password
  146 +
147 147 def forgot_password
148 148 if @plugins.dispatch(:allow_password_recovery).include?(false)
149 149 redirect_back_or_default(:controller => 'home')
150 150 session[:notice] = _("This environment doesn't allow password recovery.")
151 151 end
152   - @change_password = ChangePassword.new(params[:change_password])
  152 +
  153 + @change_password = ChangePassword.new
153 154  
154 155 if request.post?
155 156 begin
156   - @change_password.save!
  157 + requestors = fetch_requestors(params[:value])
  158 + raise ActiveRecord::RecordNotFound if requestors.blank? || params[:value].blank?
  159 +
  160 + requestors.each do |requestor|
  161 + ChangePassword.create!(:requestor => requestor)
  162 + end
157 163 render :action => 'password_recovery_sent'
158   - rescue ActiveRecord::RecordInvalid => e
159   - nil # just pass and render at the end of the action
  164 + rescue ActiveRecord::RecordNotFound
  165 + if params[:value].blank?
  166 + @change_password.errors.add_to_base(_('Can not recover user password with blank value.'))
  167 + else
  168 + @change_password.errors.add_to_base(_('Could not find any user with %s equal to "%s".') % [fields_label, params[:value]])
  169 + end
  170 + rescue ActiveRecord::RecordInvald
  171 + @change_password.errors.add_to_base(_('Could not perform password recovery for the user.'))
160 172 end
161 173 end
162 174 end
... ... @@ -262,7 +274,7 @@ class AccountController &lt; ApplicationController
262 274 def user_data
263 275 user_data =
264 276 if logged_in?
265   - current_user.data_hash
  277 + current_user.data_hash(gravatar_default)
266 278 else
267 279 { }
268 280 end
... ...
app/controllers/public/catalog_controller.rb
... ... @@ -12,7 +12,7 @@ class CatalogController &lt; PublicController
12 12 protected
13 13  
14 14 def check_enterprise_and_environment
15   - unless profile.kind_of?(Enterprise) && !profile.environment.enabled?('disable_products_for_enterprises')
  15 + unless profile.kind_of?(Enterprise) && @profile.environment.enabled?('products_for_enterprises')
16 16 redirect_to :controller => 'profile', :profile => profile.identifier, :action => 'index'
17 17 end
18 18 end
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -53,8 +53,11 @@ class ContentViewerController &lt; ApplicationController
53 53 # At this point the page will be showed
54 54 @page.hit
55 55  
56   - unless @page.mime_type == 'text/html' || (@page.image? && params[:view])
  56 + @page = FilePresenter.for @page
  57 +
  58 + if @page.download? params[:view]
57 59 headers['Content-Type'] = @page.mime_type
  60 + headers.merge! @page.download_headers
58 61 data = @page.data
59 62  
60 63 # TODO test the condition
... ... @@ -70,7 +73,7 @@ class ContentViewerController &lt; ApplicationController
70 73  
71 74 #FIXME see a better way to do this. It's not need to pass this variable anymore
72 75 @comment = Comment.new
73   -
  76 +
74 77 if @page.has_posts?
75 78 posts = if params[:year] and params[:month]
76 79 filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01")
... ... @@ -79,15 +82,21 @@ class ContentViewerController &lt; ApplicationController
79 82 @page.posts
80 83 end
81 84  
82   - if @page.blog? && @page.display_posts_in_current_language?
83   - posts = posts.native_translations.all(Article.display_filter(user, profile)).map{ |p| p.get_translation_to(FastGettext.locale) }.compact
84   - end
  85 + #FIXME Need to run this before the pagination because this version of
  86 + # will_paginate returns a will_paginate collection instead of a
  87 + # relation.
  88 + blog_with_translation = @page.blog? && @page.display_posts_in_current_language?
  89 + posts = posts.native_translations if blog_with_translation
85 90  
86 91 @posts = posts.paginate({ :page => params[:npage], :per_page => @page.posts_per_page }.merge(Article.display_filter(user, profile)))
  92 +
  93 + if blog_with_translation
  94 + @posts.replace @posts.map{ |p| p.get_translation_to(FastGettext.locale) }.compact
  95 + end
87 96 end
88 97  
89 98 if @page.folder? && @page.gallery?
90   - @images = @page.images
  99 + @images = @page.images.select{ |a| a.display_to? user }
91 100 @images = @images.paginate(:per_page => per_page, :page => params[:npage]) unless params[:slideshow]
92 101 end
93 102  
... ... @@ -100,9 +109,9 @@ class ContentViewerController &lt; ApplicationController
100 109 end
101 110  
102 111 @comments = @page.comments.without_spam
  112 + @comments = @plugins.filter(:unavailable_comments, @comments)
103 113 @comments_count = @comments.count
104   - @comments = @plugins.filter(:unavailable_comments, @comments.without_reply)
105   - @comments = @comments.paginate(:per_page => per_page, :page => params[:comment_page] )
  114 + @comments = @comments.without_reply.paginate(:per_page => per_page, :page => params[:comment_page] )
106 115  
107 116 if params[:slideshow]
108 117 render :action => 'slideshow', :layout => 'slideshow'
... ...
app/controllers/public/events_controller.rb
1 1 class EventsController < PublicController
2 2  
3 3 needs_profile
4   - no_design_blocks
5 4  
6 5 def events
7   - @selected_day = nil
8   - @events_of_the_day = []
9   - date = build_date(params[:year], params[:month], params[:day])
  6 + @events = []
  7 + @date = build_date(params[:year], params[:month], params[:day])
10 8  
11   - if params[:day] || !params[:year] && !params[:month]
12   - @selected_day = date
13   - @events_of_the_day = profile.events.by_day(@selected_day)
  9 + if !params[:year] && !params[:month] && !params[:day]
  10 + @events = profile.events.next_events_from_month(@date)
14 11 end
15 12  
16   - events = profile.events.by_range((date - 1.month).at_beginning_of_month..(date + 1.month).at_end_of_month)
  13 + if params[:year] || params[:month]
  14 + @events = profile.events.by_month(@date)
  15 + end
  16 +
  17 + events_in_range = profile.events.by_range((@date - 1.month).at_beginning_of_month .. (@date + 1.month).at_end_of_month)
17 18  
18   - @calendar = populate_calendar(date, events)
19   - @previous_calendar = populate_calendar(date - 1.month, events)
20   - @next_calendar = populate_calendar(date + 1.month, events)
  19 + @calendar = populate_calendar(@date, events_in_range)
21 20 end
22 21  
23 22 def events_by_day
24   - @selected_day = build_date(params[:year], params[:month], params[:day])
25   - @events_of_the_day = profile.events.by_day(@selected_day)
26   - render :partial => 'events_by_day'
  23 + @date = build_date(params[:year], params[:month], params[:day])
  24 + @events = profile.events.by_day(@date)
  25 + render :partial => 'events'
27 26 end
28 27  
29 28 protected
... ...
app/controllers/public/profile_controller.rb
... ... @@ -206,10 +206,50 @@ class ProfileController &lt; PublicController
206 206 end
207 207  
208 208 def view_more_network_activities
209   - @activities = @profile.tracked_notifications.paginate(:per_page => 10, :page => params[:page])
  209 + @activities = @profile.tracked_notifications.paginate(:per_page => 10, :page => params[:page])
210 210 render :partial => 'profile_network_activities', :locals => {:network_activities => @activities}
211 211 end
212 212  
  213 + def more_comments
  214 + activity = ActionTracker::Record.find(:first, :conditions => {:id => params[:activity], :user_id => @profile})
  215 + comments_count = activity.comments.count
  216 + comment_page = (params[:comment_page] || 1).to_i
  217 + comments_per_page = 5
  218 + no_more_pages = comments_count <= comment_page * comments_per_page
  219 +
  220 + render :update do |page|
  221 + page.insert_html :bottom, 'profile-wall-activities-comments-'+params[:activity],
  222 + :partial => 'comment', :collection => activity.comments.paginate(:per_page => comments_per_page, :page => comment_page)
  223 +
  224 + if no_more_pages
  225 + page.remove 'profile-wall-activities-comments-more-'+params[:activity]
  226 + else
  227 + page.replace_html 'profile-wall-activities-comments-more-'+params[:activity],
  228 + :partial => 'more_comments', :locals => {:activity => activity, :comment_page => comment_page}
  229 + end
  230 + end
  231 + end
  232 +
  233 + def more_replies
  234 + activity = Scrap.find(:first, :conditions => {:id => params[:activity], :receiver_id => @profile, :scrap_id => nil})
  235 + comments_count = activity.replies.count
  236 + comment_page = (params[:comment_page] || 1).to_i
  237 + comments_per_page = 5
  238 + no_more_pages = comments_count <= comment_page * comments_per_page
  239 +
  240 + render :update do |page|
  241 + page.insert_html :bottom, 'profile-wall-activities-comments-'+params[:activity],
  242 + :partial => 'profile_scrap', :collection => activity.replies.paginate(:per_page => comments_per_page, :page => comment_page), :as => :scrap
  243 +
  244 + if no_more_pages
  245 + page.remove 'profile-wall-activities-comments-more-'+params[:activity]
  246 + else
  247 + page.replace_html 'profile-wall-activities-comments-more-'+params[:activity],
  248 + :partial => 'more_replies', :locals => {:activity => activity, :comment_page => comment_page}
  249 + end
  250 + end
  251 + end
  252 +
213 253 def remove_scrap
214 254 begin
215 255 scrap = current_user.person.scraps(params[:scrap_id])
... ... @@ -343,6 +383,7 @@ class ProfileController &lt; PublicController
343 383 end
344 384 end
345 385  
  386 +
346 387 protected
347 388  
348 389 def check_access_to_profile
... ... @@ -393,4 +434,5 @@ class ProfileController &lt; PublicController
393 434 def relations_to_include
394 435 [:image, :domains, :preferred_domain, :environment]
395 436 end
  437 +
396 438 end
... ...
app/controllers/public/search_controller.rb
... ... @@ -93,25 +93,27 @@ class SearchController &lt; PublicController
93 93 year = (params[:year] ? params[:year].to_i : Date.today.year)
94 94 month = (params[:month] ? params[:month].to_i : Date.today.month)
95 95 day = (params[:day] ? params[:day].to_i : Date.today.day)
96   - date = build_date(params[:year], params[:month], params[:day])
97   - date_range = (date - 1.month).at_beginning_of_month..(date + 1.month).at_end_of_month
  96 + @date = build_date(year, month, day)
  97 + date_range = (@date - 1.month).at_beginning_of_month..(@date + 1.month).at_end_of_month
98 98  
99   - @selected_day = nil
100   - @events_of_the_day = []
  99 + @events = []
101 100 if params[:day] || !params[:year] && !params[:month]
102   - @selected_day = date
103   - @events_of_the_day = @category ?
104   - environment.events.by_day(@selected_day).in_category(Category.find(@category_id)) :
105   - environment.events.by_day(@selected_day)
  101 + @events = @category ?
  102 + environment.events.by_day(@date).in_category(Category.find(@category_id)) :
  103 + environment.events.by_day(@date)
  104 + end
  105 +
  106 + if params[:year] || params[:month]
  107 + @events = @category ?
  108 + environment.events.by_month(@date).in_category(Category.find(@category_id)) :
  109 + environment.events.by_month(@date)
106 110 end
107 111  
108 112 @scope = date_range && params[:action] == 'events' ? environment.events.by_range(date_range) : environment.events
109 113 full_text_search
110 114  
111 115 events = @searches[@asset][:results]
112   - @calendar = populate_calendar(date, events)
113   - @previous_calendar = populate_calendar(date - 1.month, events)
114   - @next_calendar = populate_calendar(date + 1.month, events)
  116 + @calendar = populate_calendar(@date, events)
115 117 end
116 118  
117 119 # keep old URLs workings
... ... @@ -136,9 +138,9 @@ class SearchController &lt; PublicController
136 138 end
137 139  
138 140 def events_by_day
139   - @selected_day = build_date(params[:year], params[:month], params[:day])
140   - @events_of_the_day = environment.events.by_day(@selected_day)
141   - render :partial => 'events/events_by_day'
  141 + @date = build_date(params[:year], params[:month], params[:day])
  142 + @events = environment.events.by_day(@date)
  143 + render :partial => 'events/events'
142 144 end
143 145  
144 146 #######################################################
... ...
app/helpers/application_helper.rb
... ... @@ -40,6 +40,8 @@ module ApplicationHelper
40 40  
41 41 include LayoutHelper
42 42  
  43 + include Noosfero::Gravatar
  44 +
43 45 VIEW_EXTENSIONS = ['.rhtml', '.html.erb']
44 46  
45 47 def locale
... ... @@ -367,7 +369,7 @@ module ApplicationHelper
367 369 def current_theme
368 370 @current_theme ||=
369 371 begin
370   - if (session[:theme])
  372 + if session[:theme]
371 373 session[:theme]
372 374 else
373 375 # utility for developers: set the theme to 'random' in development mode and
... ... @@ -376,7 +378,7 @@ module ApplicationHelper
376 378 if Rails.env.development? && environment.theme == 'random'
377 379 @random_theme ||= Dir.glob('public/designs/themes/*').map { |f| File.basename(f) }.rand
378 380 @random_theme
379   - elsif Rails.env.development? && params[:theme] && File.exists?(Rails.root.join('public/designs/themes', params[:theme]))
  381 + elsif Rails.env.development? && respond_to?(:params) && params[:theme] && File.exists?(Rails.root.join('public/designs/themes', params[:theme]))
380 382 params[:theme]
381 383 else
382 384 if profile && !profile.theme.nil?
... ... @@ -398,18 +400,25 @@ module ApplicationHelper
398 400 end
399 401 end
400 402  
401   - def theme_include(template)
402   - # XXX Since we cannot control what people are doing in external themes, we
  403 + def theme_view_file(template)
  404 + # Since we cannot control what people are doing in external themes, we
403 405 # will keep looking for the deprecated .rhtml extension here.
404 406 VIEW_EXTENSIONS.each do |ext|
405 407 file = Rails.root.join('public', theme_path[1..-1], template + ext)
406   - if File.exists?(file)
407   - return render :file => file, :use_full_path => false
408   - end
  408 + return file if File.exists?(file)
409 409 end
410 410 nil
411 411 end
412 412  
  413 + def theme_include(template)
  414 + file = theme_view_file(template)
  415 + if file
  416 + render :file => file, :use_full_path => false
  417 + else
  418 + nil
  419 + end
  420 + end
  421 +
413 422 def theme_favicon
414 423 return '/designs/themes/' + current_theme + '/favicon.ico' if profile.nil? || profile.theme.nil?
415 424 if File.exists?(Rails.root.join('public', theme_path, 'favicon.ico'))
... ... @@ -561,6 +570,9 @@ module ApplicationHelper
561 570 # displays a link to the profile homepage with its image (as generated by
562 571 # #profile_image) and its name below it.
563 572 def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil )
  573 + if content = @plugins.dispatch_first(:profile_image_link, profile, size, tag, extra_info)
  574 + return instance_eval(&content)
  575 + end
564 576 name = profile.short_name
565 577 if profile.person?
566 578 url = url_for(profile.check_friendship_url)
... ... @@ -577,45 +589,20 @@ module ApplicationHelper
577 589 extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' )
578 590 links = links_for_balloon(profile)
579 591 content_tag('div', content_tag(tag,
580   - (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") +
581   - link_to(
582   - content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
583   - content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
584   - extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ),
585   - profile.url,
586   - :class => 'profile_link url',
587   - :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
588   - :title => profile.name ),
589   - :class => 'vcard'), :class => 'common-profile-list-block')
590   - end
591   -
592   - def gravatar_url_for(email, options = {})
593   - # Ta dando erro de roteamento
594   - default = theme_option['gravatar'] || NOOSFERO_CONF['gravatar'] || nil
595   - url_for( { :gravatar_id => Digest::MD5.hexdigest(email.to_s),
596   - :host => 'www.gravatar.com',
597   - :protocol => 'http://',
598   - :only_path => false,
599   - :controller => 'avatar.php',
600   - :d => default
601   - }.merge(options) )
602   - end
603   -
604   - def str_gravatar_url_for(email, options = {})
605   - default = theme_option['gravatar'] || NOOSFERO_CONF['gravatar'] || nil
606   - url = 'http://www.gravatar.com/avatar.php?gravatar_id=' +
607   - Digest::MD5.hexdigest(email.to_s)
608   - {
609   - :only_path => false,
610   - :d => default
611   - }.merge(options).each { |k,v|
612   - url += ( '&%s=%s' % [ k,v ] )
613   - }
614   - url
615   - end
616   -
617   - def gravatar_profile_url(email)
618   - 'http://www.gravatar.com/'+ Digest::MD5.hexdigest(email.to_s)
  592 + (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") +
  593 + link_to(
  594 + content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
  595 + content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
  596 + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ),
  597 + profile.url,
  598 + :class => 'profile_link url',
  599 + :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
  600 + :title => profile.name ),
  601 + :class => 'vcard'), :class => 'common-profile-list-block')
  602 + end
  603 +
  604 + def gravatar_default
  605 + (respond_to?(:theme_option) && theme_option.present? && theme_option['gravatar']) || NOOSFERO_CONF['gravatar']
619 606 end
620 607  
621 608 attr_reader :environment
... ... @@ -730,8 +717,15 @@ module ApplicationHelper
730 717 end
731 718  
732 719 def rolename_for(profile, resource)
733   - role = profile.role_assignments.find_by_resource_id(resource.id).role
734   - content_tag('span', role.name, :style => "color: #{role_color(role, resource.environment.id)}")
  720 + roles = profile.role_assignments.
  721 + where(:resource_id => resource.id).
  722 + sort_by{ |role_assignment| role_assignment.role_id }.
  723 + map(&:role)
  724 + names = []
  725 + roles.each do |role|
  726 + names << content_tag('span', role.name, :style => "color: #{role_color(role, resource.environment.id)}")
  727 + end
  728 + names.join(', ')
735 729 end
736 730  
737 731 def role_color(role, env_id)
... ... @@ -945,7 +939,7 @@ module ApplicationHelper
945 939 (@category ? " - #{@category.full_name}" : '')
946 940 end
947 941  
948   - # DEPRECATED. Do not use this·
  942 + # DEPRECATED. Do not use this.
949 943 def import_controller_stylesheets(options = {})
950 944 stylesheet_import( "controller_"+ controller.controller_name(), options )
951 945 end
... ... @@ -1128,15 +1122,34 @@ module ApplicationHelper
1128 1122 result
1129 1123 end
1130 1124  
1131   - def manage_enterprises
1132   - if user && !user.enterprises.empty?
1133   - enterprises_link = user.enterprises.map do |enterprise|
1134   - link_to(content_tag('strong', [_('<span>Manage</span> %s') % enterprise.short_name(25)]), @environment.top_url + "/myprofile/#{enterprise.identifier}", :class => "icon-menu-"+enterprise.class.identification.underscore, :title => [_('Manage %s') % enterprise.short_name])
  1125 + def manage_link(list, kind)
  1126 + if list.present?
  1127 + link_to_all = nil
  1128 + if list.count > 5
  1129 + list = list.first(5)
  1130 + link_to_all = link_to(content_tag('strong', _('See all')), :controller => 'memberships', :profile => current_user.login)
  1131 + end
  1132 + link = list.map do |element|
  1133 + link_to(content_tag('strong', [_('<span>Manage</span> %s') % element.short_name(25)]), @environment.top_url + "/myprofile/#{element.identifier}", :class => "icon-menu-"+element.class.identification.underscore, :title => [_('Manage %s') % element.short_name])
  1134 + end
  1135 + if link_to_all
  1136 + link << link_to_all
1135 1137 end
1136   - render :partial => 'shared/manage_enterprises', :locals => {:enterprises_link => enterprises_link}
  1138 + render :partial => "shared/manage_link", :locals => {:link => link, :kind => kind.to_s}
1137 1139 end
1138 1140 end
1139 1141  
  1142 + def manage_enterprises
  1143 + return unless user && user.environment.enabled?(:display_my_enterprises_on_user_menu)
  1144 + manage_link(user.enterprises, :enterprises)
  1145 + end
  1146 +
  1147 + def manage_communities
  1148 + return unless user && user.environment.enabled?(:display_my_communities_on_user_menu)
  1149 + administered_communities = user.communities.more_popular.select {|c| c.admins.include? user}
  1150 + manage_link(administered_communities, :communities)
  1151 + end
  1152 +
1140 1153 def usermenu_logged_in
1141 1154 pending_tasks_count = ''
1142 1155 count = user ? Task.to(user).pending.count : -1
... ... @@ -1144,11 +1157,12 @@ module ApplicationHelper
1144 1157 pending_tasks_count = link_to(count.to_s, @environment.top_url + '/myprofile/{login}/tasks', :id => 'pending-tasks-count', :title => _("Manage your pending tasks"))
1145 1158 end
1146 1159  
1147   - (_("<span class='welcome'>Welcome,</span> %s") % link_to('<i></i><strong>{login}</strong>', @environment.top_url + '/{login}', :id => "homepage-link", :title => _('Go to your homepage'))) +
  1160 + (_("<span class='welcome'>Welcome,</span> %s") % link_to('<i style="background-image:url({avatar})"></i><strong>{login}</strong>', @environment.top_url + '/{login}', :id => "homepage-link", :title => _('Go to your homepage'))) +
1148 1161 render_environment_features(:usermenu) +
1149   - link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', @environment.top_url + '/admin', :id => "controlpanel", :title => _("Configure the environment"), :class => 'admin-link', :style => 'display: none') +
  1162 + link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', @environment.top_url + '/admin', :title => _("Configure the environment"), :class => 'admin-link', :style => 'display: none') +
1150 1163 manage_enterprises.to_s +
1151   - link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', @environment.top_url + '/myprofile/{login}', :id => "controlpanel", :title => _("Configure your personal account and content")) +
  1164 + manage_communities.to_s +
  1165 + link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', @environment.top_url + '/myprofile/{login}', :class => 'ctrl-panel', :title => _("Configure your personal account and content")) +
1152 1166 pending_tasks_count +
1153 1167 link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
1154 1168 end
... ... @@ -1325,8 +1339,8 @@ module ApplicationHelper
1325 1339 @plugins.dispatch("content_remove_#{action.to_s}", @page).include?(true)
1326 1340 end
1327 1341  
1328   - def template_options(klass, field_name)
1329   - templates = klass.templates(environment)
  1342 + def template_options(kind, field_name)
  1343 + templates = environment.send(kind).templates
1330 1344 return '' if templates.count == 0
1331 1345 return hidden_field_tag("#{field_name}[template_id]", templates.first.id) if templates.count == 1
1332 1346  
... ... @@ -1440,8 +1454,8 @@ module ApplicationHelper
1440 1454 end
1441 1455  
1442 1456 def filter_html(html, source)
1443   - if @plugins
1444   - html = convert_macro(html, source)
  1457 + if @plugins && source && source.has_macro?
  1458 + html = convert_macro(html, source) unless @plugins.enabled_macros.blank?
1445 1459 #TODO This parse should be done through the macro infra, but since there
1446 1460 # are old things that do not support it we are keeping this hot spot.
1447 1461 html = @plugins.pipeline(:parse_content, html, source).first
... ...
app/helpers/blog_helper.rb
... ... @@ -42,7 +42,7 @@ module BlogHelper
42 42  
43 43 def display_post(article, format = 'full')
44 44 no_comments = (format == 'full') ? false : true
45   - html = send("display_#{format}_format", article)
  45 + html = send("display_#{format}_format", FilePresenter.for(article)).html_safe
46 46  
47 47 article_title(article, :no_comments => no_comments) + html
48 48 end
... ...
app/helpers/boxes_helper.rb
... ... @@ -99,17 +99,19 @@ module BoxesHelper
99 99 unless block.visible?
100 100 options[:title] = _("This block is invisible. Your visitors will not see it.")
101 101 end
102   - if controller.send(:content_editor?)
103   - result = filter_html(result, block)
104   - end
105   - box_decorator.block_target(block.box, block) +
106   - content_tag('div',
107   - content_tag('div',
  102 +
  103 + result = filter_html(result, block)
  104 +
  105 + content_tag('div',
  106 + box_decorator.block_target(block.box, block) +
  107 + content_tag('div',
108 108 content_tag('div',
109   - result + footer_content + box_decorator.block_edit_buttons(block),
110   - :class => 'block-inner-2'),
111   - :class => 'block-inner-1'),
112   - options) +
  109 + content_tag('div',
  110 + result + footer_content + box_decorator.block_edit_buttons(block),
  111 + :class => 'block-inner-2'),
  112 + :class => 'block-inner-1'),
  113 + options),
  114 + :class => 'block-outer') +
113 115 box_decorator.block_handle(block)
114 116 end
115 117  
... ... @@ -225,15 +227,11 @@ module BoxesHelper
225 227  
226 228 # DEPRECATED. Do not use this.
227 229 def import_blocks_stylesheets(options = {})
228   - @blocks_css_files ||= current_blocks.map{|b|'blocks/' + block_css_class_name(b)}.uniq
  230 + @blocks_css_files ||= current_blocks.map{|b|'blocks/' + block.class.name.to_css_class}.uniq
229 231 stylesheet_import(@blocks_css_files, options)
230 232 end
231   -
232   - def block_css_class_name(block)
233   - block.class.name.underscore.gsub('_', '-')
234   - end
235 233 def block_css_classes(block)
236   - classes = block_css_class_name(block)
  234 + classes = block.class.name.to_css_class
237 235 classes += ' invisible-block' if block.display == 'never'
238 236 classes
239 237 end
... ...
app/helpers/categories_helper.rb
... ... @@ -3,10 +3,21 @@ module CategoriesHelper
3 3  
4 4 COLORS = [
5 5 [ N_('Do not display at the menu'), nil ],
6   - [ N_('Orange'), 1 ],
7   - [ N_('Green'), 2 ],
8   - [ N_('Purple'), 3 ],
9   - [ N_('Red'), 4 ],
  6 + [ N_('Orange'), 1],
  7 + [ N_('Green'), 2],
  8 + [ N_('Purple'), 3],
  9 + [ N_('Red'), 4],
  10 + [ N_('Dark Green'), 5],
  11 + [ N_('Blue Oil'), 6],
  12 + [ N_('Blue'), 7],
  13 + [ N_('Brown'), 8],
  14 + [ N_('Light Green'), 9],
  15 + [ N_('Light Blue'), 10],
  16 + [ N_('Dark Blue'), 11],
  17 + [ N_('Blue Pool'), 12],
  18 + [ N_('Beige'), 13],
  19 + [ N_('Yellow'), 14],
  20 + [ N_('Light Brown'), 15]
10 21 ]
11 22  
12 23 TYPES = [
... ...
app/helpers/cms_helper.rb
... ... @@ -28,12 +28,12 @@ module CmsHelper
28 28 end
29 29  
30 30 def link_to_article(article)
31   - article_name = short_filename(article.title, 30)
  31 + article_name = article.title
32 32 if article.folder?
33 33 link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article)
34 34 else
35 35 if article.image?
36   - image_tag(icon_for_article(article)) + link_to(article_name, article.url)
  36 + image_tag(icon_for_article(article)) + link_to(article_name, article.url)
37 37 else
38 38 link_to article_name, article.url, :class => icon_for_article(article)
39 39 end
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -17,7 +17,7 @@ module ContentViewerHelper
17 17 title = article.display_title if article.kind_of?(UploadedFile) && article.image?
18 18 title = article.title if title.blank?
19 19 title = content_tag('h1', h(title), :class => 'title')
20   - if article.belongs_to_blog?
  20 + if article.belongs_to_blog? || article.belongs_to_forum?
21 21 unless args[:no_link]
22 22 title = content_tag('h1', link_to(article.name, article.url), :class => 'title')
23 23 end
... ...
app/helpers/dates_helper.rb
... ... @@ -35,6 +35,18 @@ module DatesHelper
35 35 end
36 36 end
37 37  
  38 + def show_date_month(date, use_numbers = false, year=true)
  39 + if date && use_numbers
  40 + date_format = year ? _('%{month}/%{year}') : _('%{month}/%{day}')
  41 + date_format % { :month => date.month, :year => date.year }
  42 + elsif date
  43 + date_format = year ? _('%{month_name}, %{year}') : _('%{month_name}')
  44 + date_format % { :month_name => month_name(date.month), :year => date.year }
  45 + else
  46 + ''
  47 + end
  48 + end
  49 +
38 50 # formats a datetime for displaying.
39 51 def show_time(time)
40 52 if time
... ... @@ -98,7 +110,11 @@ module DatesHelper
98 110 elsif opts[:previous]
99 111 date = date << 1
100 112 end
101   - _('%{month} %{year}') % { :year => date.year, :month => month_name(date.month.to_i) }
  113 + if opts[:only_month]
  114 + _('%{month}') % {:month => month_name(date.month.to_i) }
  115 + else
  116 + _('%{month} %{year}') % { :year => date.year, :month => month_name(date.month.to_i) }
  117 + end
102 118 end
103 119  
104 120 def build_date(year, month, day = 1)
... ... @@ -123,7 +139,7 @@ module DatesHelper
123 139 previous_month_date = date - 1.month
124 140  
125 141 label ||= show_month(previous_month_date.year, previous_month_date.month)
126   - link_to label, :year => previous_month_date.year, :month => previous_month_date.month
  142 + button(:back, label, {:year => previous_month_date.year, :month => previous_month_date.month})
127 143 end
128 144  
129 145 def link_to_next_month(year, month, label = nil)
... ... @@ -131,7 +147,7 @@ module DatesHelper
131 147 next_month_date = date + 1.month
132 148  
133 149 label ||= show_month(next_month_date.year, next_month_date.month)
134   - link_to label, :year => next_month_date.year, :month => next_month_date.month
  150 + button(:next, label, {:year => next_month_date.year, :month => next_month_date.month})
135 151 end
136 152  
137 153 def pick_date(object, method, options = {}, html_options = {})
... ...
app/helpers/events_helper.rb
1 1 module EventsHelper
2 2  
3 3 def list_events(date, events)
4   - return content_tag('em', _("Select a day on the left to display it's events here"), :class => 'select-a-day') unless date
5   - title = _('Events for %s') % show_date(date)
  4 + title = _('Events for %s') % show_date_month(date)
6 5 content_tag('h2', title) +
7 6 content_tag('div',
8 7 (events.any? ?
9 8 content_tag('table', events.select { |item| item.display_to?(user) }.map {|item| display_event_in_listing(item)}.join('')) :
10   - content_tag('em', _('No events for this date'), :class => 'no-events')
  9 + content_tag('em', _('No events for this month'), :class => 'no-events')
11 10 ), :id => 'agenda-items'
12 11 )
13 12 end
14 13  
15 14 def display_event_in_listing(article)
16   - content_tag(
17   - 'tr',
18   - content_tag('td', link_to(article.name, article.url, :class => icon_for_article(article))),
19   - :class => 'agenda-item'
  15 +
  16 + content_tag( 'tr',
  17 + content_tag('td',
  18 + content_tag('div', show_date(article.start_date) + ( article.end_date.nil? ? '' : (_(" to ") + show_date(article.end_date))),:class => 'event-date' ) +
  19 + content_tag('div',link_to(article.name,article.url),:class => 'event-title') +
  20 + content_tag('div',(article.address.nil? or article.address == '') ? '' : (_('Place: ') + article.address),:class => 'event-place')
  21 + )
20 22 )
21 23 end
22 24  
... ...
app/helpers/folder_helper.rb
... ... @@ -23,6 +23,7 @@ module FolderHelper
23 23 end
24 24  
25 25 def display_article_in_listing(article, recursive = false, level = 0)
  26 + article = FilePresenter.for article
26 27 article_link = if article.image?
27 28 link_to('&nbsp;' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true))
28 29 else
... ... @@ -42,12 +43,15 @@ module FolderHelper
42 43 end
43 44  
44 45 def icon_for_article(article)
45   - icon = article.class.icon_name(article)
  46 + article = FilePresenter.for article
  47 + icon = article.respond_to?(:icon_name) ?
  48 + article.icon_name :
  49 + article.class.icon_name(article)
46 50 if (icon =~ /\//)
47 51 icon
48 52 else
49   - klasses = 'icon icon-' + icon
50   - if article.kind_of?(UploadedFile)
  53 + klasses = 'icon ' + [icon].flatten.map{|name| 'icon-'+name}.join(' ')
  54 + if article.kind_of?(UploadedFile) || article.kind_of?(FilePresenter)
51 55 klasses += ' icon-upload-file'
52 56 end
53 57 klasses
... ...
app/helpers/forgot_password_helper.rb 0 → 100644
... ... @@ -0,0 +1,43 @@
  1 +module ForgotPasswordHelper
  2 + def plugins_options
  3 + @plugins.dispatch(:change_password_fields)
  4 + end
  5 +
  6 + def user_fields
  7 + %w[login email] + plugins_options.select {|options| options[:model].to_sym == :user }.map { |options| options[:field].to_s }
  8 + end
  9 +
  10 + def person_fields
  11 + %w[] + plugins_options.select {|options| options[:model].to_sym == :person }.map { |options| options[:field].to_s }
  12 + end
  13 +
  14 + def fields
  15 + user_fields + person_fields
  16 + end
  17 +
  18 + def fields_label
  19 + labels = [
  20 + _('Username'),
  21 + _('Email'),
  22 + ] + plugins_options.map { |options| options[:name] }
  23 +
  24 + last = labels.pop
  25 + label = labels.join(', ')
  26 + "#{label} #{_('or')} #{last}"
  27 + end
  28 +
  29 + def build_query(fields, value)
  30 + fields.map {|field| "#{field} = '#{value}'"}.join(' OR ')
  31 + end
  32 +
  33 + def fetch_requestors(value)
  34 + requestors = []
  35 + person_query = build_query(person_fields, value)
  36 + user_query = build_query(user_fields, value)
  37 +
  38 + requestors += Person.where(person_query).where(:environment_id => environment.id) if person_fields.present?
  39 + requestors += User.where(user_query).where(:environment_id => environment.id).map(&:person) if user_fields.present?
  40 + requestors
  41 + end
  42 +
  43 +end
... ...
app/helpers/profile_editor_helper.rb
... ... @@ -136,7 +136,7 @@ module ProfileEditorHelper
136 136 concat(
137 137 content_tag(
138 138 'div',
139   - capture(&block) + content_tag('br', '', :style => 'clear: left'),
  139 + capture(&block) + tag('br', :style => 'clear: left'),
140 140 :class => 'control-panel')
141 141 )
142 142 end
... ...
app/helpers/profile_helper.rb
1 1 module ProfileHelper
2 2  
3 3 def display_field(title, profile, field, force = false)
4   - if (!force && !profile.active_fields.include?(field.to_s)) ||
5   - (profile.active_fields.include?(field.to_s) && !profile.public_fields.include?(field.to_s) && (!user || (user != profile && !user.is_a_friend?(profile))))
  4 + unless force || profile.may_display_field_to?(field, user)
6 5 return ''
7 6 end
8 7 value = profile.send(field)
... ...
app/helpers/sweeper_helper.rb
... ... @@ -44,4 +44,30 @@ module SweeperHelper
44 44 def expire_profile_index(profile)
45 45 expire_timeout_fragment(profile.relationships_cache_key)
46 46 end
  47 +
  48 + def expire_blocks_cache(context, causes)
  49 + if context.kind_of?(Profile)
  50 + profile = context
  51 + environment = profile.environment
  52 + else
  53 + environment = context
  54 + profile = nil
  55 + end
  56 +
  57 + blocks_to_expire = []
  58 + if profile
  59 + profile.blocks.each {|block|
  60 + conditions = block.class.expire_on
  61 + blocks_to_expire << block unless (conditions[:profile] & causes).empty?
  62 + }
  63 + end
  64 + environment.blocks.each {|block|
  65 + conditions = block.class.expire_on
  66 + blocks_to_expire << block unless (conditions[:environment] & causes).empty?
  67 + }
  68 +
  69 + blocks_to_expire.uniq!
  70 + BlockSweeper.expire_blocks(blocks_to_expire)
  71 + end
  72 +
47 73 end
... ...
app/helpers/users_helper.rb 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +module UsersHelper
  2 +
  3 + FILTER_TRANSLATION = {
  4 + 'all_users' => _('All users'),
  5 + 'admin_users' => _('Admin users'),
  6 + 'activated_users' => _('Activated users'),
  7 + 'deactivated_users' => _('Deativated users'),
  8 + }
  9 +
  10 + def filter_selector(filter, float = 'right')
  11 + options = options_for_select(FILTER_TRANSLATION.map {|key, name| [name, key]}, :selected => filter)
  12 + url_params = url_for(params.merge(:filter => 'FILTER'))
  13 + onchange = "document.location.href = '#{url_params}'.replace('FILTER', this.value)"
  14 + select_field = select_tag(:filter, options, :onchange => onchange)
  15 + content_tag('div',
  16 + content_tag('strong', _('Filter')) + ': ' + select_field,
  17 + :class => "environment-users-customize-search"
  18 + )
  19 + end
  20 +
  21 + def filter_title(filter)
  22 + FILTER_TRANSLATION[filter]
  23 + end
  24 +
  25 +end
... ...
app/models/action_tracker_notification.rb
... ... @@ -3,7 +3,7 @@ class ActionTrackerNotification &lt; ActiveRecord::Base
3 3 belongs_to :profile
4 4 belongs_to :action_tracker, :class_name => 'ActionTracker::Record', :foreign_key => 'action_tracker_id'
5 5  
6   - has_many :comments, :through => :action_tracker, :class_name => 'Comment', :foreign_key => 'source_id'
  6 + delegate :comments, :to => :action_tracker, :allow_nil => true
7 7  
8 8 validates_presence_of :profile_id, :action_tracker_id
9 9 validates_uniqueness_of :action_tracker_id, :scope => :profile_id
... ...
app/models/approve_comment.rb
... ... @@ -6,7 +6,7 @@ class ApproveComment &lt; Task
6 6 validates_presence_of :comment_attributes
7 7  
8 8 def comment
9   - @comment ||= Comment.new(JSON.parse(self.comment_attributes)) unless self.comment_attributes.nil?
  9 + @comment ||= Comment.new(ActiveSupport::JSON.decode(self.comment_attributes)) unless self.comment_attributes.nil?
10 10 end
11 11  
12 12 def requestor_name
... ... @@ -42,9 +42,9 @@ class ApproveComment &lt; Task
42 42 def information
43 43 if article
44 44 if requestor
45   - {:message => _('%{requestor} commented on the the article: %{linked_subject}.')}
  45 + {:message => _('%{requestor} commented on the article: %{linked_subject}.')}
46 46 else
47   - { :message => _('%{requestor} commented on the the article: %{linked_subject}.'),
  47 + { :message => _('%{requestor} commented on the article: %{linked_subject}.'),
48 48 :variables => {:requestor => requestor_name} }
49 49 end
50 50 else
... ...
app/models/article.rb
... ... @@ -4,6 +4,8 @@ class Article &lt; ActiveRecord::Base
4 4  
5 5 attr_accessible :name, :body, :abstract, :profile, :tag_list, :parent
6 6  
  7 + acts_as_having_image
  8 +
7 9 SEARCHABLE_FIELDS = {
8 10 :name => 10,
9 11 :abstract => 3,
... ... @@ -59,7 +61,7 @@ class Article &lt; ActiveRecord::Base
59 61 has_many :article_categorizations, :conditions => [ 'articles_categories.virtual = ?', false ]
60 62 has_many :categories, :through => :article_categorizations
61 63  
62   - has_many :article_categorizations_including_virtual, :class_name => 'ArticleCategorization', :dependent => :destroy
  64 + has_many :article_categorizations_including_virtual, :class_name => 'ArticleCategorization'
63 65 has_many :categories_including_virtual, :through => :article_categorizations_including_virtual, :source => :category
64 66  
65 67 acts_as_having_settings :field => :setting
... ... @@ -156,8 +158,12 @@ class Article &lt; ActiveRecord::Base
156 158 end
157 159 end
158 160  
  161 + def css_class_list
  162 + [self.class.name.to_css_class]
  163 + end
  164 +
159 165 def css_class_name
160   - self.class.name.underscore.dasherize
  166 + [css_class_list].flatten.compact.join(' ')
161 167 end
162 168  
163 169 def pending_categorizations
... ... @@ -190,7 +196,7 @@ class Article &lt; ActiveRecord::Base
190 196 pending_categorizations.clear
191 197 end
192 198  
193   - acts_as_taggable
  199 + acts_as_taggable
194 200 N_('Tag list')
195 201  
196 202 acts_as_filesystem
... ... @@ -268,7 +274,7 @@ class Article &lt; ActiveRecord::Base
268 274 end
269 275  
270 276 # returns the data of the article. Must be overriden in each subclass to
271   - # provide the correct content for the article.
  277 + # provide the correct content for the article.
272 278 def data
273 279 body
274 280 end
... ... @@ -282,6 +288,11 @@ class Article &lt; ActiveRecord::Base
282 288 'text-html'
283 289 end
284 290  
  291 + # TODO Migrate the class method icon_name to instance methods.
  292 + def icon_name
  293 + self.class.icon_name(self)
  294 + end
  295 +
285 296 def mime_type
286 297 'text/html'
287 298 end
... ... @@ -310,6 +321,10 @@ class Article &lt; ActiveRecord::Base
310 321 def belongs_to_blog?
311 322 self.parent and self.parent.blog?
312 323 end
  324 +
  325 + def belongs_to_forum?
  326 + self.parent and self.parent.forum?
  327 + end
313 328  
314 329 def info_from_last_update
315 330 last_comment = comments.last
... ... @@ -325,7 +340,7 @@ class Article &lt; ActiveRecord::Base
325 340 end
326 341  
327 342 def view_url
328   - @view_url ||= image? ? url.merge(:view => true) : url
  343 + @view_url ||= is_a?(UploadedFile) ? url.merge(:view => true) : url
329 344 end
330 345  
331 346 def comment_url_structure(comment, action = :edit)
... ... @@ -340,24 +355,18 @@ class Article &lt; ActiveRecord::Base
340 355 true
341 356 end
342 357  
343   - def folder?
344   - false
345   - end
346   -
347   - def blog?
348   - false
349   - end
350   -
351   - def forum?
  358 + def has_posts?
352 359 false
353 360 end
354 361  
355   - def uploaded_file?
356   - false
  362 + def download? view = nil
  363 + (self.uploaded_file? and not self.image?) or
  364 + (self.image? and view.blank?) or
  365 + (not self.uploaded_file? and self.mime_type != 'text/html')
357 366 end
358 367  
359   - def has_posts?
360   - false
  368 + def download_headers
  369 + {}
361 370 end
362 371  
363 372 scope :native_translations, :conditions => { :translation_of_id => nil }
... ... @@ -444,6 +453,7 @@ class Article &lt; ActiveRecord::Base
444 453 scope :galleries, :conditions => { :type => 'Gallery' }
445 454 scope :images, :conditions => { :is_image => true }
446 455 scope :text_articles, :conditions => [ 'articles.type IN (?)', text_article_types ]
  456 + scope :with_types, lambda { |types| { :conditions => [ 'articles.type IN (?)', types ] } }
447 457  
448 458 scope :more_popular, :order => 'hits DESC'
449 459 scope :more_comments, :order => "comments_count DESC"
... ... @@ -586,6 +596,22 @@ class Article &lt; ActiveRecord::Base
586 596 false
587 597 end
588 598  
  599 + def folder?
  600 + false
  601 + end
  602 +
  603 + def blog?
  604 + false
  605 + end
  606 +
  607 + def forum?
  608 + false
  609 + end
  610 +
  611 + def uploaded_file?
  612 + false
  613 + end
  614 +
589 615 def author
590 616 if versions.empty?
591 617 last_changed_by
... ... @@ -725,6 +751,10 @@ class Article &lt; ActiveRecord::Base
725 751  
726 752 delegate :region, :region_id, :environment, :environment_id, :to => :profile, :allow_nil => true
727 753  
  754 + def has_macro?
  755 + true
  756 + end
  757 +
728 758 private
729 759  
730 760 def sanitize_tag_list
... ...
app/models/article_block.rb
... ... @@ -12,7 +12,7 @@ class ArticleBlock &lt; Block
12 12 block = self
13 13 proc do
14 14 block_title(block.title) +
15   - (block.article ? article_to_html(block.article,
  15 + (block.article ? article_to_html(FilePresenter.for(block.article),
16 16 :gallery_view => false,
17 17 :inside_block => block, # For Blogs and folders
18 18 :format => block.visualization_format # For Articles and contents
... ... @@ -23,7 +23,7 @@ class ArticleBlock &lt; Block
23 23 def article_id
24 24 self.settings[:article_id]
25 25 end
26   -
  26 +
27 27 def article_id= value
28 28 self.settings[:article_id] = value.blank? ? nil : value.to_i
29 29 end
... ... @@ -49,8 +49,8 @@ class ArticleBlock &lt; Block
49 49 end
50 50  
51 51 def available_articles
52   - return [] if self.box.nil? or self.box.owner.nil?
53   - self.box.owner.kind_of?(Environment) ? self.box.owner.portal_community.articles : self.box.owner.articles
  52 + return [] if self.owner.nil?
  53 + self.owner.kind_of?(Environment) ? self.owner.portal_community.articles : self.owner.articles
54 54 end
55 55  
56 56 def posts_per_page
... ... @@ -63,4 +63,9 @@ class ArticleBlock &lt; Block
63 63 end
64 64  
65 65 settings_items :visualization_format, :type => :string, :default => 'short'
  66 +
  67 + def self.expire_on
  68 + { :profile => [:article], :environment => [:article] }
  69 + end
  70 +
66 71 end
... ...
app/models/block.rb
... ... @@ -140,4 +140,19 @@ class Block &lt; ActiveRecord::Base
140 140 4.hours
141 141 end
142 142  
  143 + def has_macro?
  144 + false
  145 + end
  146 +
  147 + # Override in your subclasses.
  148 + # Define which events and context should cause the block cache to expire
  149 + # Possible events are: :article, :profile, :friendship, :category
  150 + # Possible contexts are: :profile, :environment
  151 + def self.expire_on
  152 + {
  153 + :profile => [],
  154 + :environment => []
  155 + }
  156 + end
  157 +
143 158 end
... ...
app/models/blog_archives_block.rb
... ... @@ -45,4 +45,7 @@ class BlogArchivesBlock &lt; Block
45 45 content_tag('div', link_to(_('Subscribe RSS Feed'), owner_blog.feed.url), :class => 'subscribe-feed')
46 46 end
47 47  
  48 + def self.expire_on
  49 + { :profile => [:article], :environment => [:article] }
  50 + end
48 51 end
... ...
app/models/box.rb
... ... @@ -3,12 +3,15 @@ class Box &lt; ActiveRecord::Base
3 3 acts_as_list :scope => 'owner_id = #{owner_id} and owner_type = \'#{owner_type}\''
4 4 has_many :blocks, :dependent => :destroy, :order => 'position'
5 5  
  6 + include Noosfero::Plugin::HotSpot
  7 +
6 8 def environment
7 9 owner ? (owner.kind_of?(Environment) ? owner : owner.environment) : nil
8 10 end
9 11  
10 12 def acceptable_blocks
11   - to_css_class_name central? ? Box.acceptable_center_blocks : Box.acceptable_side_blocks
  13 + blocks_classes = central? ? Box.acceptable_center_blocks + plugins.dispatch(:extra_blocks, :position => 1) : Box.acceptable_side_blocks + plugins.dispatch(:extra_blocks, :position => [2, 3])
  14 + to_css_class_name(blocks_classes)
12 15 end
13 16  
14 17 def central?
... ... @@ -74,8 +77,8 @@ class Box &lt; ActiveRecord::Base
74 77  
75 78 private
76 79  
77   - def to_css_class_name(blocks)
78   - blocks.map{ |block| block.to_s.underscore.tr('_', '-') }
  80 + def to_css_class_name(blocks_classes)
  81 + blocks_classes.map{ |block_class| block_class.name.to_css_class }
79 82 end
80 83  
81 84 end
... ...
app/models/categories_block.rb
... ... @@ -35,4 +35,7 @@ class CategoriesBlock &lt; Block
35 35 end
36 36 end
37 37  
  38 + def self.expire_on
  39 + { :profile => [], :environment => [:category] }
  40 + end
38 41 end
... ...
app/models/category.rb
... ... @@ -9,13 +9,13 @@ class Category &lt; ActiveRecord::Base
9 9 :slug => 1,
10 10 }
11 11  
12   - validates_exclusion_of :slug, :in => [ 'index' ], :message => N_('%{fn} cannot be like that.').fix_i18n
  12 + validates_exclusion_of :slug, :in => [ 'index' ], :message => N_('{fn} cannot be like that.').fix_i18n
13 13 validates_presence_of :name, :environment_id
14   - validates_uniqueness_of :slug,:scope => [ :environment_id, :parent_id ], :message => N_('%{fn} is already being used by another category.').fix_i18n
  14 + validates_uniqueness_of :slug,:scope => [ :environment_id, :parent_id ], :message => N_('{fn} is already being used by another category.').fix_i18n
15 15 belongs_to :environment
16 16  
17   - validates_inclusion_of :display_color, :in => [ 1, 2, 3, 4, nil ]
18   - validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.').fix_i18n
  17 + validates_inclusion_of :display_color, :in => 1..15, :allow_nil => true
  18 + validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('{fn} was already assigned to another category.').fix_i18n
19 19  
20 20 # Finds all top level categories for a given environment.
21 21 scope :top_level_for, lambda { |environment|
... ... @@ -24,23 +24,15 @@ class Category &lt; ActiveRecord::Base
24 24  
25 25 scope :on_level, lambda { |parent| {:conditions => {:parent_id => parent}} }
26 26  
27   - scope :sub_categories, lambda { |category|
28   - {:conditions => ['categories.path LIKE ? AND categories.id != ?', "%#{category.slug}%", category.id]}
29   - }
30   -
31   - scope :sub_tree, lambda { |category|
32   - {:conditions => ['categories.path LIKE ?', "%#{category.slug}%"]}
33   - }
34   -
35 27 acts_as_filesystem
36 28  
37   - has_many :article_categorizations, :dependent => :destroy
  29 + has_many :article_categorizations
38 30 has_many :articles, :through => :article_categorizations
39 31 has_many :comments, :through => :articles
40 32  
41 33 has_many :events, :through => :article_categorizations, :class_name => 'Event', :source => :article
42 34  
43   - has_many :profile_categorizations, :dependent => :destroy
  35 + has_many :profile_categorizations
44 36 has_many :profiles, :through => :profile_categorizations, :source => :profile
45 37 has_many :enterprises, :through => :profile_categorizations, :source => :profile, :class_name => 'Enterprise'
46 38 has_many :people, :through => :profile_categorizations, :source => :profile, :class_name => 'Person'
... ...
app/models/change_password.rb
1 1 class ChangePassword < Task
2 2  
3   - attr_accessor :login, :email, :password, :password_confirmation, :environment_id
  3 + attr_accessor :password, :password_confirmation
4 4  
5 5 def self.human_attribute_name(attrib)
6 6 case attrib.to_sym
7   - when :login
8   - _('Username')
9   - when :email
10   - _('e-mail')
11 7 when :password
12 8 _('Password')
13 9 when :password_confirmation
... ... @@ -17,29 +13,7 @@ class ChangePassword &lt; Task
17 13 end
18 14 end
19 15  
20   - ###################################################
21   - # validations for creating a ChangePassword task
22   -
23   - validates_presence_of :login, :email, :environment_id, :on => :create, :message => _('must be filled in')
24   -
25   - validates_format_of :email, :on => :create, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda { |obj| !obj.email.blank? })
26   -
27   - validates_each :login, :on => :create do |data,attr,value|
28   - unless data.login.blank? || data.email.blank?
29   - user = User.find_by_login_and_environment_id(data.login, data.environment_id)
30   - if user.nil?
31   - data.errors.add(:login, _('is invalid or user does not exists.'))
32   - else
33   - if user.email != data.email
34   - data.errors.add(:email, _('does not match the username you filled in'))
35   - end
36   - end
37   - end
38   - end
39   -
40   - before_validation(:on => :create) do |change_password|
41   - change_password.requestor = Person.find_by_identifier_and_environment_id(change_password.login, change_password.environment_id)
42   - end
  16 + validates_presence_of :requestor
43 17  
44 18 ###################################################
45 19 # validations for updating a ChangePassword task
... ... @@ -49,6 +23,10 @@ class ChangePassword &lt; Task
49 23 validates_presence_of :password_confirmation, :on => :update, :if => lambda { |change| change.status != Task::Status::CANCELLED }
50 24 validates_confirmation_of :password, :if => lambda { |change| change.status != Task::Status::CANCELLED }
51 25  
  26 + def environment
  27 + requestor.environment unless requestor.nil?
  28 + end
  29 +
52 30 def title
53 31 _("Change password")
54 32 end
... ... @@ -87,12 +65,8 @@ class ChangePassword &lt; Task
87 65 url = url_for(:host => hostname, :controller => 'account', :action => 'new_password', :code => code)
88 66  
89 67 proc do
90   - _("In order to change your password, please visit the following address:\n\n%s") % url
  68 + _("In order to change your password, please visit the following address:\n\n%s\n\nIf you did not required any change to your password just desconsider this email.") % url
91 69 end
92 70 end
93 71  
94   - def environment
95   - self.requestor.environment
96   - end
97   -
98 72 end
... ...
app/models/comment.rb
... ... @@ -16,9 +16,7 @@ class Comment &lt; ActiveRecord::Base
16 16 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
17 17 belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id'
18 18  
19   - scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false]
20 19 scope :without_reply, :conditions => ['reply_of_id IS NULL']
21   - scope :spam, :conditions => ['spam = ?', true]
22 20  
23 21 # unauthenticated authors:
24 22 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? })
... ... @@ -29,7 +27,7 @@ class Comment &lt; ActiveRecord::Base
29 27 validates_presence_of :author_id, :if => (lambda { |rec| rec.name.blank? && rec.email.blank? })
30 28 validates_each :name do |rec,attribute,value|
31 29 if rec.author_id && (!rec.name.blank? || !rec.email.blank?)
32   - rec.errors.add(:name, _('%{fn} can only be informed for unauthenticated authors').fix_i18n)
  30 + rec.errors.add(:name, _('{fn} can only be informed for unauthenticated authors').fix_i18n)
33 31 end
34 32 end
35 33  
... ... @@ -104,10 +102,21 @@ class Comment &lt; ActiveRecord::Base
104 102 end
105 103  
106 104 delegate :environment, :to => :profile
107   - delegate :profile, :to => :source
  105 + delegate :profile, :to => :source, :allow_nil => true
108 106  
109 107 include Noosfero::Plugin::HotSpot
110 108  
  109 + include Spammable
  110 +
  111 + def after_spam!
  112 + SpammerLogger.log(ip_address, self)
  113 + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam))
  114 + end
  115 +
  116 + def after_ham!
  117 + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_ham))
  118 + end
  119 +
111 120 def verify_and_notify
112 121 check_for_spam
113 122 unless spam?
... ... @@ -115,10 +124,6 @@ class Comment &lt; ActiveRecord::Base
115 124 end
116 125 end
117 126  
118   - def check_for_spam
119   - plugins.dispatch(:check_comment_for_spam, self)
120   - end
121   -
122 127 def notify_by_mail
123 128 if source.kind_of?(Article) && article.notify_comments?
124 129 if !notification_emails.empty?
... ... @@ -171,37 +176,6 @@ class Comment &lt; ActiveRecord::Base
171 176 @rejected = true
172 177 end
173 178  
174   - def spam?
175   - !spam.nil? && spam
176   - end
177   -
178   - def ham?
179   - !spam.nil? && !spam
180   - end
181   -
182   - def spam!
183   - self.spam = true
184   - self.save!
185   - SpammerLogger.log(ip_address, self)
186   - Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam))
187   - self
188   - end
189   -
190   - def ham!
191   - self.spam = false
192   - self.save!
193   - Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_ham))
194   - self
195   - end
196   -
197   - def marked_as_spam
198   - plugins.dispatch(:comment_marked_as_spam, self)
199   - end
200   -
201   - def marked_as_ham
202   - plugins.dispatch(:comment_marked_as_ham, self)
203   - end
204   -
205 179 def need_moderation?
206 180 article.moderate_comments? && (author.nil? || article.author != author)
207 181 end
... ...
app/models/create_enterprise.rb
... ... @@ -42,14 +42,14 @@ class CreateEnterprise &lt; Task
42 42 def validator_correct_region
43 43 if self.region && self.target
44 44 unless self.region.validators.include?(self.target) || self.target_type == "Environment"
45   - self.errors.add(:target, _('%{fn} is not a validator for the chosen region').fix_i18n)
  45 + self.errors.add(:target, _('{fn} is not a validator for the chosen region').fix_i18n)
46 46 end
47 47 end
48 48 end
49 49  
50 50 def not_used_identifier
51 51 if self.status != Task::Status::CANCELLED && self.identifier && Profile.exists?(:identifier => self.identifier)
52   - self.errors.add(:identifier, _('%{fn} is already being as identifier by another enterprise, organization or person.').fix_i18n)
  52 + self.errors.add(:identifier, _('{fn} is already being as identifier by another enterprise, organization or person.').fix_i18n)
53 53 end
54 54 end
55 55  
... ...
app/models/domain.rb
... ... @@ -14,7 +14,7 @@ class Domain &lt; ActiveRecord::Base
14 14  
15 15 # <tt>name</tt> must be sequences of alphanumeric characters (a to z,
16 16 # 0 to 9), plus '_' or '-', separated by dots. Letters must be lowercase.
17   - validates_format_of :name, :with => /^([a-z0-9_-]+\.)+[a-z0-9_-]+$/, :message => N_('%{fn} must be composed of sequences of lowercase letters (a to z), numbers (0 to 9), "_" and "-", separated by dots.').fix_i18n
  17 + validates_format_of :name, :with => /^([a-z0-9_-]+\.)+[a-z0-9_-]+$/, :message => N_('{fn} must be composed of sequences of lowercase letters (a to z), numbers (0 to 9), "_" and "-", separated by dots.').fix_i18n
18 18  
19 19 # checks validations that could not be expressed using Rails' predefined
20 20 # validations. In particular:
... ... @@ -23,7 +23,7 @@ class Domain &lt; ActiveRecord::Base
23 23  
24 24 def no_www
25 25 if self.name =~ /^www\./
26   - self.errors.add(:name, _('%{fn} must not start with www.').fix_i18n)
  26 + self.errors.add(:name, _('{fn} must not start with www.').fix_i18n)
27 27 end
28 28 end
29 29  
... ...
app/models/enterprise.rb
... ... @@ -135,7 +135,7 @@ class Enterprise &lt; Organization
135 135 [ProfileImageBlock.new, LinkListBlock.new(:links => links)],
136 136 []
137 137 ]
138   - if !environment.enabled?('disable_products_for_enterprises')
  138 + if environment.enabled?('products_for_enterprises')
139 139 blocks[2].unshift ProductsBlock.new
140 140 end
141 141 blocks
... ...
app/models/environment.rb
... ... @@ -97,7 +97,6 @@ class Environment &lt; ActiveRecord::Base
97 97 'disable_asset_communities' => _('Disable search for communities'),
98 98 'disable_asset_products' => _('Disable search for products'),
99 99 'disable_asset_events' => _('Disable search for events'),
100   - 'disable_products_for_enterprises' => _('Disable products for enterprises'),
101 100 'disable_categories' => _('Disable categories'),
102 101 'disable_header_and_footer' => _('Disable header/footer editing by users'),
103 102 'disable_gender_icon' => _('Disable gender icon'),
... ... @@ -105,9 +104,13 @@ class Environment &lt; ActiveRecord::Base
105 104 'disable_select_city_for_contact' => _('Disable state/city select for contact form'),
106 105 'disable_contact_person' => _('Disable contact for people'),
107 106 'disable_contact_community' => _('Disable contact for groups/communities'),
108   - 'enterprise_registration' => _('Enterprise registration'),
109 107  
110   - 'enterprise_activation' => _('Enable activation of enterprises'),
  108 + 'products_for_enterprises' => __('Enable products for enterprises'),
  109 + 'enterprise_registration' => __('Enterprise registration'),
  110 + 'enterprise_activation' => __('Enable activation of enterprises'),
  111 + 'enterprises_are_disabled_when_created' => __('Enterprises are disabled when created'),
  112 + 'enterprises_are_validated_when_created' => __('Enterprises are validated when created'),
  113 +
111 114 'media_panel' => _('Media panel in WYSIWYG editor'),
112 115 'select_preferred_domain' => _('Select preferred domains per profile'),
113 116 'use_portal_community' => _('Use the portal as news source for front page'),
... ... @@ -120,15 +123,15 @@ class Environment &lt; ActiveRecord::Base
120 123 'organizations_are_moderated_by_default' => _("Organizations have moderated publication by default"),
121 124 'enable_organization_url_change' => _("Allow organizations to change their URL"),
122 125 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"),
123   - 'enterprises_are_disabled_when_created' => _('Enterprises are disabled when created'),
124   - 'enterprises_are_validated_when_created' => _('Enterprises are validated when created'),
125 126 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'),
126 127 'xmpp_chat' => _('XMPP/Jabber based chat'),
127 128 'show_zoom_button_on_article_images' => _('Show a zoom link on all article images'),
128 129 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'),
129 130 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users'),
130 131 'send_welcome_email_to_new_users' => _('Send welcome e-mail to new users'),
131   - 'allow_change_of_redirection_after_login' => _('Allow users to set the page to redirect after login')
  132 + 'allow_change_of_redirection_after_login' => _('Allow users to set the page to redirect after login'),
  133 + 'display_my_communities_on_user_menu' => _('Display on menu the list of communities the user can manage'),
  134 + 'display_my_enterprises_on_user_menu' => _('Display on menu the list of enterprises the user can manage')
132 135 }
133 136 end
134 137  
... ... @@ -169,7 +172,7 @@ class Environment &lt; ActiveRecord::Base
169 172  
170 173 # One Environment can be reached by many domains
171 174 has_many :domains, :as => :owner
172   - has_many :profiles
  175 + has_many :profiles, :dependent => :destroy
173 176  
174 177 has_many :organizations
175 178 has_many :enterprises
... ... @@ -277,7 +280,7 @@ class Environment &lt; ActiveRecord::Base
277 280 settings_items :restrict_to_access_control_origins, :default => false
278 281 # Set this according to http://www.w3.org/TR/cors/. Headers are set at every response
279 282 # For multiple domains acts as suggested in http://stackoverflow.com/questions/1653308/access-control-allow-origin-multiple-origin-domains
280   - settings_items :access_control_allow_origin, :type => Array
  283 + settings_items :access_control_allow_origin, :type => Array, :default => []
281 284 settings_items :access_control_allow_methods, :type => String
282 285  
283 286 def news_amount_by_folder=(amount)
... ... @@ -285,8 +288,9 @@ class Environment &lt; ActiveRecord::Base
285 288 end
286 289  
287 290 # Enables a feature identified by its name
288   - def enable(feature)
  291 + def enable(feature, must_save=true)
289 292 self.settings["#{feature}_enabled".to_sym] = true
  293 + self.save! if must_save
290 294 end
291 295  
292 296 def enable_plugin(plugin)
... ... @@ -296,8 +300,9 @@ class Environment &lt; ActiveRecord::Base
296 300 end
297 301  
298 302 # Disables a feature identified by its name
299   - def disable(feature)
  303 + def disable(feature, must_save=true)
300 304 self.settings["#{feature}_enabled".to_sym] = false
  305 + self.save! if must_save
301 306 end
302 307  
303 308 def disable_plugin(plugin)
... ... @@ -341,7 +346,7 @@ class Environment &lt; ActiveRecord::Base
341 346 %w(
342 347 disable_asset_products
343 348 disable_gender_icon
344   - disable_products_for_enterprises
  349 + products_for_enterprises
345 350 disable_select_city_for_contact
346 351 enterprise_registration
347 352 media_panel
... ... @@ -349,7 +354,7 @@ class Environment &lt; ActiveRecord::Base
349 354 show_balloon_with_profile_links_when_clicked
350 355 use_portal_community
351 356 ).each do |feature|
352   - enable(feature)
  357 + enable(feature, false)
353 358 end
354 359 end
355 360  
... ... @@ -802,13 +807,6 @@ class Environment &lt; ActiveRecord::Base
802 807 self.save!
803 808 end
804 809  
805   - after_destroy :destroy_templates
806   - def destroy_templates
807   - [enterprise_template, inactive_enterprise_template, community_template, person_template].compact.each do |template|
808   - template.destroy
809   - end
810   - end
811   -
812 810 after_create :create_default_licenses
813 811 def create_default_licenses
814 812 [
... ...
app/models/event.rb
... ... @@ -30,12 +30,30 @@ class Event &lt; Article
30 30  
31 31 validates_each :start_date do |event,field,value|
32 32 if event.end_date && event.start_date && event.start_date > event.end_date
33   - event.errors.add(:start_date, _('%{fn} cannot come before end date.').fix_i18n)
  33 + event.errors.add(:start_date, _('{fn} cannot come before end date.').fix_i18n)
34 34 end
35 35 end
36 36  
37 37 scope :by_day, lambda { |date|
38   - {:conditions => ['start_date = :date AND end_date IS NULL OR (start_date <= :date AND end_date >= :date)', {:date => date}]}
  38 + { :conditions => ['start_date = :date AND end_date IS NULL OR (start_date <= :date AND end_date >= :date)', {:date => date}],
  39 + :order => 'start_date ASC'
  40 + }
  41 + }
  42 +
  43 + scope :next_events_from_month, lambda { |date|
  44 + date_temp = date.strftime("%Y-%m-%d")
  45 + { :conditions => ["start_date >= ?","#{date_temp}"],
  46 + :limit => 10,
  47 + :order => 'start_date ASC'
  48 + }
  49 + }
  50 +
  51 + scope :by_month, lambda { |date|
  52 + date_temp = date.strftime("%Y-%m")
  53 + { :conditions => ["EXTRACT(YEAR FROM start_date) = ? AND EXTRACT(MONTH FROM start_date) = ?",date.year,date.month],
  54 + :limit => 10,
  55 + :order => 'start_date ASC'
  56 + }
39 57 }
40 58  
41 59 include WhiteListFilter
... ... @@ -112,7 +130,7 @@ class Event &lt; Article
112 130  
113 131 # TODO: some good soul, please clean this ugly hack:
114 132 if self.body
115   - html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description')
  133 + html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description')
116 134 end
117 135 }
118 136  
... ...
app/models/featured_products_block.rb
... ... @@ -7,8 +7,9 @@ class FeaturedProductsBlock &lt; Block
7 7  
8 8 before_save do |block|
9 9 if block.owner.kind_of?(Environment) && block.product_ids.blank?
10   - seed = block.owner.products.count
11   - block.product_ids = block.owner.highlighted_products_with_image(:offset => (rand(seed) % (seed - block.groups_of * 3)), :limit => block.groups_of * 3).map(&:id)
  10 + total = block.owner.products.count
  11 + offset = rand([(total - block.groups_of * 3) + 1, 1].max)
  12 + block.product_ids = block.owner.highlighted_products_with_image(:offset => offset, :limit => block.groups_of * 3).map(&:id)
12 13 end
13 14 block.groups_of = block.groups_of.to_i
14 15 end
... ...
app/models/image.rb
... ... @@ -17,7 +17,7 @@ class Image &lt; ActiveRecord::Base
17 17 :icon => '20x20!' },
18 18 :max_size => 5.megabytes # remember to update validate message below
19 19  
20   - validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n
  20 + validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n
21 21  
22 22 delay_attachment_fu_thumbnails
23 23  
... ...
app/models/link_list_block.rb
... ... @@ -35,6 +35,12 @@ class LinkListBlock &lt; Block
35 35 ['chat', N_('Chat')]
36 36 ]
37 37  
  38 + TARGET_OPTIONS = [
  39 + [N_('Same page'), '_self'],
  40 + [N_('New tab'), '_blank'],
  41 + [N_('New window'), '_new'],
  42 + ]
  43 +
38 44 settings_items :links, Array, :default => []
39 45  
40 46 before_save do |block|
... ... @@ -59,7 +65,7 @@ class LinkListBlock &lt; Block
59 65 def link_html(link)
60 66 klass = 'icon-' + link[:icon] if link[:icon]
61 67 sanitize_link(
62   - link_to(link[:name], expand_address(link[:address]), :class => klass)
  68 + link_to(link[:name], expand_address(link[:address]), :target => link[:target], :class => klass)
63 69 )
64 70 end
65 71  
... ...
app/models/person.rb
... ... @@ -32,6 +32,10 @@ class Person &lt; Profile
32 32 Profile.memberships_of(self)
33 33 end
34 34  
  35 + def memberships_by_role(role)
  36 + memberships.where('role_assignments.role_id = ?', role.id)
  37 + end
  38 +
35 39 has_many :friendships, :dependent => :destroy
36 40 has_many :friends, :class_name => 'Person', :through => :friendships
37 41  
... ... @@ -61,6 +65,10 @@ class Person &lt; Profile
61 65 scope :abusers, :joins => :abuse_complaints, :conditions => ['tasks.status = 3'], :select => 'DISTINCT profiles.*'
62 66 scope :non_abusers, :joins => "LEFT JOIN tasks ON profiles.id = tasks.requestor_id AND tasks.type='AbuseComplaint'", :conditions => ["tasks.status != 3 OR tasks.id is NULL"], :select => "DISTINCT profiles.*"
63 67  
  68 + scope :admins, :joins => [:role_assignments => :role], :conditions => ['roles.key = ?', 'environment_administrator' ]
  69 + scope :activated, :joins => :user, :conditions => ['users.activation_code IS NULL AND users.activated_at IS NOT NULL']
  70 + scope :deactivated, :joins => :user, :conditions => ['NOT (users.activation_code IS NULL AND users.activated_at IS NOT NULL)']
  71 +
64 72 after_destroy do |person|
65 73 Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy }
66 74 end
... ... @@ -230,7 +238,7 @@ class Person &lt; Profile
230 238  
231 239 validates_each :email, :on => :update do |record,attr,value|
232 240 if User.find(:first, :conditions => ['email = ? and id != ? and environment_id = ?', value, record.user.id, record.environment.id])
233   - record.errors.add(attr, _('%{fn} is already used by other user').fix_i18n)
  241 + record.errors.add(attr, _('{fn} is already used by other user').fix_i18n)
234 242 end
235 243 end
236 244  
... ... @@ -458,11 +466,19 @@ class Person &lt; Profile
458 466 end
459 467  
460 468 def activities
461   - Scrap.find_by_sql("SELECT id, updated_at, '#{Scrap.to_s}' AS klass FROM #{Scrap.table_name} WHERE scraps.receiver_id = #{self.id} AND scraps.scrap_id IS NULL UNION SELECT id, updated_at, '#{ActionTracker::Record.to_s}' AS klass FROM #{ActionTracker::Record.table_name} WHERE action_tracker.user_id = #{self.id} and action_tracker.verb != 'leave_scrap_to_self' and action_tracker.verb != 'add_member_in_community' ORDER BY updated_at DESC")
  469 + Scrap.find_by_sql("SELECT id, updated_at, '#{Scrap.to_s}' AS klass FROM #{Scrap.table_name} WHERE scraps.receiver_id = #{self.id} AND scraps.scrap_id IS NULL UNION SELECT id, updated_at, '#{ActionTracker::Record.to_s}' AS klass FROM #{ActionTracker::Record.table_name} WHERE action_tracker.user_id = #{self.id} and action_tracker.verb != 'leave_scrap_to_self' and action_tracker.verb != 'add_member_in_community' and action_tracker.verb != 'reply_scrap_on_self' ORDER BY updated_at DESC")
462 470 end
463 471  
  472 + # by default, all fields are private
464 473 def public_fields
465   - self.fields_privacy.nil? ? self.active_fields : self.fields_privacy.reject{ |k, v| v != 'public' }.keys.map(&:to_s)
  474 + self.fields_privacy.nil? ? [] : self.fields_privacy.reject{ |k, v| v != 'public' }.keys.map(&:to_s)
  475 + end
  476 +
  477 + include Noosfero::Gravatar
  478 +
  479 + def profile_custom_icon(gravatar_default=nil)
  480 + (self.image.present? && self.image.public_filename(:icon)) ||
  481 + gravatar_profile_image_url(self.email, :size=>20, :d => gravatar_default)
466 482 end
467 483  
468 484 protected
... ...
app/models/price_detail.rb
... ... @@ -6,11 +6,11 @@ class PriceDetail &lt; ActiveRecord::Base
6 6 validates_presence_of :product_id
7 7  
8 8 belongs_to :production_cost
9   - validates_presence_of :production_cost
  9 + # Do not validates_presence_of production_cost. We may have undefined other costs.
10 10 validates_uniqueness_of :production_cost_id, :scope => :product_id
11 11  
12 12 def name
13   - production_cost.name
  13 + production_cost.nil? ? _('Other costs') : production_cost.name
14 14 end
15 15  
16 16 def price
... ...
app/models/product.rb
... ... @@ -205,11 +205,13 @@ class Product &lt; ActiveRecord::Base
205 205 (price - total_production_cost.to_f).zero?
206 206 end
207 207  
208   - def update_price_details(price_details)
209   - self.price_details.destroy_all
210   - price_details.each do |price_detail|
211   - self.price_details.create(price_detail)
  208 + def update_price_details(new_price_details)
  209 + price_details.destroy_all
  210 + new_price_details.each do |detail|
  211 + price_details.create(detail)
212 212 end
  213 + reload # to remove temporary duplicated price_details
  214 + price_details
213 215 end
214 216  
215 217 def price_description_percentage
... ...
app/models/profile.rb
... ... @@ -86,7 +86,8 @@ class Profile &lt; ActiveRecord::Base
86 86 #FIXME: these will work only if the subclass is already loaded
87 87 scope :enterprises, lambda { {:conditions => (Enterprise.send(:subclasses).map(&:name) << 'Enterprise').map { |klass| "profiles.type = '#{klass}'"}.join(" OR ")} }
88 88 scope :communities, lambda { {:conditions => (Community.send(:subclasses).map(&:name) << 'Community').map { |klass| "profiles.type = '#{klass}'"}.join(" OR ")} }
89   - scope :templates, lambda { |environment| { :conditions => {:is_template => true, :environment_id => environment.id} } }
  89 + scope :templates, {:conditions => {:is_template => true}}
  90 + scope :no_templates, {:conditions => {:is_template => false}}
90 91  
91 92 def members
92 93 scopes = plugins.dispatch_scopes(:organization_members, self)
... ... @@ -98,6 +99,14 @@ class Profile &lt; ActiveRecord::Base
98 99 members.count
99 100 end
100 101  
  102 + class << self
  103 + def count_with_distinct(*args)
  104 + options = args.last || {}
  105 + count_without_distinct(:id, {:distinct => true}.merge(options))
  106 + end
  107 + alias_method_chain :count, :distinct
  108 + end
  109 +
101 110 def members_by_role(role)
102 111 Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', role.id])
103 112 end
... ... @@ -111,6 +120,7 @@ class Profile &lt; ActiveRecord::Base
111 120 end
112 121  
113 122 scope :visible, :conditions => { :visible => true }
  123 + scope :public, :conditions => { :visible => true, :public_profile => true }
114 124 # Subclasses must override these methods
115 125 scope :more_popular
116 126 scope :more_active
... ... @@ -187,7 +197,7 @@ class Profile &lt; ActiveRecord::Base
187 197  
188 198 has_many :tasks, :dependent => :destroy, :as => 'target'
189 199  
190   - has_many :events, :source => 'articles', :class_name => 'Event', :order => 'name'
  200 + has_many :events, :source => 'articles', :class_name => 'Event', :order => 'start_date'
191 201  
192 202 def find_in_all_tasks(task_id)
193 203 begin
... ... @@ -220,12 +230,14 @@ class Profile &lt; ActiveRecord::Base
220 230  
221 231 belongs_to :region
222 232  
  233 + LOCATION_FIELDS = %w[address district city state country_name zip_code]
  234 +
223 235 def location(separator = ' - ')
224 236 myregion = self.region
225 237 if myregion
226 238 myregion.hierarchy.reverse.first(2).map(&:name).join(separator)
227 239 else
228   - %w[address district city state country_name zip_code ].map {|item| (self.respond_to?(item) && !self.send(item).blank?) ? self.send(item) : nil }.compact.join(separator)
  240 + LOCATION_FIELDS.map {|item| (self.respond_to?(item) && !self.send(item).blank?) ? self.send(item) : nil }.compact.join(separator)
229 241 end
230 242 end
231 243  
... ... @@ -292,7 +304,7 @@ class Profile &lt; ActiveRecord::Base
292 304 validate :valid_template
293 305  
294 306 def valid_template
295   - if template_id.present? and !template.is_template
  307 + if template_id.present? && template && !template.is_template
296 308 errors.add(:template, _('is not a template.'))
297 309 end
298 310 end
... ... @@ -843,8 +855,10 @@ private :generate_url, :url_options
843 855 }[amount] || _("%s members") % amount
844 856 end
845 857  
846   - def profile_custom_icon
847   - self.image.public_filename(:icon) unless self.image.blank?
  858 + include Noosfero::Gravatar
  859 +
  860 + def profile_custom_icon(gravatar_default=nil)
  861 + image.public_filename(:icon) if image.present?
848 862 end
849 863  
850 864 def jid(options = {})
... ... @@ -883,6 +897,21 @@ private :generate_url, :url_options
883 897 []
884 898 end
885 899  
  900 + def may_display_field_to? field, user = nil
  901 + if not self.active_fields.include? field.to_s
  902 + self.send "may_display_#{field}_to?", user rescue true
  903 + else
  904 + not (!self.public_fields.include? field.to_s and (!user or (user != self and !user.is_a_friend?(self))))
  905 + end
  906 + end
  907 +
  908 + def may_display_location_to? user = nil
  909 + LOCATION_FIELDS.each do |field|
  910 + return false if !self.may_display_field_to? field, user
  911 + end
  912 + return true
  913 + end
  914 +
886 915 # field => privacy (e.g.: "address" => "public")
887 916 def fields_privacy
888 917 self.data[:fields_privacy]
... ...
app/models/profile_list_block.rb
... ... @@ -56,7 +56,7 @@ class ProfileListBlock &lt; Block
56 56 list = content_tag 'ul', nl +' '+ list + nl
57 57 end
58 58 block_title(title) + nl +
59   - content_tag('div', nl + list + nl + content_tag('br', '', :style => 'clear:both'))
  59 + content_tag('div', nl + list + nl + tag('br', :style => 'clear:both'))
60 60 end
61 61 end
62 62  
... ...
app/models/raw_html_block.rb
... ... @@ -12,4 +12,7 @@ class RawHTMLBlock &lt; Block
12 12 (title.blank? ? '' : block_title(title)).html_safe + html.to_s.html_safe
13 13 end
14 14  
  15 + def has_macro?
  16 + true
  17 + end
15 18 end
... ...
app/models/recent_documents_block.rb
... ... @@ -32,8 +32,7 @@ class RecentDocumentsBlock &lt; Block
32 32 end
33 33 end
34 34  
35   - def docs
36   - self.limit.nil? ? owner.recent_documents(nil, {}, false) : owner.recent_documents(self.limit, {}, false)
  35 + def self.expire_on
  36 + { :profile => [:article], :environment => [:article] }
37 37 end
38   -
39 38 end
... ...
app/models/region.rb
... ... @@ -11,9 +11,9 @@ class Region &lt; Category
11 11 validators.count > 0
12 12 end
13 13  
14   - scope :with_validators, :group => 'id',
  14 + scope :with_validators, :select => 'DISTINCT ON (categories.id) *',
15 15 :joins => 'INNER JOIN region_validators on (region_validators.region_id = categories.id)'
16   -
  16 +
17 17 end
18 18  
19 19 require_dependency 'city'
... ...
app/models/scrap.rb
... ... @@ -17,9 +17,11 @@ class Scrap &lt; ActiveRecord::Base
17 17  
18 18 scope :not_replies, :conditions => {:scrap_id => nil}
19 19  
20   - track_actions :leave_scrap, :after_create, :keep_params => ['sender.name', 'content', 'receiver.name', 'receiver.url'], :if => Proc.new{|s| s.receiver != s.sender}, :custom_target => :action_tracker_target
  20 + track_actions :leave_scrap, :after_create, :keep_params => ['sender.name', 'content', 'receiver.name', 'receiver.url'], :if => Proc.new{|s| s.sender != s.receiver && s.sender != s.top_root.receiver}, :custom_target => :action_tracker_target
21 21  
22   - track_actions :leave_scrap_to_self, :after_create, :keep_params => ['sender.name', 'content'], :if => Proc.new{|s| s.receiver == s.sender}
  22 + track_actions :leave_scrap_to_self, :after_create, :keep_params => ['sender.name', 'content'], :if => Proc.new{|s| s.sender == s.receiver}
  23 +
  24 + track_actions :reply_scrap_on_self, :after_create, :keep_params => ['sender.name', 'content'], :if => Proc.new{|s| s.sender != s.receiver && s.sender == s.top_root.receiver}
23 25  
24 26 after_create do |scrap|
25 27 scrap.root.update_attribute('updated_at', DateTime.now) unless scrap.root.nil?
... ... @@ -28,6 +30,12 @@ class Scrap &lt; ActiveRecord::Base
28 30  
29 31 before_validation :strip_all_html_tags
30 32  
  33 + def top_root
  34 + scrap = self
  35 + scrap = Scrap.find(scrap.scrap_id) while scrap.scrap_id
  36 + scrap
  37 + end
  38 +
31 39 def strip_all_html_tags
32 40 sanitizer = HTML::WhiteListSanitizer.new
33 41 self.content = sanitizer.sanitize(self.content, :tags => [])
... ...
app/models/spammer_logger.rb
... ... @@ -6,6 +6,8 @@ class SpammerLogger &lt; Logger
6 6 if object
7 7 if object.kind_of?(Comment)
8 8 @logger << "[#{Time.now.strftime('%F %T %z')}] Comment-id: #{object.id} IP: #{spammer_ip}\n"
  9 + elsif object.kind_of?(SuggestArticle)
  10 + @logger << "[#{Time.now.strftime('%F %T %z')}] SuggestArticle-id: #{object.id} IP: #{spammer_ip}\n"
9 11 end
10 12 else
11 13 @logger << "[#{Time.now.strftime('%F %T %z')}] IP: #{spammer_ip}\n"
... ...
app/models/suggest_article.rb
... ... @@ -11,6 +11,17 @@ class SuggestArticle &lt; Task
11 11 settings_items :source, :type => String
12 12 settings_items :source_name, :type => String
13 13 settings_items :highlighted, :type => :boolean, :default => false
  14 + settings_items :ip_address, :type => String
  15 + settings_items :user_agent, :type => String
  16 + settings_items :referrer, :type => String
  17 +
  18 + after_create :schedule_spam_checking
  19 +
  20 + def schedule_spam_checking
  21 + self.delay.check_for_spam
  22 + end
  23 +
  24 + include Noosfero::Plugin::HotSpot
14 25  
15 26 def sender
16 27 "#{name} (#{email})"
... ... @@ -61,4 +72,12 @@ class SuggestArticle &lt; Task
61 72 _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name }
62 73 end
63 74  
  75 + def after_spam!
  76 + SpammerLogger.log(ip_address, self)
  77 + self.delay.marked_as_spam
  78 + end
  79 +
  80 + def after_ham!
  81 + self.delay.marked_as_ham
  82 + end
64 83 end
... ...
app/models/tags_block.rb
... ... @@ -59,4 +59,8 @@ class TagsBlock &lt; Block
59 59 15.minutes
60 60 end
61 61  
  62 + def self.expire_on
  63 + { :profile => [:article], :environment => [:article] }
  64 + end
  65 +
62 66 end
... ...
app/models/task.rb
... ... @@ -258,6 +258,8 @@ class Task &lt; ActiveRecord::Base
258 258 status == Task::Status::ACTIVE || status == Task::Status::HIDDEN
259 259 end
260 260  
  261 + include Spammable
  262 +
261 263 protected
262 264  
263 265 # This method must be overrided in subclasses, and its implementation must do
... ...
app/models/uploaded_file.rb
... ... @@ -45,7 +45,25 @@ class UploadedFile &lt; Article
45 45 end
46 46  
47 47 def self.max_size
48   - UploadedFile.attachment_options[:max_size]
  48 + default = 5.megabytes
  49 +
  50 + multipliers = {
  51 + :KB => :kilobytes,
  52 + :MB => :megabytes,
  53 + :GB => :gigabytes,
  54 + :TB => :terabytes,
  55 + }
  56 + max_upload_size = NOOSFERO_CONF['max_upload_size']
  57 +
  58 + if max_upload_size =~ /^(\d+(\.\d+)?)\s*(KB|MB|GB|TB)?$/
  59 + number = $1.to_f
  60 + unit = $3 || :MB
  61 + multiplier = multipliers[unit.to_sym]
  62 +
  63 + number.send(multiplier).to_i
  64 + else
  65 + default
  66 + end
49 67 end
50 68  
51 69 # FIXME need to define min/max file size
... ... @@ -56,20 +74,28 @@ class UploadedFile &lt; Article
56 74 has_attachment :storage => :file_system,
57 75 :thumbnails => { :icon => [24,24], :thumb => '130x130>', :slideshow => '320x240>', :display => '640X480>' },
58 76 :thumbnail_class => Thumbnail,
59   - :max_size => 5.megabytes # remember to update validate message below
  77 + :max_size => self.max_size
60 78  
61   - validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n
  79 + 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
62 80  
63 81 delay_attachment_fu_thumbnails
64 82  
65 83 postgresql_attachment_fu
66 84  
  85 + # Use this method only to get the generic icon for this kind of content.
  86 + # If you want the specific icon for a file type or the iconified version
  87 + # of an image, use FilePresenter.for(uploaded_file).icon_name
67 88 def self.icon_name(article = nil)
68   - if article
69   - article.image? ? article.public_filename(:icon) : (article.mime_type ? article.mime_type.gsub(/[\/+.]/, '-') : 'upload-file')
70   - else
71   - 'upload-file'
  89 + unless article.nil?
  90 + warn = ('='*80) + "\n" +
  91 + 'The method `UploadedFile.icon_name(obj)` is deprecated. ' +
  92 + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' +
  93 + "\n" + ('='*80)
  94 + raise NoMethodError, warn if ENV['RAILS_ENV'] == 'test'
  95 + Rails.logger.warn warn if Rails.logger
  96 + puts warn if ENV['RAILS_ENV'] == 'development'
72 97 end
  98 + 'upload-file'
73 99 end
74 100  
75 101 def mime_type
... ... @@ -90,45 +116,38 @@ class UploadedFile &lt; Article
90 116 self.name = self.filename
91 117 end
92 118  
  119 + def download_headers
  120 + {
  121 + 'Content-Disposition' => "attachment; filename=\"#{self.filename}\"",
  122 + }
  123 + end
  124 +
93 125 def data
94 126 File.read(self.full_filename)
95 127 end
96 128  
97 129 def to_html(options = {})
  130 + warn = ('='*80) + "\n" +
  131 + 'The method `UploadedFile#to_html()` is deprecated. ' +
  132 + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' +
  133 + "\n" + ('='*80)
  134 + raise NoMethodError, warn if ENV['RAILS_ENV'] == 'test'
  135 + Rails.logger.warn warn if Rails.logger
  136 + puts warn if ENV['RAILS_ENV'] == 'development'
98 137 article = self
99 138 if image?
100 139 proc do
101   - if article.gallery? && options[:gallery_view]
102   - images = article.parent.images
103   - current_index = images.index(article)
104   - total_of_images = images.count
105   -
106   - link_to_previous = if current_index >= 1
107   - link_to(_('&laquo; Previous'), images[current_index - 1].view_url, :class => 'left')
108   - else
109   - content_tag('span', _('&laquo; Previous'), :class => 'left')
110   - end
111   -
112   - link_to_next = if current_index < total_of_images - 1
113   - link_to(_('Next &raquo;'), images[current_index + 1].view_url, :class => 'right')
114   - else
115   - content_tag('span', _('Next &raquo;'), :class => 'right')
116   - end
117   -
118   - content_tag(
119   - 'div',
120   - link_to_previous + (content_tag('span', _('image %d of %d'), :class => 'total-of-images') % [current_index + 1, total_of_images]).html_safe + link_to_next,
121   - :class => 'gallery-navigation'
122   - )
123   - end.to_s +
124   - image_tag(article.public_filename(:display), :class => article.css_class_name, :style => 'max-width: 100%') +
125   - content_tag('p', article.abstract, :class => 'uploaded-file-description')
126   -
  140 + image_tag(article.public_filename(:display),
  141 + :class => article.css_class_name,
  142 + :style => 'max-width: 100%') +
  143 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
127 144 end
128 145 else
129 146 proc do
130   - content_tag('ul', content_tag('li', link_to(article.name, article.url, :class => article.css_class_name))) +
131   - content_tag('p', article.abstract, :class => 'uploaded-file-description')
  147 + content_tag('div',
  148 + link_to(article.name, article.url),
  149 + :class => article.css_class_name) +
  150 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
132 151 end
133 152 end
134 153 end
... ...
app/models/user.rb
... ... @@ -89,7 +89,7 @@ class User &lt; ActiveRecord::Base
89 89 before_save :encrypt_password
90 90 validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?})
91 91  
92   - validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('%{fn} must be checked in order to signup.').fix_i18n
  92 + validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('{fn} must be checked in order to signup.').fix_i18n
93 93  
94 94 # Authenticates a user by their login name or email and unencrypted password. Returns the user or nil.
95 95 def self.authenticate(login, password, environment = nil)
... ... @@ -118,6 +118,21 @@ class User &lt; ActiveRecord::Base
118 118 end
119 119 end
120 120  
  121 + # Deactivates the user in the database.
  122 + def deactivate
  123 + return false unless self.person
  124 + self.activated_at = nil
  125 + self.person.visible = false
  126 + begin
  127 + self.person.save! && self.save!
  128 + rescue Exception => exception
  129 + logger.error(exception.to_s)
  130 + false
  131 + else
  132 + true
  133 + end
  134 + end
  135 +
121 136 def activated?
122 137 self.activation_code.nil? && !self.activated_at.nil?
123 138 end
... ... @@ -250,19 +265,21 @@ class User &lt; ActiveRecord::Base
250 265 end
251 266 end
252 267  
253   - def data_hash
  268 + def data_hash(gravatar_default = nil)
254 269 friends_list = {}
255 270 enterprises = person.enterprises.map { |e| { 'name' => e.short_name, 'identifier' => e.identifier } }
256 271 self.person.friends.online.map do |person|
257 272 friends_list[person.identifier] = {
258   - 'avatar' => person.profile_custom_icon,
  273 + 'avatar' => person.profile_custom_icon(gravatar_default),
259 274 'name' => person.short_name,
260 275 'jid' => person.full_jid,
261 276 'status' => person.user.chat_status,
262 277 }
263 278 end
  279 +
264 280 {
265 281 'login' => self.login,
  282 + 'avatar' => self.person.profile_custom_icon(gravatar_default),
266 283 'is_admin' => self.person.is_admin?,
267 284 'since_month' => self.person.created_at.month,
268 285 'since_year' => self.person.created_at.year,
... ...
app/presenters/generic.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +# Made to encapsulate any UploadedFile
  2 +class FilePresenter::Generic < FilePresenter
  3 + # if returns low priority, because it is generic.
  4 + def self.accepts?(f)
  5 + 1 if f.is_a? UploadedFile
  6 + end
  7 +end
... ...
app/presenters/image.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +class FilePresenter::Image < FilePresenter
  2 + def self.accepts?(f)
  3 + return nil unless f.respond_to? :image?
  4 + f.image? ? 10 : nil
  5 + end
  6 +
  7 + def icon_name
  8 + public_filename :icon
  9 + end
  10 +
  11 + def short_description
  12 + _('Image (%s)') % content_type.split('/')[1].upcase
  13 + end
  14 +end
... ...
app/sweepers/article_sweeper.rb
... ... @@ -16,15 +16,15 @@ class ArticleSweeper &lt; ActiveRecord::Observer
16 16 end
17 17 end
18 18  
  19 +
19 20 protected
20 21  
21 22 def expire_caches(article)
  23 + expire_blocks_cache(article.profile, [:article])
  24 +
22 25 return if !article.environment
  26 +
23 27 article.hierarchy(true).each { |a| a.touch if a != article }
24   - blocks = article.profile.blocks
25   - blocks += article.profile.environment.blocks if article.profile.environment
26   - blocks = blocks.select{|b|[RecentDocumentsBlock, BlogArchivesBlock].any?{|c| b.kind_of?(c)}}
27   - BlockSweeper.expire_blocks(blocks)
28 28 env = article.profile.environment
29 29 if env && (env.portal_community == article.profile)
30 30 article.environment.locales.keys.each do |locale|
... ...
app/sweepers/category_sweeper.rb
... ... @@ -3,7 +3,13 @@ class CategorySweeper &lt; ActiveRecord::Observer
3 3 include SweeperHelper
4 4  
5 5 def after_save(category)
  6 + expire_blocks_cache(category.environment, [:category])
  7 +
  8 + # Needed for environments with application layout
6 9 expire_fragment(category.environment.id.to_s + "_categories_menu")
7 10 end
8 11  
  12 + def after_destroy(category)
  13 + expire_blocks_cache(category.environment, [:category])
  14 + end
9 15 end
... ...
app/sweepers/profile_sweeper.rb
... ... @@ -4,7 +4,7 @@ class ProfileSweeper # &lt; ActiveRecord::Observer
4 4 include SweeperHelper
5 5  
6 6 def after_update(profile)
7   - expire_caches(profile)
  7 + self.delay.expire_caches profile
8 8 end
9 9  
10 10 def after_create(profile)
... ...
app/views/account/_signup_form.html.erb
... ... @@ -101,7 +101,7 @@
101 101  
102 102 <%= @plugins.dispatch(:signup_extra_contents).collect { |content| instance_eval(&content) }.join("") %>
103 103  
104   - <%= template_options(Person, 'profile_data') %>
  104 + <%= template_options(:people, 'profile_data') %>
105 105  
106 106 <% unless @terms_of_use.blank? %>
107 107 <div id='terms-of-use-box' class='formfieldline'>
... ...
app/views/account/forgot_password.html.erb
... ... @@ -2,21 +2,13 @@
2 2  
3 3 <%= error_messages_for :change_password, :header_message => _('Instructions to password recovery could not be sent'), :message => nil %>
4 4  
5   -<%= labelled_form_for :change_password, @change_password, :url => { :action => 'forgot_password' } do |f| %>
6   -
7   - <%= f.text_field :login,
8   - :onchange => 'this.value = convToValidUsername( this.value )' %>
9   -
10   - <%= f.text_field :email %>
11   -
12   - <%= f.hidden_field :environment_id, :value => environment.id %>
13   -
14   -<div>
15   - <% button_bar do %>
16   - <%= submit_button('send', _('Send instructions')) %>
17   - <% end %>
18   -</div>
19   -<%= content_tag(:small,_('After clicking the button above, you will receive an email with a link to a page where you will be able to create a new password.')) %>
20   -
  5 +<% form_tag do %>
  6 + <%= labelled_form_field fields_label, text_field_tag(:value) %>
  7 +
  8 + <div>
  9 + <% button_bar do %>
  10 + <%= submit_button('send', _('Send instructions')) %>
  11 + <% end %>
  12 + </div>
  13 + <%= content_tag(:small,_('After clicking the button above, you will receive an email with a link to a page where you will be able to create a new password.')) %>
21 14 <% end %>
22   -
... ...
app/views/blocks/location.html.erb
1 1 <% if profile.lat %>
2 2 <%= block_title block.title %>
3 3 <div class='the-localization-map'>
4   - <img src="http://maps.google.com/maps/api/staticmap?center=<%=profile.lat%>,<%=profile.lng%>&zoom=<%=block.zoom%>&size=190x250&maptype=<%=block.map_type%>&markers=<%=profile.lat%>,<%=profile.lng%>&sensor=false"/>
  4 + <img src="https://maps.google.com/maps/api/staticmap?center=<%=profile.lat%>,<%=profile.lng%>&zoom=<%=block.zoom%>&size=190x250&maptype=<%=block.map_type%>&markers=<%=profile.lat%>,<%=profile.lng%>&sensor=false"/>
5 5 </div>
6 6 </div>
7 7 <% else %>
... ...
app/views/blocks/profile_info.html.erb
... ... @@ -17,7 +17,7 @@
17 17 <ul class="profile-info-data" id="profile-info-data-<%= block.id %>">
18 18 <li><%= link_to _('Homepage'), block.owner.url, :class => 'url' %></li>
19 19 <li><%= link_to _('View profile'), block.owner.public_profile_url %></li>
20   - <% if block.owner.enterprise? && !block.owner.environment.enabled?('disable_products_for_enterprises') %>
  20 + <% if block.owner.enterprise? && block.owner.environment.enabled?('products_for_enterprises') %>
21 21 <li><%= link_to(_('Products/Services'), :controller => 'catalog', :profile => block.owner.identifier) %></li>
22 22 <% end %>
23 23 <li id="profile-admin-url-<%= block.id %>"></li>
... ...
app/views/box_organizer/_article_block.html.erb
1 1 <div class="article-block-edition">
2   -<% if @block.box.owner.kind_of?(Environment) and @block.box.owner.portal_community.nil? %>
  2 +<% if @block.owner.kind_of?(Environment) and @block.owner.portal_community.nil? %>
3 3 <p id="no_portal_community">
4 4 <%= _("You don't have an community defined as the portal community. Define it before use this block properly.") %>
5 5 </p>
... ...
app/views/box_organizer/_link_list_block.html.erb
1 1 <strong><%= _('Links') %></strong>
2 2 <div id='edit-link-list-block' style='width:450px'>
3 3 <table id='links' class='noborder'>
4   - <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th></tr>
  4 + <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th><th><%= _('Target') %></th></tr>
5 5 <% for link in @block.links do %>
6 6 <tr>
7 7 <td>
... ... @@ -9,6 +9,9 @@
9 9 </td>
10 10 <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td>
11 11 <td class='cel-address'><%= text_field_tag 'block[links][][address]', link[:address], :class => 'link-address' %></td>
  12 + <td>
  13 + <%= select_tag('block[links][][target]', options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target])) %>
  14 + </td>
12 15 </tr>
13 16 <% end %>
14 17 </table>
... ... @@ -18,7 +21,9 @@
18 21 page.insert_html :bottom, 'links', content_tag('tr',
19 22 content_tag('td', icon_selector('ok')) +
20 23 content_tag('td', text_field_tag('block[links][][name]', '', :maxlength => 20)) +
21   - content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'cel-address'))
  24 + content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'link-address'), :class => 'cel-address') +
  25 + content_tag('td', select_tag('block[links][][target]',
  26 +options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target])))
22 27 ) +
23 28 javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight")
24 29 end %>
... ...
app/views/catalog/index.html.erb
... ... @@ -92,7 +92,7 @@
92 92 <% end %>
93 93 <% product.price_details.each do |i| %>
94 94 <div class="search-product-input-dots-to-price">
95   - <div class="search-product-input-name"><%= i.production_cost.name %></div>
  95 + <div class="search-product-input-name"><%= i.name %></div>
96 96 <%= price_span i.price, :class => 'search-product-input-price' %>
97 97 </div>
98 98 <% end %>
... ...
app/views/cms/_blog.html.erb
... ... @@ -54,6 +54,15 @@
54 54  
55 55 <%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 10)) %>
56 56  
  57 +<% f.fields_for :image_builder, @article.image do |i| %>
  58 + <%= file_field_or_thumbnail(_('Cover image:'), @article.image, i)%>
  59 + <%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %>
  60 +<% end %>
  61 +
  62 +<% unless @article.image.nil? %>
  63 + <%= labelled_check_box(_('Remove cover image'),'remove_image',true,false)%>
  64 +<% end %>
  65 +
57 66 <%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [ [ _('Full post'), 'full'], [ _('First paragraph'), 'short'] ])) %>
58 67  
59 68 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Blog.posts_per_page_options)) %>
... ...
app/views/cms/view.html.erb
... ... @@ -2,6 +2,18 @@
2 2 <%= _('Content management') %>
3 3 </h1>
4 4  
  5 +<% if !environment.enabled?('cant_change_homepage') && !remove_content_button(:home) %>
  6 + <div class="cms-homepage">
  7 + <%= _('Profile homepage:') %>
  8 + <% if profile.home_page %>
  9 + <%= link_to_article(profile.home_page) %>
  10 + <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %>
  11 + <% else %>
  12 + <span class="cms-homepage-default"><%= _('Profile Information') %></span>
  13 + <% end %>
  14 + </div>
  15 +<% end %>
  16 +
5 17 <% button_bar(:style => 'margin-bottom: 1em;') do %>
6 18 <% parent_id = ((@article && @article.allow_children?) ? @article : nil) %>
7 19  
... ... @@ -40,20 +52,27 @@
40 52 </tr>
41 53 <% end %>
42 54  
43   - <% @articles.each do |article| %>
44   - <tr>
45   - <td>
  55 + <% @articles.each do |article| article = FilePresenter.for article %>
  56 + <tr title="<%= article.title%>" >
  57 + <td class="article-name">
46 58 <%= link_to_article(article) %>
47 59 </td>
48   - <td>
49   - <%= article.class.short_description %>
  60 + <% short_description = article.respond_to?(:short_description) ?
  61 + article.short_description :
  62 + article.class.short_description %>
  63 + <td class="article-mime" title=<%= short_description.to_json %>>
  64 + <%= short_description %>
50 65 </td>
51 66 <td class="article-controls">
52 67 <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %>
53 68 <%= button_without_text :eyes, _('Public view'), article.view_url %>
54 69 <%= display_spread_button(profile, article) unless article.folder? || remove_content_button(:spread)%>
55 70 <% if !environment.enabled?('cant_change_homepage') && !remove_content_button(:home) %>
56   - <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %>
  71 + <% if profile.home_page != article %>
  72 + <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %>
  73 + <% else %>
  74 + <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %>
  75 + <% end %>
57 76 <% end %>
58 77 <%= display_delete_button(article) if !remove_content_button(:delete) %>
59 78 </td>
... ...
app/views/comment/_comment.rhtml
... ... @@ -13,11 +13,11 @@
13 13 <% else %>
14 14 <% url_image, status_class = comment.author_id ?
15 15 [comment.removed_user_image, 'icon-user-removed'] :
16   - [str_gravatar_url_for( comment.email, :size => 50, :d=>404 ), 'icon-user-unknown'] %>
  16 + [gravatar_profile_image_url( comment.email, :size => 50, :d=>404 ), 'icon-user-unknown'] %>
17 17  
18 18 <%= link_to(
19 19 image_tag(url_image, :onerror=>'gravatarCommentFailback(this)',
20   - 'data-gravatar'=>str_gravatar_url_for(comment.email, :size=>50)) +
  20 + 'data-gravatar'=>gravatar_profile_image_url(comment.email, :size=>50)) +
21 21 content_tag('span', comment.author_name, :class => 'comment-info') +
22 22 content_tag('span', comment.message,
23 23 :class => 'comment-user-status ' + status_class),
... ...
app/views/content_viewer/_article_toolbar.html.erb
... ... @@ -52,6 +52,9 @@
52 52  
53 53 </div>
54 54 <div id="article-header">
  55 + <% if @page.blog? and !@page.image.nil? %>
  56 + <div class="blog-cover"><%= image_tag(@page.image.public_filename())%></div>
  57 + <% end %>
55 58 <%= link_to(image_tag('icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %>
56 59 <%= article_title(@page, :no_link => true) %>
57 60 <%= article_translations(@page) %>
... ...
app/views/content_viewer/_enterprise_homepage.html.erb
1 1 <%= display_profile_info enterprise_homepage.profile %>
2 2 <div><%= enterprise_homepage.body %></div>
3   -<%= render :file => 'catalog/index' unless enterprise_homepage.profile.environment.enabled?('disable_products_for_enterprises') %>
  3 +<%= render :file => 'catalog/index' if enterprise_homepage.profile.environment.enabled?('products_for_enterprises') %>
... ...
app/views/content_viewer/view_page.html.erb
... ... @@ -14,6 +14,12 @@
14 14 window.NO_COMMENT_YET = "<%= _('No comments yet') %>";
15 15 </script>
16 16  
  17 +<% if @page.parent && !@page.parent.path.blank? %>
  18 +<div id="article-parent">
  19 + <%= button(:back, _('Go back to %s') % @page.parent.short_title, @page.parent.url) %>
  20 +</div>
  21 +<% end %>
  22 +
17 23 <div id="article-toolbar"></div>
18 24  
19 25 <script type="text/javascript">
... ... @@ -43,11 +49,6 @@
43 49 </div>
44 50 <% end %>
45 51  
46   -<% if @page.parent && !@page.parent.path.blank? %>
47   -<div id="article-parent">
48   - <%= button(:back, _('Go back to %s') % @page.parent.short_title, @page.parent.url) %>
49   -</div>
50   -<% end %>
51 52  
52 53 <%= render :partial => 'shared/disabled_enterprise' %>
53 54  
... ...
app/views/enterprise_registration/basic_information.html.erb
... ... @@ -34,7 +34,7 @@
34 34 <% end %>
35 35 <% end %>
36 36  
37   - <%= template_options(Enterprise, 'create_enterprise')%>
  37 + <%= template_options(:enterprises, 'create_enterprise')%>
38 38  
39 39 <% button_bar do %>
40 40 <%= submit_button('next', _('Next'), :cancel => {:profile => current_user.person.identifier, :action=>"enterprises", :controller=>"profile"}) %>
... ...
app/views/events/_agenda.html.erb
... ... @@ -3,22 +3,14 @@
3 3 <table class='noborder current-month'>
4 4 <caption>
5 5 <h2><%= show_month(params[:year], params[:month]) %></h2>
6   - <%= link_to_previous_month(params[:year], params[:month], '&laquo; %s' % _('previous')) %>
7   - <%= link_to_next_month(params[:year], params[:month], '%s &raquo;' % _('next')) %>
  6 + <%= link_to_previous_month(params[:year], params[:month], show_month(params[:year], params[:month], :previous => true, :only_month => true)) %>
  7 + <%= link_to_next_month(params[:year], params[:month], show_month(params[:year], params[:month], :next => true, :only_month => true)) %>
8 8 </caption>
9 9 <%= render :partial => 'events/month', :locals => {:calendar => @calendar, :abbreviated => true} %>
10 10 </table>
11   - <table class='noborder previous-month'>
12   - <caption><h3><%= link_to_previous_month(params[:year], params[:month], show_month(params[:year], params[:month], :previous => true)) %></h3></caption>
13   - <%= render :partial => 'events/month', :locals => {:calendar => @previous_calendar, :abbreviated => true} %>
14   - </table>
15   - <table class='noborder next-month'>
16   - <caption><h3><%= link_to_next_month(params[:year], params[:month], show_month(params[:year], params[:month], :next => true)) %></h3></caption>
17   - <%= render :partial => 'events/month', :locals => {:calendar => @next_calendar, :abbreviated => true} %>
18   - </table>
19 11 <br clear='both'/>
20 12 </div>
21 13 <div id='events-of-the-day'>
22   - <%= render :partial => 'events/events_by_day' %>
  14 + <%= render :partial => 'events/events' %>
23 15 </div>
24 16 </div>
... ...
app/views/events/_events.rhtml 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= list_events(@date, @events) %>
0 2 \ No newline at end of file
... ...
app/views/events/_events_by_day.html.erb
... ... @@ -1 +0,0 @@
1   -<%= list_events(@selected_day, @events_of_the_day) %>
app/views/file_presenter/_generic.html.erb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<span class="download-link">
  2 + <span>Download</span>
  3 + <strong><%= link_to generic.filename, generic.public_filename %></strong>
  4 +</span>
  5 +
  6 +<div class="uploaded-file-description <%= 'empty' if generic.abstract.blank? %>">
  7 + <%= generic.abstract %>
  8 +</div>
  9 +
... ...
app/views/file_presenter/_image.html.erb 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +<% if image.gallery? && options[:gallery_view] %>
  2 +<%
  3 + images = image.parent.images
  4 + current_index = images.index(image.encapsulated_file)
  5 + total_of_images = images.count
  6 + link_to_previous = if current_index >= 1
  7 + link_to(_('&laquo; Previous'), images[current_index - 1].view_url, :class => 'previous')
  8 + else
  9 + content_tag('span', _('&laquo; Previous'), :class => 'previous')
  10 + end
  11 +
  12 + link_to_next = if current_index < total_of_images - 1
  13 + link_to(_('Next &raquo;'), images[current_index + 1].view_url, :class => 'next')
  14 + else
  15 + content_tag('span', _('Next &raquo;'), :class => 'next')
  16 + end
  17 +%>
  18 +
  19 +<div class="gallery-navigation">
  20 + <%= link_to_previous %>
  21 + <span class="total-of-images">
  22 + <%= _('image %d of %d') % [current_index + 1, total_of_images] %>
  23 + </span>
  24 + <%= link_to_next %>
  25 +</div>
  26 +
  27 +<% end %>
  28 +
  29 +<%# image_tag(article.public_filename(:display), :class => article.css_class_name, :style => 'max-width: 100%') %>
  30 +
  31 +<img src="<%=image.public_filename(:display)%>" class="<%=image.css_class_name%>">
  32 +
  33 +<div class="uploaded-file-description <%= 'empty' if image.abstract.blank? %>">
  34 + <%= image.abstract %>
  35 +</div>
  36 +
... ...
app/views/manage_products/_display_price_details.html.erb
... ... @@ -10,7 +10,7 @@
10 10 </li>
11 11 <% @product.price_details.each do |price_detail| %>
12 12 <li>
13   - <div class='price-detail-name'><%= "%s:" % price_detail.production_cost.name %></div>
  13 + <div class='price-detail-name'><%= "%s:" % price_detail.name %></div>
14 14 <div class='price-detail-price'><%= float_to_currency(price_detail.price) %></div>
15 15 </li>
16 16 <% end %>
... ...
app/views/manage_products/_edit_price_details.html.erb
1 1 <% price_details.each do |price_detail| %>
2 2 <tr id='<%= "price-detail-#{price_detail.id}" %>'>
3 3 <td><%= select_production_cost(@product, price_detail.production_cost_id) %></td>
4   - <td><%= labelled_form_field(environment.currency_unit, text_field_tag('price_details[][price]', price_detail.formatted_value(:price), :class => 'numbers-only price-details-price')) %></td>
  4 + <td><%= labelled_form_field(environment.currency_unit, text_field_tag('price_details[][price]', price_detail.formatted_value(:price), :class => 'numbers-only price-details-price', :size => 6)) %></td>
5 5 <td>
6 6 <%= link_to_remote(_('Remove'),
7 7 :update => "price-detail-#{price_detail.id}",
... ...
app/views/manage_products/_manage_product_details.html.erb
... ... @@ -34,7 +34,7 @@
34 34 <table id='new-cost-fields'>
35 35 <tr>
36 36 <td><%= select_production_cost(@product) %></td>
37   - <td><%= labelled_form_field(environment.currency_unit, text_field_tag('price_details[][price]', nil, :class => 'numbers-only price-details-price')) %></td>
  37 + <td><%= labelled_form_field(environment.currency_unit, text_field_tag('price_details[][price]', nil, :class => 'numbers-only price-details-price', :size => 6)) %></td>
38 38 <td><%= link_to(_('Cancel'), '#', {:class => 'cancel-new-cost'}) %></td>
39 39 </tr>
40 40 </table>
... ...
app/views/maps/edit_location.html.erb
... ... @@ -29,5 +29,5 @@
29 29  
30 30 <% end %>
31 31  
32   -<%= content_tag('script', '', :src => "http://maps.googleapis.com/maps/api/js?sensor=false", :type => 'text/javascript') %>
  32 +<%= content_tag('script', '', :src => "https://maps.googleapis.com/maps/api/js?sensor=false", :type => 'text/javascript') %>
33 33 <%= content_tag('script', '', :src => url_for(:controller => :maps, :action => :google_map), :type => 'text/javascript') %>
... ...
app/views/memberships/index.html.erb
... ... @@ -8,6 +8,19 @@
8 8 <%= button :back, _('Go back'), :controller => 'profile_editor' %>
9 9 <% end %>
10 10  
11   -<%= render :partial => 'shared/list_groups', :locals => {:groups => @memberships} %>
  11 +<% type_collection = [[nil, _('All')]] %>
  12 +<% type_collection += @roles.sort_by {|role| role.id}.map{|r| ["#{r.id}", r.name]} %>
  13 +
  14 +<p>
  15 + <%= labelled_select(_('Filter')+': ', :filter_type, :first, :last, @filter, type_collection, :onchange => 'document.location.href = "?filter_type="+this.value')%>
  16 +</p>
  17 +
  18 +<% if @memberships.empty? %>
  19 + <p>
  20 + <em><%= _('No groups to list') %></em>
  21 + </p>
  22 +<% else %>
  23 + <%= render :partial => 'shared/list_groups', :locals => {:groups => @memberships} %>
  24 +<% end %>
12 25  
13 26 </div>
... ...
app/views/memberships/new_community.html.erb
... ... @@ -44,11 +44,13 @@
44 44 </div>
45 45 </div>
46 46  
47   - <%= template_options(Community, 'community')%>
  47 + <%= template_options(:communities, 'community')%>
  48 +
  49 + <%= hidden_field_tag('back_to', @back_to) %>
48 50  
49 51 <% button_bar do %>
50 52 <%= submit_button(:save, _('Create')) %>
51   - <%= button(:cancel, _('Cancel'), :action => 'index') %>
  53 + <%= button(:cancel, _('Cancel'), @back_to ) %>
52 54 <% end %>
53 55  
54 56 <% end %>
... ...