Commit d35779c07490f70c33e688afa945036e075a90f2

Authored by Antonio Terceiro
2 parents 1420310b af89dbce

Merge branch 'master' into rails-2.3.5

Conflicts:
	app/controllers/admin_controller.rb
	app/controllers/application.rb
	app/controllers/public/account_controller.rb
	app/controllers/public/content_viewer_controller.rb
	lib/noosfero/i18n.rb
	test/functional/account_controller_test.rb
	test/functional/admin_controller_test.rb
	test/unit/contact_test.rb
	test/unit/profile_test.rb
Showing 842 changed files with 113986 additions and 21393 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 842 files displayed.

.mailmap
... ... @@ -3,7 +3,8 @@ Antonio Terceiro <terceiro@colivre.coop.br> <terceiro@softwarelivre.org>
3 3 Aurelio A. Heckert <aurelio@colivre.coop.br> <AurelioAHeckert@3f533792-8f58-4932-b0fe-aaf55b0a4547>
4 4 Aurelio A. Heckert <aurelio@colivre.coop.br> <aurelio@colivre.coop.br>
5 5 Aurelio A. Heckert <aurelio@colivre.coop.br> <aurium@gmail.com>
6   -Caio SBA <caiosba@gmail.com> <caiosba@safernet.org.br>
  6 +Caio SBA <caio@colivre.coop.br> <caiosba@gmail.com>
  7 +Caio SBA <caio@colivre.coop.br> <caiosba@safernet.org.br>
7 8 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <DanielaFeitosa@3f533792-8f58-4932-b0fe-aaf55b0a4547>
8 9 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <danielafeitosa@colivre.coop.br>
9 10 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <daniela@sede.colivre.coop.br>
... ...
AUTHORS
... ... @@ -8,8 +8,9 @@ Developers
8 8  
9 9 Antonio Terceiro <terceiro@colivre.coop.br>
10 10 Aurelio A. Heckert <aurelio@colivre.coop.br>
  11 +Braulio Bhavamitra <brauliobo@gmail.com>
11 12 Bráulio Bhavamitra <brauliobo@gmail.com>
12   -Caio SBA <caiosba@gmail.com>
  13 +Caio SBA <caio@colivre.coop.br>
13 14 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>
14 15 Daniel Cunha <daniel@colivre.coop.br>
15 16 Fernanda Lopes <nanda.listas+psl@gmail.com>
... ... @@ -23,6 +24,7 @@ LinguÁgil 2010 &lt;linguagil.bahia@gmail.com&gt;
23 24 Martín Olivera <molivera@solar.org.ar>
24 25 Moises Machado <moises@colivre.coop.br>
25 26 Nanda Lopes <nanda.listas+psl@gmail.com>
  27 +Rafael Gomes <rafaelgomes@techfree.com.br>
26 28 Raphaël Rousseau <raph@r4f.org>
27 29 Raquel Lira <raquel.lira@gmail.com>
28 30 Rodrigo Souto <rodrigo@colivre.coop.br>
... ...
HACKING
... ... @@ -31,11 +31,13 @@ You can copy and paste the commands below into a terminal (please review the
31 31 commands and make sure you understand what you are doing):
32 32  
33 33 # checkout the code from repository
34   - git clone git://git.colivre.coop.br/noosfero.git
  34 + git clone git://gitorious.org/noosfero/noosfero.git
35 35 # enter the directory
36 36 cd noosfero
37 37 # copy a sample config file
38 38 cp config/database.yml.sqlite3 config/database.yml
  39 + # create tmp directory if it doesn't exist
  40 + mkdir tmp
39 41 # create the development database
40 42 rake db:schema:load
41 43 # run pending migrations
... ... @@ -59,8 +61,8 @@ another port than 3000, you can use the -p option of ./script/server:
59 61  
60 62 The above command makes the server available at http://localhost:9999/
61 63  
62   -The sample-data data scripts creates one administrator user with login "ze" and
63   -password "test".
  64 +The sample-data data scripts creates two administrator users with login "ze" and
  65 +password "test" and login "adminuser" and password "admin".
64 66  
65 67 Note that some operations, like generating image thumbnails, sending e-mails,
66 68 etc, are done in background in the context of a service independent from the
... ... @@ -84,3 +86,7 @@ you want to enable it then you need to change some files:
84 86  
85 87 3) Add in config/noosfero.yml at development section:
86 88 exception_recipients: [admin@example.com]
  89 +
  90 +== Releasing and building Debian package
  91 +
  92 +See RELEASING file.
... ...
INSTALL
... ... @@ -43,6 +43,9 @@ case will need do install hicolor-icon-theme as well bacause tango-icon-theme
43 43 depends on it. After that you can try the command line above again, but
44 44 without "tango-icon-theme".
45 45  
  46 +More Informations to install the tango-icon-theme on Debian Lenny:
  47 +* http://packages.debian.org/en/lenny/all/tango-icon-theme/download
  48 +
46 49 If you manage to install Noosfero successfully on other systems than Debian,
47 50 please feel free to contact the Noosfero development mailing with the
48 51 instructions for doing so, and we'll include it here.
... ... @@ -98,7 +101,7 @@ downloading from git
98 101 Here we are cloning the noosfero repository from git. Note: you will need to
99 102 install git before.
100 103  
101   -$ git clone git://git.colivre.coop.br/noosfero.git current
  104 +$ git clone git://gitorious.org/noosfero/noosfero.git current
102 105 $ cd current
103 106 $ git checkout -b stable origin/stable
104 107  
... ...
INSTALL.chat
... ... @@ -221,16 +221,19 @@ Note: module proxy_http must be enabled:
221 221  
222 222 # a2enmod proxy_http
223 223  
224   -
225 224 8. DNS configuration
226 225  
227   - * /etc/bind/db.colivre:
  226 +For this point, we assume you are using BIND as your DNS server. You need to
  227 +add the following entries to the DNS zone file corresponding to the domain
  228 +of your noosfero site:
228 229  
229 230 _xmpp-client._tcp SRV 5 100 5222 master
230   -(...)
231 231 conference CNAME master
232 232 _xmpp-client._tcp.conference SRV 5 100 5222 master
233 233  
  234 +If you are running a DNS server other than BIND, you will have to figure out
  235 +how to create equivalente rules for your zone file. Patches to this
  236 +documentation are welcome.
234 237  
235 238 9. Testing this Setup
236 239  
... ...
INSTALL.multitenancy 0 → 100644
... ... @@ -0,0 +1,163 @@
  1 +== Multitenancy support
  2 +
  3 +Multitenancy refers to a principle in software architecture where a
  4 +single instance of the software runs on a server, serving multiple
  5 +client organizations (tenants). Multitenancy is contrasted with a
  6 +multi-instance architecture where separate software instances (or
  7 +hardware systems) are set up for different client organizations. With
  8 +a multitenant architecture, a software application is designed to
  9 +virtually partition its data and configuration, and each client
  10 +organization works with a customized virtual application instance.
  11 +
  12 +Today this feature is available only for PostgreSQL databases.
  13 +
  14 +This document assumes that you have a new fully PostgresSQL default Noosfero
  15 +installation as explained at the INSTALL file.
  16 +
  17 +== Separated data
  18 +
  19 +The items below are separated for each hosted environment:
  20 +
  21 +* Uploaded files
  22 +* Database
  23 +* Ferret index
  24 +* ActiveRecord#cache_key
  25 +* Feed updater
  26 +* Delayed Job Workers
  27 +
  28 +== Database configuration file
  29 +
  30 +The file config/database.yml must follow a structure in order to
  31 +achieve multitenancy support. In this example, we will set 3
  32 +different environments: env1, env2 and env3.
  33 +
  34 +Each "hosted" environment must have an entry like this:
  35 +
  36 +env1_production:
  37 + adapter: postgresql
  38 + encoding: unicode
  39 + database: noosfero
  40 + schema_search_path: public
  41 + username: noosfero
  42 + domains:
  43 + - env1.com
  44 + - env1.org
  45 +
  46 +env2_production:
  47 + adapter: postgresql
  48 + encoding: unicode
  49 + database: noosfero
  50 + schema_search_path: env2
  51 + username: noosfero
  52 + domains:
  53 + - env2.com
  54 + - env2.org
  55 +
  56 +env3_production:
  57 + adapter: postgresql
  58 + encoding: unicode
  59 + database: noosfero
  60 + schema_search_path: env3
  61 + username: noosfero
  62 + domains:
  63 + - env3.com
  64 + - env3.net
  65 +
  66 +The "hosted" environments define, besides the schema_search_path, a
  67 +list of domains that, when accessed, tells which database the
  68 +application should use. Also, the environment name must end with
  69 +'_hosting', where 'hosting' is the name of the hosting environment.
  70 +
  71 +You must also tell the application which is the default environment.
  72 +
  73 +production:
  74 + env1_production
  75 +
  76 +On the example above there are only three hosted environments, but it
  77 +can be more than three. The schemas 'env2' and 'env3' must already
  78 +exist in the same database of the hosting environment. As postgres
  79 +user, you can create them typing:
  80 +
  81 +$ psql database_name -c "CREATE SCHEMA env2 AUTHORIZATION database_user"
  82 +$ psql database_name -c "CREATE SCHEMA env3 AUTHORIZATION database_user"
  83 +
  84 +Replace database_name and database_user above with your stuff.
  85 +
  86 +So, yet on this same example, when a user accesses http://env2.com or
  87 +http://env2.org, the Noosfero application running on production will
  88 +turn the database schema to 'env2'. When the access is from domains
  89 +http://env3.com or http://env3.net, the schema to be loaded will be
  90 +'env3'.
  91 +
  92 +There is an example of this file in config/database.yml.multitenancy
  93 +
  94 +== Preparing the database
  95 +
  96 +Now create the environments:
  97 +
  98 +$ RAILS_ENV=production rake multitenancy:create
  99 +
  100 +This command above will create the hosted environment files equal to
  101 +their hosting environment, here called 'production'.
  102 +
  103 +Run db:schema:load for each other environment:
  104 +
  105 +$ RAILS_ENV=env2_production rake db:schema:load
  106 +$ RAILS_ENV=env3_production rake db:schema:load
  107 +
  108 +Then run the migrations for the hosting environment, and it will
  109 +run for each of its hosted environments:
  110 +
  111 +RAILS_ENV=production rake db:migrate
  112 +
  113 +== Start Noosfero
  114 +
  115 +Run Noosfero init file as root:
  116 +
  117 +# invoke-rc.d noosfero start
  118 +
  119 +== Ferret
  120 +
  121 +It's necessary to run only one instance of ferret_server. Don't worry
  122 +about this, Noosfero initializer had already done this for you.
  123 +
  124 +== Feed updater & Delayed job
  125 +
  126 +Just for your information, a daemon of feed-updater and delayed_job
  127 +must be running for each environment. Noosfero initializer do this,
  128 +relax.
  129 +
  130 +== Uploaded files
  131 +
  132 +When running with PostgreSQL, Noosfero uploads stuff to a folder named
  133 +the same way as the running schema. Inside the upload folder root, for
  134 +example, will be public/image_uploads/env2 and public/image_uploads/env3.
  135 +
  136 +== Adding multitenancy support to an existing Noosfero environment
  137 +
  138 +If you already have a Noosfero environment, you can turn it multitenant
  139 +by following the steps below in addition to the previous steps:
  140 +
  141 +1. Reindex your database
  142 +
  143 +Rebuild the Ferret index by running the following task just
  144 +for your hosting environment, do this as noosfero user:
  145 +
  146 +$ RAILS_ENV=production rake multitenancy:reindex
  147 +
  148 +2. Move the uploaded files to the right place
  149 +
  150 +Add a directory with the same name as your schema name (by default this
  151 +name is 'public') in the root of each upload directory, for example,
  152 +public/articles/0000 will be moved to public/articles/public/0000. Do this
  153 +with the directories public/image_uploads, public/articles and public/thumbnails.
  154 +
  155 +3. Fix paths on activities
  156 +
  157 +The profile activities store static paths to the images, so it's necessary to fix
  158 +these paths. You can do this easily by setting an alias on your webserver.
  159 +On Apache you can add the three rules below, where 'public' is the schema name:
  160 +
  161 + RewriteRule ^/articles(.+) /articles/public$1
  162 + RewriteRule ^/image_uploads(.+) /image_uploads/public$1
  163 + RewriteRule ^/thumbnails(.+) /thumbnails/public$1
... ...
INSTALL.varnish
... ... @@ -50,14 +50,22 @@ apache-compatible format. You should change your statistics generation software
50 50  
51 51 # invoke-rc.d varnish restart
52 52  
53   -6) Configure varnish to store separate caches for each language
  53 +6) Configure varnish to fit noosfero
  54 +(assuming Noosfero is installed in /var/lib/noosfero)
54 55  
55   -6a) Add the following line to your /etc/varnish/default.vcl file (assuming
56   -Noosfero is installed in /var/lib/noosfero):
  56 +6a) Configure noosfero to do specific routines to varnish
  57 +
  58 +Add the following line to your /etc/varnish/default.vcl file:
  59 +
  60 + include "/var/lib/noosfero/etc/noosfero/varnish-noosfero.vcl";
  61 +
  62 +6b) Configure varnish to store separate caches for each language
  63 +
  64 +Add the following line to your /etc/varnish/default.vcl file:
57 65  
58 66 include "/var/lib/noosfero/etc/noosfero/varnish-accept-language.vcl";
59 67  
60   -6b) Restart Varnish
  68 +7) Restart Varnish
61 69  
62 70 # invoke-rc.d varnish restart
63 71  
... ...
RELEASING
... ... @@ -12,28 +12,28 @@ This file documents release-related activities.
12 12  
13 13 == Releasing noosfero
14 14  
  15 +Considering you are on a Debian GNU/Linux or Debian-based system
  16 + # apt-get install devscripts debhelper
  17 +
15 18 To prepare a release of noosfero, you must follow the steps below:
16 19  
17   -* finish all requirements and bugs assigned to the to-be-released version
18   -* make sure all tests pass
19   -* write release notes at the version's wiki topic.
20   -* generate package with <tt>rake package</tt>. Your tarball will be under the pkg/
21   - directory, named as noosfero-${VERSION}.tar.gz
22   -* test that the package contains everything that is needed: explode the tarball
23   - in a temporary directory, copy config/database.yml.sqlite3 to
24   - config/database.yml, and make <tt>rake db:migrate</tt> and <tt>rake test</tt>. If
25   - everything is ok, you are done. If not, maybe some files are not going into
26   - the tarball. See lib/tasks/package.rake, probably you'll need to change it.
27   -* Go to the version's wiki topic and edit it to reflect the new reality.
28   -* Attach the generated package to that topic. Before attaching calculate the md5 of the package (with mu5sum and paste the MD5 hash as comment in the attachment form)
  20 +* Finish all requirements and bugs assigned to the to-be-released version
  21 +* Make sure all tests pass
  22 +* Change the version in lib/noosfero.rb and debian/changelog to the
  23 + to-be-released version (e.g. 0.2.0, 0.3.1)
  24 +* Write release notes at the version's wiki topic
  25 +* Generate packages with <tt>rake noosfero:release</tt>. Your tarball and deb
  26 + pkg will be under the pkg/ directory. This task will create a git tag too.
  27 +* Test that the tarball and deb package are ok
  28 +* Go to the version's wiki topic and edit it to reflect the new reality
  29 +* Edit the topic WebPreferences and update DEBIAN_REPOSITORY_TOPICS setting
  30 +* Attach the generated packages to that topic. Before attaching calculate the
  31 + sha1 of the package (with sha1sum and paste the SHA1 hash as comment in the
  32 + attachment form)
29 33 * Download the attached and verify the MD5 hash
30   -* create a git tag for the released version with <tt>git tag</tt>.
31   -* IMMEDIATELY change the version in lib/noosfero.rb to the next version. (e.g.
32   - 0.2.0 -> 0.3.0)
33   -* update an eventual demonstration version that you run.
34   -* write an announcement e-mail to the relevant maimling lists pointing to the release notes, and maybe to the demonstration version.
  34 +* Update an eventual demonstration version that you run.
  35 +* Write an announcement e-mail to the relevant mailing lists pointing to the
  36 + release notes, and maybe to the demonstration version.
35 37  
36 38 If you had any problem during these steps, you can do <tt>rake clobber_package</tt> to
37 39 completely delete the generated packages and start the process again.
38   -
39   -
... ...
app/controllers/admin/admin_panel_controller.rb
1 1 class AdminPanelController < AdminController
2 2  
3   - before_filter :login_required
4   -
5 3 protect 'view_environment_admin_panel', :environment
6 4  
7 5 def boxes_holder
... ... @@ -11,6 +9,7 @@ class AdminPanelController &lt; AdminController
11 9 def site_info
12 10 if request.post?
13 11 if @environment.update_attributes(params[:environment])
  12 + session[:notice] = _('Environment settings updated')
14 13 redirect_to :action => 'index'
15 14 end
16 15 end
... ...
app/controllers/admin/categories_controller.rb
... ... @@ -10,11 +10,16 @@ class CategoriesController &lt; AdminController
10 10 @product_categories = environment.product_categories.find(:all, :conditions => {:parent_id => nil})
11 11 end
12 12  
  13 + def get_children
  14 + children = Category.find(params[:id]).children
  15 + render :partial => 'category_children', :locals => {:children => children}
  16 + end
  17 +
13 18 ALLOWED_TYPES = CategoriesHelper::TYPES.map {|item| item[1] }
14 19  
15 20 # posts back
16 21 def new
17   - type = (params[:type] || 'Category')
  22 + type = (params[:type] || params[:parent_type] || 'Category')
18 23 raise 'Type not allowed' unless ALLOWED_TYPES.include?(type)
19 24  
20 25 @category = type.constantize.new(params[:category])
... ...
app/controllers/admin/plugins_controller.rb
1 1 class PluginsController < AdminController
  2 + protect 'edit_environment_features', :environment
2 3  
3 4 def index
4 5 @active_plugins = Noosfero::Plugin.all.map {|plugin_name| plugin_name.constantize }.compact
... ...
app/controllers/admin/role_controller.rb
... ... @@ -9,21 +9,6 @@ class RoleController &lt; AdminController
9 9 @role = environment.roles.find(params[:id])
10 10 end
11 11  
12   - def new
13   - @role = Role.new
14   - end
15   -
16   - def create
17   - @role = Role.new(params[:role])
18   - @role.environment = environment
19   - if @role.save
20   - redirect_to :action => 'show', :id => @role
21   - else
22   - session[:notice] = _('Failed to create role')
23   - render :action => 'new'
24   - end
25   - end
26   -
27 12 def edit
28 13 @role = environment.roles.find(params[:id])
29 14 end
... ... @@ -38,13 +23,4 @@ class RoleController &lt; AdminController
38 23 end
39 24 end
40 25  
41   - def destroy
42   - @role = environment.roles.find(params[:id])
43   - if @role.destroy
44   - redirect_to :action => 'index'
45   - else
46   - session[:notice] = _('Failed to edit role')
47   - redirect_to :action => 'index'
48   - end
49   - end
50 26 end
... ...
app/controllers/admin_controller.rb
1 1 class AdminController < ApplicationController
  2 + before_filter :login_required
2 3 end
... ...
app/controllers/box_organizer_controller.rb
... ... @@ -82,7 +82,7 @@ class BoxOrganizerController &lt; ApplicationController
82 82 def save
83 83 @block = boxes_holder.blocks.find(params[:id])
84 84 @block.update_attributes(params[:block])
85   - expire_timeout_fragment(@block.cache_keys)
  85 + expire_timeout_fragment(@block.cache_key)
86 86 redirect_to :action => 'index'
87 87 end
88 88  
... ... @@ -93,7 +93,7 @@ class BoxOrganizerController &lt; ApplicationController
93 93 def remove
94 94 @block = Block.find(params[:id])
95 95 if @block.destroy
96   - expire_timeout_fragment(@block.cache_keys)
  96 + expire_timeout_fragment(@block.cache_key)
97 97 redirect_to :action => 'index'
98 98 else
99 99 session[:notice] = _('Failed to remove block')
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -43,6 +43,9 @@ class CmsController &lt; MyProfileController
43 43 if @parent && @parent.blog?
44 44 articles -= Article.folder_types.map(&:constantize)
45 45 end
  46 + if user.is_admin?(profile.environment)
  47 + articles << RawHTMLArticle
  48 + end
46 49 articles
47 50 end
48 51  
... ... @@ -168,8 +171,7 @@ class CmsController &lt; MyProfileController
168 171 @article = @parent = check_parent(params[:parent_id])
169 172 @target = @parent ? ('/%s/%s' % [profile.identifier, @parent.full_name]) : '/%s' % profile.identifier
170 173 @folders = Folder.find(:all, :conditions => { :profile_id => profile })
171   - @media_listing = params[:media_listing]
172   - if @article && !@media_listing
  174 + if @article
173 175 record_coming
174 176 end
175 177 if request.post? && params[:uploaded_files]
... ... @@ -178,26 +180,14 @@ class CmsController &lt; MyProfileController
178 180 end
179 181 @errors = @uploaded_files.select { |f| f.errors.any? }
180 182 if @errors.any?
181   - if @media_listing
182   - flash[:notice] = _('Could not upload all files')
183   - redirect_to :action => 'media_listing'
184   - else
185   - render :action => 'upload_files', :parent_id => @parent_id
186   - end
  183 + render :action => 'upload_files', :parent_id => @parent_id
187 184 else
188   - if @media_listing
189   - flash[:notice] = _('All files were uploaded successfully')
190   - redirect_to :action => 'media_listing'
  185 + if @back_to
  186 + redirect_to @back_to
  187 + elsif @parent
  188 + redirect_to :action => 'view', :id => @parent.id
191 189 else
192   - if @back_to
193   - redirect_to @back_to
194   - else
195   - redirect_to( if @parent
196   - {:action => 'view', :id => @parent.id}
197   - else
198   - {:action => 'index'}
199   - end)
200   - end
  190 + redirect_to :action => 'index'
201 191 end
202 192 end
203 193 end
... ... @@ -286,44 +276,28 @@ class CmsController &lt; MyProfileController
286 276 @task = SuggestArticle.new(params[:task])
287 277 if request.post?
288 278 @task.target = profile
289   - if @task.save
  279 + if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save
290 280 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.')
291 281 redirect_to @back_to
292 282 end
293 283 end
294 284 end
295 285  
296   - def media_listing
297   - if params[:image_folder_id]
298   - folder = profile.articles.find(params[:image_folder_id]) if !params[:image_folder_id].blank?
299   - @images = (folder ? folder.children : profile.top_level_articles).images
300   - elsif params[:document_folder_id]
301   - folder = profile.articles.find(params[:document_folder_id]) if !params[:document_folder_id].blank?
302   - @documents = (folder ? folder.children : profile.top_level_articles)
303   - else
304   - @documents = profile.articles
305   - @images = @documents.images
306   - @documents -= @images
307   - end
308   -
309   - @images = @images.paginate(:per_page => per_page, :page => params[:ipage], :order => "updated_at desc") if @images
310   - @documents = @documents.paginate(:per_page => per_page, :page => params[:dpage], :order => "updated_at desc", :conditions => {:is_image => false}) if @documents
311   -
312   - @folders = profile.folders
313   - @image_folders = @folders.select {|f| f.children.any? {|c| c.image?} }
314   - @document_folders = @folders.select {|f| f.children.any? {|c| !c.image? && c.kind_of?(UploadedFile) } }
315   -
316   - @media_listing = true
317   -
318   - respond_to do |format|
319   - format.html { render :layout => false}
320   - format.js {
321   - render :update do |page|
322   - page.replace_html 'media-listing-folder-images', :partial => 'image_thumb', :locals => {:images => @images } if !@images.blank?
323   - page.replace_html 'media-listing-folder-documents', :partial => 'document_link', :locals => {:documents => @documents } if !@documents.blank?
324   - end
325   - }
  286 + def search
  287 + query = params[:q]
  288 + results = query.blank? ? [] : profile.articles.published.find_by_contents(query)
  289 + render :text => article_list_to_json(results), :content_type => 'application/json'
  290 + end
  291 + def media_upload
  292 + files_uploaded = []
  293 + parent = check_parent(params[:parent_id])
  294 + files = [:file1,:file2, :file3].map { |f| params[f] }.compact
  295 + if request.post?
  296 + files.each do |file|
  297 + files_uploaded << UploadedFile.create(:uploaded_data => file, :profile => profile, :parent => parent) unless file == ''
  298 + end
326 299 end
  300 + render :text => article_list_to_json(files_uploaded), :content_type => 'text/plain'
327 301 end
328 302  
329 303 protected
... ... @@ -353,7 +327,7 @@ class CmsController &lt; MyProfileController
353 327 end
354 328  
355 329 def refuse_blocks
356   - if ['TinyMceArticle', 'Event', 'EnterpriseHomepage'].include?(@type)
  330 + if ['TinyMceArticle', 'TextileArticle', 'Event', 'EnterpriseHomepage'].include?(@type)
357 331 @no_design_blocks = true
358 332 end
359 333 end
... ... @@ -367,5 +341,21 @@ class CmsController &lt; MyProfileController
367 341 @selected_locale = @article.language || FastGettext.locale
368 342 end
369 343  
  344 + def article_list_to_json(list)
  345 + list.map do |item|
  346 + {
  347 + 'title' => item.title,
  348 + 'url' => item.image? ? item.public_filename(:uploaded) : url_for(item.url),
  349 + :icon => icon_for_article(item),
  350 + :content_type => item.mime_type,
  351 + :error => item.errors.any? ? _('%s could not be uploaded') % item.title : nil,
  352 + }
  353 + end.to_json
  354 + end
  355 +
  356 + def content_editor?
  357 + true
  358 + end
  359 +
370 360 end
371 361  
... ...
app/controllers/my_profile/manage_products_controller.rb
... ... @@ -4,6 +4,7 @@ class ManageProductsController &lt; ApplicationController
4 4 protect 'manage_products', :profile, :except => [:show]
5 5 before_filter :check_environment_feature
6 6 before_filter :login_required, :except => [:show]
  7 + before_filter :create_product?, :only => [:new]
7 8  
8 9 protected
9 10  
... ... @@ -14,6 +15,13 @@ class ManageProductsController &lt; ApplicationController
14 15 end
15 16 end
16 17  
  18 + def create_product?
  19 + if !profile.create_product?
  20 + render_access_denied
  21 + return
  22 + end
  23 + end
  24 +
17 25 public
18 26  
19 27 def index
... ... @@ -40,8 +48,8 @@ class ManageProductsController &lt; ApplicationController
40 48 end
41 49  
42 50 def new
43   - @product = @profile.products.build(:product_category_id => params[:selected_category_id])
44   - @category = @product.product_category
  51 + @category = params[:selected_category_id] ? Category.find(params[:selected_category_id]) : nil
  52 + @product = @profile.products.build(:product_category => @category)
45 53 @categories = ProductCategory.top_level_for(environment)
46 54 @level = 0
47 55 if request.post?
... ... @@ -50,7 +58,7 @@ class ManageProductsController &lt; ApplicationController
50 58 render :partial => 'shared/redirect_via_javascript',
51 59 :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) }
52 60 else
53   - render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' }
  61 + render_dialog_error_messages 'product'
54 62 end
55 63 end
56 64 end
... ... @@ -72,7 +80,7 @@ class ManageProductsController &lt; ApplicationController
72 80  
73 81 def edit_category
74 82 @product = @profile.products.find(params[:id])
75   - @category = @product.product_category
  83 + @category = @product.product_category || ProductCategory.first
76 84 @categories = ProductCategory.top_level_for(environment)
77 85 @edit = true
78 86 @level = @category.level
... ... @@ -81,7 +89,7 @@ class ManageProductsController &lt; ApplicationController
81 89 render :partial => 'shared/redirect_via_javascript',
82 90 :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) }
83 91 else
84   - render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' }
  92 + render_dialog_error_messages 'product'
85 93 end
86 94 end
87 95 end
... ... @@ -96,7 +104,7 @@ class ManageProductsController &lt; ApplicationController
96 104 @inputs = @product.inputs
97 105 render :partial => 'display_inputs'
98 106 else
99   - render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' }
  107 + render_dialog_error_messages 'product'
100 108 end
101 109 else
102 110 render :partial => 'add_input'
... ... @@ -147,7 +155,7 @@ class ManageProductsController &lt; ApplicationController
147 155 @inputs = @product.inputs
148 156 render :partial => 'display_inputs'
149 157 else
150   - render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'input' }
  158 + render_dialog_error_messages 'input'
151 159 end
152 160 end
153 161 end
... ...
app/controllers/my_profile/profile_design_controller.rb
... ... @@ -25,6 +25,7 @@ class ProfileDesignController &lt; BoxOrganizerController
25 25 blocks << DisabledEnterpriseMessageBlock
26 26 blocks << HighlightsBlock
27 27 blocks << FeaturedProductsBlock
  28 + blocks << FansBlock
28 29 end
29 30  
30 31 # product block exclusive for enterprises in environments that permits it
... ... @@ -37,6 +38,10 @@ class ProfileDesignController &lt; BoxOrganizerController
37 38 blocks << BlogArchivesBlock
38 39 end
39 40  
  41 + if user.is_admin?(profile.environment)
  42 + blocks << RawHTMLBlock
  43 + end
  44 +
40 45 blocks
41 46 end
42 47  
... ...
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 = profile.all_pending_tasks.select{|i| user.has_permission?(i.permission, profile)}
  7 + @pending_tasks = Task.to(profile).pending.select{|i| user.has_permission?(i.permission, profile)}
8 8 end
9 9  
10 10 helper :profile
... ...
app/controllers/my_profile/profile_members_controller.rb
1 1 class ProfileMembersController < MyProfileController
2 2 protect 'manage_memberships', :profile
3   - no_design_blocks
4 3  
5 4 def index
6 5 @members = profile.members
... ... @@ -15,29 +14,25 @@ class ProfileMembersController &lt; MyProfileController
15 14 rescue ActiveRecord::RecordNotFound
16 15 @person = nil
17 16 end
18   - if !params[:confirmation] && @person && @person.is_last_admin_leaving?(profile, @roles)
19   - redirect_to :action => :last_admin, :roles => params[:roles], :person => @person
20   - else
21   - if @person && @person.define_roles(@roles, profile)
  17 +
  18 + if @person
  19 + if@person.is_last_admin_leaving?(profile, @roles)
  20 + redirect_to :action => :last_admin
  21 + elsif @person.define_roles(@roles, profile)
22 22 session[:notice] = _('Roles successfuly updated')
  23 + redirect_to :controller => 'profile_editor'
23 24 else
24 25 session[:notice] = _('Couldn\'t change the roles')
  26 + redirect_to :action => 'index'
25 27 end
26   - if params[:confirmation]
27   - redirect_to profile.url
28   - else
29   - redirect_to :action => :index
30   - end
  28 + else
  29 + redirect_to :action => 'index'
31 30 end
32 31 end
33   -
  32 +
34 33 def last_admin
35   - @person = params[:person]
36   - @roles = params[:roles] || []
37   - @members = profile.members.select {|member| !profile.admins.include?(member)}
38   - @title = _('Current admins')
39   - @collection = :profile_admins
40   - @remove_action = {:action => 'remove_admin'}
  34 + @roles = [Profile::Roles.admin(environment.id)]
  35 + @pre_population = [].to_json
41 36 end
42 37  
43 38 def add_role
... ... @@ -90,6 +85,7 @@ class ProfileMembersController &lt; MyProfileController
90 85 end
91 86  
92 87 def add_members
  88 + @roles = Profile::Roles.organization_member_roles(environment.id)
93 89 end
94 90  
95 91 def add_member
... ... @@ -122,19 +118,42 @@ class ProfileMembersController &lt; MyProfileController
122 118 render :layout => false
123 119 end
124 120  
125   - def find_users
126   - if !params[:query] || params[:query].length <= 2
127   - @users_found = []
128   - elsif params[:scope] == 'all_users'
129   - @users_found = Person.find_by_contents(params[:query] + '*').select {|user| !profile.members.include?(user)}
130   - @button_alt = _('Add member')
131   - @add_action = {:action => 'add_member'}
132   - elsif params[:scope] == 'new_admins'
133   - @users_found = Person.find_by_contents(params[:query] + '*').select {|user| profile.members.include?(user) && !profile.admins.include?(user)}
134   - @button_alt = _('Add member')
135   - @add_action = {:action => 'add_admin'}
  121 + def search_user
  122 + role = Role.find(params[:role])
  123 + render :text => environment.people.find(:all, :conditions => ['LOWER(name) LIKE ? OR LOWER(identifier) LIKE ?', "%#{params['q_'+role.key]}%", "%#{params['q_'+role.key]}%"]).
  124 + select { |person| !profile.members_by_role(role).include?(person) }.
  125 + map {|person| {:id => person.id, :name => person.name} }.
  126 + to_json
  127 + end
  128 +
  129 + def save_associations
  130 + error = false
  131 + roles = Profile::Roles.organization_member_roles(environment.id)
  132 + roles.select { |role| params['q_'+role.key] }.each do |role|
  133 + people = [Person.find(params['q_'+role.key].split(','))].flatten
  134 + to_remove = profile.members_by_role(role) - people
  135 + to_add = people - profile.members_by_role(role)
  136 +
  137 + begin
  138 + to_remove.each { |person| profile.disaffiliate(person, role) }
  139 + to_add.each { |person| profile.affiliate(person, role) }
  140 + rescue Exception => ex
  141 + logger.info ex
  142 + error = true
  143 + end
  144 + end
  145 +
  146 + if error
  147 + session[:notice] = _('The members list couldn\'t be updated. Please contact the administrator.')
  148 + redirect_to :action => 'add_members'
  149 + else
  150 + if profile.admins.blank? && !params[:last_admin]
  151 + redirect_to :action => 'last_admin'
  152 + else
  153 + session[:notice] = _('The members list was updated.')
  154 + redirect_to :controller => 'profile_editor'
  155 + end
136 156 end
137   - render :layout => false
138 157 end
139 158  
140 159 def send_mail
... ...
app/controllers/my_profile/tasks_controller.rb
... ... @@ -3,12 +3,13 @@ class TasksController &lt; MyProfileController
3 3 protect 'perform_task', :profile
4 4  
5 5 def index
6   - @tasks = profile.all_pending_tasks.sort_by(&:created_at)
  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 8 @failed = params ? params[:failed] : {}
8 9 end
9 10  
10 11 def processed
11   - @tasks = profile.all_finished_tasks.sort_by(&:created_at)
  12 + @tasks = Task.to(profile).finished.sort_by(&:created_at)
12 13 end
13 14  
14 15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ]
... ...
app/controllers/public/account_controller.rb
... ... @@ -4,7 +4,6 @@ class AccountController &lt; ApplicationController
4 4  
5 5 inverse_captcha :field => 'e_mail'
6 6  
7   -
8 7 before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise]
9 8 before_filter :redirect_if_logged_in, :only => [:login, :signup]
10 9  
... ... @@ -15,6 +14,17 @@ class AccountController &lt; ApplicationController
15 14 end
16 15 end
17 16  
  17 + def activate
  18 + @user = User.find_by_activation_code(params[:activation_code]) if params[:activation_code]
  19 + if @user and @user.activate
  20 + @message = _("Your account has been activated, now you can log in!")
  21 + render :action => 'login', :userlogin => @user.login
  22 + else
  23 + session[:notice] = _("It looks like you're trying to activate an account. Perhaps have already activated this account?")
  24 + redirect_to :controller => :home
  25 + end
  26 + end
  27 +
18 28 # action to perform login to the application
19 29 def login
20 30 @user = User.new
... ... @@ -57,9 +67,8 @@ class AccountController &lt; ApplicationController
57 67 @user.person_data = params[:profile_data]
58 68 @person = Person.new(params[:profile_data])
59 69 @person.environment = @user.environment
60   - if request.post? && params[self.icaptcha_field].blank?
  70 + if request.post?
61 71 @user.signup!
62   - self.current_user = @user
63 72 owner_role = Role.find_by_name('owner')
64 73 @user.person.affiliate(@user.person, [owner_role]) if owner_role
65 74 invitation = Task.find_by_code(@invitation_code)
... ... @@ -67,8 +76,7 @@ class AccountController &lt; ApplicationController
67 76 invitation.update_attributes!({:friend => @user.person})
68 77 invitation.finish
69 78 end
70   - session[:notice] = _("Thanks for signing up!")
71   - go_to_initial_page if redirect?
  79 + @register_pending = true
72 80 end
73 81 rescue ActiveRecord::RecordInvalid
74 82 @person.valid?
... ... @@ -223,6 +231,8 @@ class AccountController &lt; ApplicationController
223 231 session[:notice] = nil # consume the notice
224 232 end
225 233  
  234 + @plugins.enabled_plugins.each { |plugin| user_data.merge!(plugin.user_data_extras) }
  235 +
226 236 render :text => user_data.to_json, :layout => false, :content_type => "application/javascript"
227 237 end
228 238  
... ...
app/controllers/public/browse_controller.rb
... ... @@ -6,6 +6,8 @@ class BrowseController &lt; PublicController
6 6 more_recent
7 7 more_active
8 8 more_popular
  9 + more_comments
  10 + more_views
9 11 )
10 12  
11 13 def per_page
... ... @@ -36,6 +38,18 @@ class BrowseController &lt; PublicController
36 38 @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])
37 39 end
38 40  
  41 + def contents
  42 + @filter = filter
  43 + @title = self.filter_description(params[:action] + '_' + @filter )
  44 +
  45 + @results = @environment.articles.published.text_articles.send(@filter)
  46 +
  47 + if !params[:query].blank?
  48 + @results = @results.find_by_contents(params[:query])
  49 + end
  50 + @results = @results.compact.paginate(:per_page => per_page, :page => params[:page])
  51 + end
  52 +
39 53 protected
40 54  
41 55 def filter
... ... @@ -54,6 +68,9 @@ class BrowseController &lt; PublicController
54 68 'communities_more_recent' => _('More recent communities'),
55 69 'communities_more_active' => _('More active communities'),
56 70 'communities_more_popular' => _('More popular communities'),
  71 + 'contents_more_recent' => _('More recent contents'),
  72 + 'contents_more_views' => _('Most viewed contents'),
  73 + 'contents_more_comments' => _('Most commented contents'),
57 74 }[str] || str
58 75 end
59 76  
... ...
app/controllers/public/contact_controller.rb
... ... @@ -4,10 +4,9 @@ class ContactController &lt; PublicController
4 4  
5 5 needs_profile
6 6  
7   - inverse_captcha :field => 'e_mail'
8 7 def new
9 8 @contact
10   - if request.post? && params[self.icaptcha_field].blank? && params[:confirm] == 'true'
  9 + if request.post? && params[:confirm] == 'true'
11 10 @contact = user.build_contact(profile, params[:contact])
12 11 @contact.city = (!params[:city].blank? && City.exists?(params[:city])) ? City.find(params[:city]).name : nil
13 12 @contact.state = (!params[:state].blank? && State.exists?(params[:state])) ? State.find(params[:state]).name : nil
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -2,8 +2,6 @@ class ContentViewerController &lt; ApplicationController
2 2  
3 3 needs_profile
4 4  
5   - inverse_captcha :field => 'e_mail'
6   -
7 5 helper ProfileHelper
8 6 helper TagsHelper
9 7  
... ... @@ -68,8 +66,13 @@ class ContentViewerController &lt; ApplicationController
68 66  
69 67 @form_div = params[:form]
70 68  
71   - if request.post? && params[:comment] && params[self.icaptcha_field].blank? && params[:confirm] == 'true' && @page.accept_comments?
72   - add_comment
  69 + if params[:comment] && params[:confirm] == 'true'
  70 + @comment = Comment.new(params[:comment])
  71 + if request.post? && @page.accept_comments?
  72 + add_comment
  73 + end
  74 + else
  75 + @comment = Comment.new
73 76 end
74 77  
75 78 if request.post? && params[:remove_comment]
... ... @@ -106,11 +109,10 @@ class ContentViewerController &lt; ApplicationController
106 109 protected
107 110  
108 111 def add_comment
109   - @comment = Comment.new(params[:comment])
110 112 @comment.author = user if logged_in?
111 113 @comment.article = @page
112   - if @comment.save
113   - @page.update_attribute(:updated_at, Time.now)
  114 + if (logged_in? || verify_recaptcha(:model => @comment, :message => _('Please type the words correctly'))) && @comment.save
  115 + @page.touch
114 116 @comment = nil # clear the comment form
115 117 redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view]
116 118 else
... ...
app/controllers/public/profile_controller.rb
... ... @@ -2,8 +2,8 @@ class ProfileController &lt; PublicController
2 2  
3 3 needs_profile
4 4 before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add]
5   - before_filter :store_before_join, :only => [:join, :join_not_logged]
6   - before_filter :login_required, :only => [:add, :join, :join_not_logged, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_scraps, :view_more_activities, :view_more_network_activities]
  5 + before_filter :store_location, :only => [:join, :join_not_logged, :report_abuse]
  6 + before_filter :login_required, :only => [:add, :join, :join_not_logged, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_scraps, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report]
7 7  
8 8 helper TagsHelper
9 9  
... ... @@ -44,7 +44,7 @@ class ProfileController &lt; PublicController
44 44 tagged,
45 45 :title => _("%s's contents tagged with \"%s\"") % [profile.name, @tag],
46 46 :description => _("%s's contents tagged with \"%s\"") % [profile.name, @tag],
47   - :link => url_for(:action => 'tags')
  47 + :link => url_for(profile.url)
48 48 )
49 49 render :text => data, :content_type => "text/xml"
50 50 end
... ... @@ -71,6 +71,10 @@ class ProfileController &lt; PublicController
71 71 end
72 72 end
73 73  
  74 + def fans
  75 + @fans = profile.fans
  76 + end
  77 +
74 78 def favorite_enterprises
75 79 @favorite_enterprises = profile.favorite_enterprises
76 80 end
... ... @@ -96,7 +100,7 @@ class ProfileController &lt; PublicController
96 100 if request.post?
97 101 profile.add_member(user)
98 102 session[:notice] = _('%s administrator still needs to accept you as member.') % profile.name if profile.closed?
99   - redirect_to_before_join
  103 + redirect_to_previous_location
100 104 else
101 105 if user.memberships.include?(profile)
102 106 session[:notice] = _('You are already a member of %s.') % profile.name
... ... @@ -223,6 +227,52 @@ class ProfileController &lt; PublicController
223 227 end
224 228 end
225 229  
  230 + def report_abuse
  231 + @abuse_report = AbuseReport.new
  232 + render :layout => false
  233 + end
  234 +
  235 + def register_report
  236 + if !verify_recaptcha
  237 + render :text => {
  238 + :ok => false,
  239 + :error => {
  240 + :code => 1,
  241 + :message => _('You could not answer the captcha.')
  242 + }
  243 + }.to_json
  244 + else
  245 + begin
  246 + abuse_report = AbuseReport.new(params[:abuse_report])
  247 + if !params[:content_type].blank?
  248 + article = params[:content_type].constantize.find(params[:content_id])
  249 + abuse_report.content = instance_eval(&article.reported_version)
  250 + end
  251 +
  252 + user.register_report(abuse_report, profile)
  253 +
  254 + if !params[:content_type].blank?
  255 + abuse_report = AbuseReport.find_by_reporter_id_and_abuse_complaint_id(user.id, profile.opened_abuse_complaint.id)
  256 + Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article)
  257 + end
  258 +
  259 + render :text => {
  260 + :ok => true,
  261 + :message => _('Your abuse report was registered. The administrators are reviewing your report.'),
  262 + }.to_json
  263 + rescue Exception => exception
  264 + logger.error(exception.to_s)
  265 + render :text => {
  266 + :ok => false,
  267 + :error => {
  268 + :code => 2,
  269 + :message => _('Your report couldn\'t be saved due to some problem. Please contact the administrator.')
  270 + }
  271 + }.to_json
  272 + end
  273 + end
  274 + end
  275 +
226 276 protected
227 277  
228 278 def check_access_to_profile
... ... @@ -231,16 +281,16 @@ class ProfileController &lt; PublicController
231 281 end
232 282 end
233 283  
234   - def store_before_join
235   - if session[:before_join].nil?
236   - session[:before_join] = request.referer
  284 + def store_location
  285 + if session[:previous_location].nil?
  286 + session[:previous_location] = request.referer
237 287 end
238 288 end
239 289  
240   - def redirect_to_before_join
241   - back = session[:before_join]
  290 + def redirect_to_previous_location
  291 + back = session[:previous_location]
242 292 if back
243   - session[:before_join] = nil
  293 + session[:previous_location] = nil
244 294 redirect_to back
245 295 else
246 296 redirect_to profile.url
... ... @@ -259,7 +309,7 @@ class ProfileController &lt; PublicController
259 309 end
260 310  
261 311 def invisible_profile
262   - render_access_denied(_("Sorry, this profile was defined as private by its owner. You'll not be able to view content here unless the profile owner adds adds you."), _("Oops ... you cannot go ahead here"))
  312 + render_access_denied(_("This profile is inaccessible. You don't have the permission to view the content here."), _("Oops ... you cannot go ahead here"))
263 313 end
264 314  
265 315 def per_page
... ...
app/helpers/application_helper.rb
... ... @@ -209,7 +209,11 @@ module ApplicationHelper
209 209 the_class << ' ' << html_options[:class]
210 210 end
211 211 the_title = html_options[:title] || label
212   - link_to('&nbsp;'+content_tag('span', label), url, html_options.merge(:class => the_class, :title => the_title))
  212 + if html_options[:disabled]
  213 + content_tag('a', '&nbsp;'+content_tag('span', label), html_options.merge(:class => the_class, :title => the_title))
  214 + else
  215 + link_to('&nbsp;'+content_tag('span', label), url, html_options.merge(:class => the_class, :title => the_title))
  216 + end
213 217 end
214 218  
215 219 def button_to_function(type, label, js_code, html_options = {}, &block)
... ... @@ -257,30 +261,59 @@ module ApplicationHelper
257 261 concat(content_tag('div', capture(&block) + tag('br', :style => 'clear: left;'), { :class => 'button-bar' }.merge(options)))
258 262 end
259 263  
260   - def partial_for_class(klass)
261   - if klass.nil?
262   - raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?'
263   - end
  264 + VIEW_EXTENSIONS = %w[.rhtml .html.erb]
264 265  
  266 + def partial_for_class_in_view_path(klass, view_path)
  267 + return nil if klass.nil?
265 268 name = klass.name.underscore
266   - if File.exists?(File.join(RAILS_ROOT, 'app', 'views', params[:controller], "_#{name}.rhtml"))
267   - name
  269 +
  270 + search_name = String.new(name)
  271 + if search_name.include?("/")
  272 + search_name.gsub!(/(\/)([^\/]*)$/,'\1_\2')
  273 + name = File.join(params[:controller], name) if defined?(params) && params[:controller]
268 274 else
269   - partial_for_class(klass.superclass)
  275 + search_name = "_" + search_name
  276 + end
  277 +
  278 + VIEW_EXTENSIONS.each do |ext|
  279 + path = defined?(params) && params[:controller] ? File.join(view_path, params[:controller], search_name+ext) : File.join(view_path, search_name+ext)
  280 + return name if File.exists?(File.join(path))
270 281 end
  282 +
  283 + partial_for_class_in_view_path(klass.superclass, view_path)
271 284 end
272 285  
273   - def partial_for_task_class(klass, action)
274   - if klass.nil?
275   - raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?'
  286 + def partial_for_class(klass)
  287 + raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil?
  288 + name = klass.name.underscore
  289 + @controller.view_paths.each do |view_path|
  290 + partial = partial_for_class_in_view_path(klass, view_path)
  291 + return partial if partial
276 292 end
277 293  
  294 + raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?'
  295 + end
  296 +
  297 + def partial_for_task_class(klass, action)
  298 + raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil?
  299 +
278 300 name = "#{klass.name.underscore}_#{action.to_s}"
279   - if File.exists?(File.join(RAILS_ROOT, 'app', 'views', params[:controller], "_#{name}.rhtml"))
280   - name
281   - else
282   - partial_for_task_class(klass.superclass, action)
  301 + VIEW_EXTENSIONS.each do |ext|
  302 + return name if File.exists?(File.join(RAILS_ROOT, 'app', 'views', params[:controller], '_'+name+ext))
  303 + end
  304 +
  305 + partial_for_task_class(klass.superclass, action)
  306 + end
  307 +
  308 + def view_for_profile_actions(klass)
  309 + raise ArgumentError, 'No profile actions view for this class.' if klass.nil?
  310 +
  311 + name = klass.name.underscore
  312 + VIEW_EXTENSIONS.each do |ext|
  313 + return "blocks/profile_info_actions/"+name+ext if File.exists?(File.join(RAILS_ROOT, 'app', 'views', 'blocks', 'profile_info_actions', name+ext))
283 314 end
  315 +
  316 + view_for_profile_actions(klass.superclass)
284 317 end
285 318  
286 319 def user
... ... @@ -556,17 +589,18 @@ module ApplicationHelper
556 589  
557 590 def gravatar_url_for(email, options = {})
558 591 # Ta dando erro de roteamento
  592 + default = theme_option['gravatar'] || NOOSFERO_CONF['gravatar'] || nil
559 593 url_for( { :gravatar_id => Digest::MD5.hexdigest(email),
560 594 :host => 'www.gravatar.com',
561 595 :protocol => 'http://',
562 596 :only_path => false,
563 597 :controller => 'avatar.php',
564   - :d => NOOSFERO_CONF['gravatar'] ? NOOSFERO_CONF['gravatar'] : nil
  598 + :d => default
565 599 }.merge(options) )
566 600 end
567 601  
568 602 def str_gravatar_url_for(email, options = {})
569   - default = NOOSFERO_CONF['gravatar'] ? NOOSFERO_CONF['gravatar'] : nil
  603 + default = theme_option['gravatar'] || NOOSFERO_CONF['gravatar'] || nil
570 604 url = 'http://www.gravatar.com/avatar.php?gravatar_id=' +
571 605 Digest::MD5.hexdigest(email)
572 606 {
... ... @@ -870,7 +904,7 @@ module ApplicationHelper
870 904  
871 905 def template_stylesheet_path
872 906 if profile.nil?
873   - '/designs/templates/default/stylesheets/style.css'
  907 + "/designs/templates/#{environment.layout_template}/stylesheets/style.css"
874 908 else
875 909 "/designs/templates/#{profile.layout_template}/stylesheets/style.css"
876 910 end
... ... @@ -947,8 +981,10 @@ module ApplicationHelper
947 981 'thickbox',
948 982 'lightbox',
949 983 'colorpicker',
  984 + colorbox_stylesheet_path,
950 985 pngfix_stylesheet_path,
951   - ]
  986 + ] +
  987 + tokeninput_stylesheets
952 988 end
953 989  
954 990 # DEPRECATED. Do not use this·
... ... @@ -960,6 +996,14 @@ module ApplicationHelper
960 996 'iepngfix/iepngfix.css'
961 997 end
962 998  
  999 + def colorbox_stylesheet_path
  1000 + 'colorbox/colorbox.css'
  1001 + end
  1002 +
  1003 + def tokeninput_stylesheets
  1004 + ['token-input', 'token-input-facebook', 'token-input-mac']
  1005 + end
  1006 +
963 1007 def noosfero_layout_features
964 1008 render :file => 'shared/noosfero_layout_features'
965 1009 end
... ... @@ -975,7 +1019,10 @@ module ApplicationHelper
975 1019 def article_to_html(article, options = {})
976 1020 options.merge!(:page => params[:npage])
977 1021 content = article.to_html(options)
978   - return self.instance_eval(&content) if content.kind_of?(Proc)
  1022 + content = content.kind_of?(Proc) ? self.instance_eval(&content) : content
  1023 + @plugins && @plugins.enabled_plugins.each do |plugin|
  1024 + content = plugin.parse_content(content)
  1025 + end
979 1026 content
980 1027 end
981 1028  
... ... @@ -1083,6 +1130,17 @@ module ApplicationHelper
1083 1130 link_to(content_tag(:span, _('Communities Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-communities-trigger')
1084 1131 end
1085 1132  
  1133 + def browse_contents_menu
  1134 + links = [
  1135 + {s_('contents|More Comments') => {:href => url_for({:controller => 'browse', :action => 'contents', :filter => 'more_comments'})}},
  1136 + {s_('contents|More Views') => {:href => url_for({:controller => 'browse', :action => 'contents', :filter => 'more_views'})}},
  1137 + {s_('contents|More Recent') => {:href => url_for({:controller => 'browse', :action => 'contents', :filter => 'more_recent'})}}
  1138 + ]
  1139 +
  1140 + link_to(content_tag(:span, _('Contents'), :class => 'icon-blog'), {:controller => "browse", :action => 'contents'}, :id => 'submenu-contents') +
  1141 + link_to(content_tag(:span, _('Contents Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-contents-trigger')
  1142 + end
  1143 +
1086 1144 def pagination_links(collection, options={})
1087 1145 options = {:prev_label => '&laquo; ' + _('Previous'), :next_label => _('Next') + ' &raquo;'}.merge(options)
1088 1146 will_paginate(collection, options)
... ... @@ -1099,16 +1157,27 @@ module ApplicationHelper
1099 1157 result
1100 1158 end
1101 1159  
  1160 + def manage_enterprises
  1161 + if user && !user.enterprises.empty?
  1162 + enterprises_link = user.enterprises.map do |enterprise|
  1163 + 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])
  1164 + end
  1165 + render :partial => 'shared/manage_enterprises', :locals => {:enterprises_link => enterprises_link}
  1166 + end
  1167 + end
  1168 +
1102 1169 def usermenu_logged_in
1103 1170 pending_tasks_count = ''
1104   - if user && user.all_pending_tasks.count > 0
1105   - pending_tasks_count = link_to(user.all_pending_tasks.count.to_s, '/myprofile/{login}/tasks', :id => 'pending-tasks-count', :title => _("Manage your pending tasks"))
  1171 + count = user ? Task.to(user).pending.count : -1
  1172 + if count > 0
  1173 + pending_tasks_count = link_to(count.to_s, @environment.top_url + '/myprofile/{login}/tasks', :id => 'pending-tasks-count', :title => _("Manage your pending tasks"))
1106 1174 end
1107 1175  
1108   - (_('Welcome, %s') % link_to('<i></i><strong>{login}</strong>', '/{login}', :id => "homepage-link", :title => _('Go to your homepage'))) +
  1176 + (_("<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'))) +
1109 1177 render_environment_features(:usermenu) +
1110   - link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', { :controller => 'admin_panel', :action => 'index' }, :id => "controlpanel", :title => _("Configure the environment"), :class => 'admin-link', :style => 'display: none') +
1111   - link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', '/myprofile/{login}', :id => "controlpanel", :title => _("Configure your personal account and content")) +
  1178 + 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') +
  1179 + manage_enterprises.to_s +
  1180 + 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")) +
1112 1181 pending_tasks_count +
1113 1182 link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
1114 1183 end
... ... @@ -1206,4 +1275,39 @@ module ApplicationHelper
1206 1275 end
1207 1276 end
1208 1277  
  1278 + def render_dialog_error_messages(instance_name)
  1279 + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => instance_name }
  1280 + end
  1281 +
  1282 + def report_abuse(profile, type, content=nil)
  1283 + return if !user || user == profile
  1284 +
  1285 + url = { :controller => 'profile',
  1286 + :action => 'report_abuse',
  1287 + :profile => profile.identifier }
  1288 + url.merge!({:content_type => content.class.name, :content_id => content.id}) if content
  1289 + text = content_tag('span', _('Report abuse'))
  1290 + klass = 'report-abuse-action'
  1291 + already_reported_message = _('You already reported this profile.')
  1292 + report_profile_message = _('Report this profile for abusive behaviour')
  1293 +
  1294 + if type == :button
  1295 + if user.already_reported?(profile)
  1296 + button(:alert, text, url, :class => klass+' disabled', :disabled => true, :title => already_reported_message)
  1297 + else
  1298 + button(:alert, text, url, :class => klass, :title => report_profile_message)
  1299 + end
  1300 + elsif type == :link
  1301 + if user.already_reported?(profile)
  1302 + content_tag('a', text, :class => klass + ' disabled button with-text icon-alert', :title => already_reported_message)
  1303 + else
  1304 + link_to(text, url, :class => klass + ' button with-text icon-alert', :title => report_profile_message)
  1305 + end
  1306 + elsif type == :comment_link
  1307 + (user.already_reported?(profile) ?
  1308 + content_tag('a', text, :class => klass + ' disabled comment-footer comment-footer-link', :title => already_reported_message) :
  1309 + link_to(text, url, :class => klass + ' comment-footer comment-footer-link', :title => report_profile_message)
  1310 + ) + content_tag('span', ' | ', :class => 'comment-footer comment-footer-hide')
  1311 + end
  1312 + end
1209 1313 end
... ...
app/helpers/boxes_helper.rb
... ... @@ -99,7 +99,9 @@ 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   -
  102 + @controller.send(:content_editor?) || @plugins.enabled_plugins.each do |plugin|
  103 + result = plugin.parse_content(result)
  104 + end
103 105 box_decorator.block_target(block.box, block) +
104 106 content_tag('div',
105 107 content_tag('div',
... ...
app/helpers/catalog_helper.rb
... ... @@ -5,16 +5,24 @@ include ManageProductsHelper
5 5  
6 6 def display_products_list(profile, products)
7 7 data = ''
  8 + extra_content = []
  9 + extra_content_list = []
8 10 products.each { |product|
9   -
  11 + extra_content = @plugins.map(:catalog_item_extras, product).collect { |content| instance_eval(&content) } if @plugins
  12 + extra_content_list = @plugins.map(:catalog_list_item_extras, product).collect { |content| instance_eval(&content) } if @plugins
10 13 data << content_tag('li',
11   - link_to_product(product, :class => 'product-pic', :style => 'background-image:url(%s)' % ( product.image ? product.image.public_filename(:portrait) : '/images/icons-app/product-default-pic-portrait.png' )) +
  14 + link_to_product(product, :class => 'product-pic', :style => 'background-image:url(%s)' % product.default_image(:portrait) ) +
12 15 content_tag('h3', link_to_product(product)) +
13 16 content_tag('ul',
14 17 (product.price ? content_tag('li', _('Price: %s') % ( "%.2f" % product.price), :class => 'product_price') : '') +
15   - content_tag('li', product_category_name(profile, product.product_category), :class => 'product_category')
  18 + content_tag('li', product_category_name(profile, product.product_category), :class => 'product_category') +
  19 + extra_content_list.map { |content| content_tag('li', content)}.join("\n")
16 20 ) +
17   - (product.description ? content_tag('div', txt2html(product.description), :class => 'description') : tag('br', :style => 'clear:both')),
  21 + (product.description ? content_tag('div',
  22 + txt2html(product.description),
  23 + :class => 'description') : tag('br',
  24 + :style => 'clear:both')) +
  25 + extra_content.join("\n"),
18 26 :class => 'product')
19 27 }
20 28 content_tag('h1', _('Products/Services')) + content_tag('ul', data, :id => 'product_list')
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -22,9 +22,14 @@ module ContentViewerHelper
22 22 end
23 23 comments = ''
24 24 unless args[:no_comments] || !article.accept_comments
25   - comments = ("- %s") % link_to_comments(article)
  25 + comments = (" - %s") % link_to_comments(article)
26 26 end
27   - title << content_tag('span', _("%s, by %s %s") % [show_date(article.published_at), link_to(article.author_name, article.author.url), comments], :class => 'created-at')
  27 + title << content_tag('span',
  28 + content_tag('span', show_date(article.published_at), :class => 'date') +
  29 + content_tag('span', [_(", by %s") % link_to(article.author_name, article.author.url)], :class => 'author') +
  30 + content_tag('span', comments, :class => 'comments'),
  31 + :class => 'created-at'
  32 + )
28 33 end
29 34 title
30 35 end
... ... @@ -46,4 +51,21 @@ module ContentViewerHelper
46 51 end
47 52 end
48 53  
  54 + def addthis_facebook_url(article)
  55 + "http://www.facebook.com/sharer.php?s=100&p[title]=%{title}&p[summary]=%{summary}&p[url]=%{url}&p[images][0]=%{image}" % {
  56 + :title => CGI.escape(article.title),
  57 + :url => CGI.escape(url_for(article.url)),
  58 + :summary => CGI.escape(truncate(strip_tags(article.body.to_s), 300)),
  59 + :image => CGI.escape(article.body_images_paths.first.to_s)
  60 + }
  61 + end
  62 +
  63 + def addthis_image_tag
  64 + if File.exists?(File.join(Rails.root, 'public', theme_path, 'images', 'addthis.gif'))
  65 + image_tag(File.join(theme_path, 'images', 'addthis.gif'), :border => 0, :alt => '')
  66 + else
  67 + image_tag("/images/bt-bookmark.gif", :width => 53, :height => 16, :border => 0, :alt => '')
  68 + end
  69 + end
  70 +
49 71 end
... ...
app/helpers/dates_helper.rb
... ... @@ -42,11 +42,11 @@ module DatesHelper
42 42 end
43 43 end
44 44  
45   - def show_period(date1, date2 = nil)
  45 + def show_period(date1, date2 = nil, use_numbers = false)
46 46 if (date1 == date2) || (date2.nil?)
47   - show_date(date1)
  47 + show_date(date1, use_numbers)
48 48 else
49   - _('from %{date1} to %{date2}') % {:date1 => show_date(date1), :date2 => show_date(date2)}
  49 + _('from %{date1} to %{date2}') % {:date1 => show_date(date1, use_numbers), :date2 => show_date(date2, use_numbers)}
50 50 end
51 51 end
52 52  
... ...
app/helpers/display_helper.rb
... ... @@ -27,14 +27,18 @@ module DisplayHelper
27 27 end
28 28  
29 29 def txt2html(txt)
30   - txt.
31   - gsub( /\n\s*\n/, ' <p/> ' ).
32   - gsub( /\n/, ' <br/> ' ).
33   - gsub( /(^|\s)(www\.[^\s])/, '\1http://\2' ).
34   - gsub( /(https?:\/\/([^\s]+))/ ) do
35   - href, content = $1, $2.scan(/.{4}/).join('&#x200B;')
36   - content_tag(:a, content, :href => href, :target => '_blank', :rel => 'nofolow',
37   - :onclick => "return confirm('%s')" % escape_javascript(_('Are you sure you want to visit this web site?')))
  30 + txt.strip.
  31 + gsub( /\s*\n\s*\n\s*/, "\r<p/>\r" ).
  32 + gsub( /\s*\n\s*/, "\n<br/>\n" ).
  33 + gsub( /\r/, "\n" ).
  34 + gsub( /(^|\s)(www\.[^\s]+|https?:\/\/[^\s]+)/ ) do
  35 + pre_char, href = $1, $2
  36 + href = 'http://'+href if ! href.match /^https?:/
  37 + content = href.gsub(/^https?:\/\//, '').scan(/.{1,4}/).join('&#x200B;')
  38 + pre_char +
  39 + content_tag(:a, content, :href => href, :target => '_blank',
  40 + :rel => 'nofolow', :onclick => "return confirm('%s')" %
  41 + _('Are you sure you want to visit this web site?'))
38 42 end
39 43 end
40 44 end
... ...
app/helpers/events_helper.rb
... ... @@ -21,14 +21,13 @@ module EventsHelper
21 21 end
22 22  
23 23 def populate_calendar(selected_date, events)
  24 + events.reject! {|event| !event.display_to?(user)}
24 25 calendar = Event.date_range(selected_date.year, selected_date.month).map do |date|
25 26 [
26 27 # the day itself
27 28 date,
28 29 # is there any events in this date?
29   - events.select {|event| event.display_to?(user)}.any? do |event|
30   - event.date_range.include?(date)
31   - end,
  30 + events.any? {|event| event.date_range.include?(date)},
32 31 # is this date in the current month?
33 32 true
34 33 ]
... ...
app/helpers/profile_editor_helper.rb
... ... @@ -104,8 +104,8 @@ module ProfileEditorHelper
104 104 @country_helper ||= CountriesHelper.instance
105 105 end
106 106  
107   - def select_country(title, object, method, options)
108   - labelled_form_field(title, select(object, method, [[_('[Select ...]'), nil]] + country_helper.countries, {}, options))
  107 + def select_country(title, object, method, html_options = {}, options = {})
  108 + labelled_form_field(title, select(object, method, [[_('[Select ...]'), nil]] + country_helper.countries, options, html_options))
109 109 end
110 110  
111 111 def select_schooling(object, method, options)
... ...
app/helpers/sweeper_helper.rb
... ... @@ -22,7 +22,7 @@ module SweeperHelper
22 22  
23 23 # friends blocks
24 24 blocks = profile.blocks.select{|b| b.kind_of?(FriendsBlock)}
25   - blocks.map(&:cache_keys).each{|ck|expire_timeout_fragment(ck)}
  25 + blocks.map(&:cache_key).each{|ck|expire_timeout_fragment(ck)}
26 26 end
27 27  
28 28 def expire_communities(profile)
... ... @@ -34,13 +34,13 @@ module SweeperHelper
34 34  
35 35 # communities block
36 36 blocks = profile.blocks.select{|b| b.kind_of?(CommunitiesBlock)}
37   - blocks.map(&:cache_keys).each{|ck|expire_timeout_fragment(ck)}
  37 + blocks.map(&:cache_key).each{|ck|expire_timeout_fragment(ck)}
38 38 end
39 39  
40 40 def expire_enterprises(profile)
41 41 # enterprises and favorite enterprises blocks
42 42 blocks = profile.blocks.select {|b| [EnterprisesBlock, FavoriteEnterprisesBlock].any?{|klass| b.kind_of?(klass)} }
43   - blocks.map(&:cache_keys).each{|ck|expire_timeout_fragment(ck)}
  43 + blocks.map(&:cache_key).each{|ck|expire_timeout_fragment(ck)}
44 44 end
45 45  
46 46 def expire_profile_index(profile)
... ...
app/helpers/tags_helper.rb
... ... @@ -28,7 +28,6 @@ module TagsHelper
28 28 # courtesy of Aurelio: http://www.colivre.coop.br/Aurium/Nuvem
29 29 # (pt_BR only).
30 30 def tag_cloud(tags, tagname_option, url, options = {})
31   -
32 31  
33 32 return content_tag('em', _('No tags yet.')) +
34 33 ' <a href="' + _('http://en.wikipedia.org/wiki/Tag_%28metadata%29') +
... ... @@ -42,7 +41,7 @@ module TagsHelper
42 41 max = tags.values.max.to_f
43 42 min = tags.values.min.to_f
44 43  
45   - tags.map do |tag,count|
  44 + tags.sort_by{ |k,v| k.downcase }.map do |tag,count|
46 45 if ( max == min )
47 46 v = 0.5
48 47 else
... ...
app/models/abuse_complaint.rb 0 → 100644
... ... @@ -0,0 +1,75 @@
  1 +class AbuseComplaint < Task
  2 + has_many :abuse_reports, :dependent => :destroy
  3 + belongs_to :reported, :class_name => "Profile", :foreign_key => "requestor_id"
  4 +
  5 + validates_presence_of :reported
  6 + alias :requestor :reported
  7 +
  8 + def initialize(*args)
  9 + super
  10 + self.status = (args.first ? args.first[:status] : nil) || Task::Status::HIDDEN
  11 + end
  12 +
  13 + after_update do |abuse_complaint|
  14 + if abuse_complaint.reported.environment.reports_lower_bound < abuse_complaint.abuse_reports.count && abuse_complaint.status == Task::Status::HIDDEN
  15 + abuse_complaint.activate
  16 + end
  17 + end
  18 +
  19 + def target
  20 + reported.environment
  21 + end
  22 +
  23 + def environment
  24 + target
  25 + end
  26 +
  27 + def title
  28 + abuse_reports.count > 1 ? (_('Abuse complaint (%s)') % abuse_reports.count) :_('Abuse complaint')
  29 + end
  30 +
  31 + def linked_subject
  32 + {:text => reported.name, :url => reported.url}
  33 + end
  34 +
  35 + def information
  36 + {:message => _('%{linked_subject} was reported due to inappropriate behavior.')}
  37 + end
  38 +
  39 + def accept_details
  40 + true
  41 + end
  42 +
  43 + def reject_details
  44 + true
  45 + end
  46 +
  47 + def icon
  48 + {:type => :profile_image, :profile => reported, :url => reported.url}
  49 + end
  50 +
  51 + def default_decision
  52 + 'skip'
  53 + end
  54 +
  55 + def perform
  56 + reported.disable
  57 + end
  58 +
  59 + def task_activated_message
  60 + _('Your profile was reported by the users of %s due to inappropriate behavior. The administrators of the environment are now reviewing the report. To solve this misunderstanding, please contact the administrators.') % environment.name
  61 + end
  62 +
  63 + def task_finished_message
  64 + _('Your profile was disabled by the administrators of %s due to inappropriate behavior. To solve this misunderstanding please contact them.') % environment.name
  65 + end
  66 +
  67 + def target_notification_description
  68 + _('%s was reported due to inappropriate behavior.') % reported.name
  69 + end
  70 +
  71 + def target_notification_message
  72 + _('The users of %{environment} reported %{reported} due to inappropriate behavior. A task was created with all the reports including the reasons and contents reported by these users. Please verify the reports and decide whether this profile must be disabled or not.') % {:environment => environment.name, :reported => reported.name}
  73 + end
  74 +
  75 +end
... ...
app/models/abuse_report.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +class AbuseReport < ActiveRecord::Base
  2 +
  3 + belongs_to :reporter, :class_name => 'Person'
  4 + belongs_to :abuse_complaint
  5 + has_many :reported_images, :dependent => :destroy
  6 +
  7 + validates_presence_of :reporter, :abuse_complaint, :reason
  8 + validates_uniqueness_of :reporter_id, :scope => :abuse_complaint_id
  9 +
  10 + xss_terminate :sanitize => [:reason]
  11 +
  12 + after_create do |abuse_report|
  13 + abuse_report.abuse_complaint.save!
  14 + end
  15 +end
... ...
app/models/approve_article.rb
... ... @@ -77,6 +77,10 @@ class ApproveArticle &lt; Task
77 77 true
78 78 end
79 79  
  80 + def reject_details
  81 + true
  82 + end
  83 +
80 84 def default_decision
81 85 if article
82 86 'skip'
... ... @@ -90,7 +94,11 @@ class ApproveArticle &lt; Task
90 94 end
91 95  
92 96 def target_notification_description
93   - _('%{requestor} wants to publish the article: %{article}.') % {:requestor => requestor.name, :article => article.name}
  97 + if article
  98 + _('%{requestor} wants to publish the article: %{article}.') % {:requestor => requestor.name, :article => article.name}
  99 + else
  100 + _('%{requestor} wanted to publish an article but it was removed.') % {:requestor => requestor.name}
  101 + end
94 102 end
95 103  
96 104 def target_notification_message
... ... @@ -107,4 +115,11 @@ class ApproveArticle &lt; Task
107 115 end
108 116 end
109 117  
  118 + def task_cancelled_message
  119 + message = _('Your request for publishing the article "{article}" was rejected.')
  120 + if !reject_explanation.blank?
  121 + message += " " + _("Here is the reject explanation left by the administrator who rejected your article: \n\n%{reject_explanation}") % {:reject_explanation => reject_explanation}
  122 + end
  123 + end
  124 +
110 125 end
... ...
app/models/article.rb
... ... @@ -188,6 +188,14 @@ class Article &lt; ActiveRecord::Base
188 188 body || ''
189 189 end
190 190  
  191 + include ApplicationHelper
  192 + def reported_version(options = {})
  193 + article = self
  194 + search_path = File.join(Rails.root, 'app', 'views', 'shared', 'reported_versions')
  195 + partial_path = File.join('shared', 'reported_versions', partial_for_class_in_view_path(article.class, search_path))
  196 + lambda { render_to_string(:partial => partial_path, :locals => {:article => article}) }
  197 + end
  198 +
191 199 # returns the data of the article. Must be overriden in each subclass to
192 200 # provide the correct content for the article.
193 201 def data
... ... @@ -265,6 +273,10 @@ class Article &lt; ActiveRecord::Base
265 273 false
266 274 end
267 275  
  276 + def uploaded_file?
  277 + false
  278 + end
  279 +
268 280 def has_posts?
269 281 false
270 282 end
... ... @@ -343,11 +355,20 @@ class Article &lt; ActiveRecord::Base
343 355 ['Folder', 'Blog', 'Forum', 'Gallery']
344 356 end
345 357  
  358 + def self.text_article_types
  359 + ['TextArticle', 'TextileArticle', 'TinyMceArticle']
  360 + end
  361 +
346 362 named_scope :published, :conditions => { :published => true }
347 363 named_scope :folders, :conditions => { :type => folder_types}
348 364 named_scope :no_folders, :conditions => ['type NOT IN (?)', folder_types]
349 365 named_scope :galleries, :conditions => { :type => 'Gallery' }
350 366 named_scope :images, :conditions => { :is_image => true }
  367 + named_scope :text_articles, :conditions => [ 'articles.type IN (?)', text_article_types ]
  368 +
  369 + named_scope :more_comments, :order => "comments_count DESC"
  370 + named_scope :more_views, :order => "hits DESC"
  371 + named_scope :more_recent, :order => "created_at DESC"
351 372  
352 373 def self.display_filter(user, profile)
353 374 return {:conditions => ['published = ?', true]} if !user
... ... @@ -509,6 +530,35 @@ class Article &lt; ActiveRecord::Base
509 530 self.parent && self.parent.accept_uploads?
510 531 end
511 532  
  533 + def body_images_paths
  534 + require 'uri'
  535 + Hpricot(self.body.to_s).search('img[@src]').collect do |i|
  536 + (self.profile && self.profile.environment) ? URI.join(self.profile.environment.top_url, URI.escape(i.attributes['src'])).to_s : i.attributes['src']
  537 + end
  538 + end
  539 +
  540 + def more_comments_label
  541 + amount = self.comments_count
  542 + {
  543 + 0 => _('no comments'),
  544 + 1 => _('one comment')
  545 + }[amount] || _("%s comments") % amount
  546 +
  547 + end
  548 +
  549 + def more_views_label
  550 + amount = self.hits
  551 + {
  552 + 0 => _('no views'),
  553 + 1 => _('one view')
  554 + }[amount] || _("%s views") % amount
  555 +
  556 + end
  557 +
  558 + def more_recent_label
  559 + _('Created at: ')
  560 + end
  561 +
512 562 private
513 563  
514 564 def sanitize_tag_list
... ...
app/models/block.rb
... ... @@ -34,6 +34,12 @@ class Block &lt; ActiveRecord::Base
34 34 else
35 35 return context[:request_path] == '/'
36 36 end
  37 + elsif display == 'except_home_page'
  38 + if context[:article]
  39 + return context[:article] != owner.home_page
  40 + else
  41 + return context[:request_path] != '/' + owner.identifier
  42 + end
37 43 end
38 44 end
39 45 true
... ... @@ -45,6 +51,8 @@ class Block &lt; ActiveRecord::Base
45 51 # * <tt>'never'</tt>: the block is hidden (it does not appear for visitors)
46 52 # * <tt>'home_page_only'</tt> the block is displayed only when viewing the
47 53 # homepage of its owner.
  54 + # * <tt>'except_home_page'</tt> the block is displayed only when viewing
  55 + # the homepage of its owner.
48 56 settings_items :display, :type => :string, :default => 'always'
49 57  
50 58 # The block can be configured to be displayed in all languages or in just one language. It can assume any locale of the environment:
... ... @@ -119,10 +127,6 @@ class Block &lt; ActiveRecord::Base
119 127 true
120 128 end
121 129  
122   - def cache_keys
123   - "block-id-#{id}"
124   - end
125   -
126 130 def timeout
127 131 4.hours
128 132 end
... ...
app/models/blog.rb
... ... @@ -72,4 +72,8 @@ class Blog &lt; Folder
72 72  
73 73 alias :display_posts_in_current_language? :display_posts_in_current_language
74 74  
  75 + def empty?
  76 + posts.empty?
  77 + end
  78 +
75 79 end
... ...
app/models/comment.rb
... ... @@ -2,7 +2,7 @@ class Comment &lt; ActiveRecord::Base
2 2  
3 3 track_actions :leave_comment, :after_create, :keep_params => ["article.title", "article.url", "title", "url", "body"], :custom_target => :action_tracker_target
4 4  
5   - validates_presence_of :title, :body
  5 + validates_presence_of :body
6 6 belongs_to :article, :counter_cache => true
7 7 belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id'
8 8 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
... ... @@ -98,6 +98,16 @@ class Comment &lt; ActiveRecord::Base
98 98 root
99 99 end
100 100  
  101 + include ApplicationHelper
  102 + def reported_version(options = {})
  103 + comment = self
  104 + lambda { render_to_string(:partial => 'shared/reported_versions/comment', :locals => {:comment => comment}) }
  105 + end
  106 +
  107 + def to_html(option={})
  108 + body || ''
  109 + end
  110 +
101 111 class Notifier < ActionMailer::Base
102 112 def mail(comment)
103 113 profile = comment.article.profile
... ...
app/models/community.rb
... ... @@ -75,4 +75,8 @@ class Community &lt; Organization
75 75 end
76 76 end
77 77  
  78 + def control_panel_settings_button
  79 + {:title => __('Community Info and settings'), :icon => 'edit-profile-group'}
  80 + end
  81 +
78 82 end
... ...
app/models/contact.rb
... ... @@ -23,23 +23,26 @@ class Contact &lt; ActiveRecord::Base #WithoutTable
23 23  
24 24 class Sender < ActionMailer::Base
25 25 def mail(contact)
  26 + content_type 'text/html'
26 27 emails = contact.dest.notification_emails
27 28 recipients emails
28   - from "#{contact.name} <#{contact.email}>"
  29 + from "#{contact.name} <#{contact.dest.environment.contact_email}>"
  30 + reply_to contact.email
29 31 if contact.sender
30 32 headers 'X-Noosfero-Sender' => contact.sender.identifier
31 33 end
32 34 if contact.receive_a_copy
33 35 cc "#{contact.name} <#{contact.email}>"
34 36 end
35   - subject contact.subject
  37 + subject "[#{contact.dest.short_name(30)}] " + contact.subject
36 38 body :name => contact.name,
37 39 :email => contact.email,
38 40 :city => contact.city,
39 41 :state => contact.state,
40 42 :message => contact.message,
41 43 :environment => contact.dest.environment.name,
42   - :url => url_for(:host => contact.dest.environment.default_hostname, :controller => 'home')
  44 + :url => url_for(:host => contact.dest.environment.default_hostname, :controller => 'home'),
  45 + :target => contact.dest.name
43 46 end
44 47 end
45 48  
... ...
app/models/domain.rb
... ... @@ -10,7 +10,7 @@ class Domain &lt; ActiveRecord::Base
10 10  
11 11 # <tt>name</tt> must be a sequence of word characters (a to z, plus 0 to 9,
12 12 # plus '_'). Letters must be lowercase
13   - validates_format_of :name, :with => /^([a-z0-9_]+\.)+[a-z0-9_]+$/, :message => N_('%{fn} must be composed only of lowercase latters (a to z), numbers (0 to 9) and "_"')
  13 + validates_format_of :name, :with => /^([a-z0-9_-]+\.)+[a-z0-9_-]+$/, :message => N_('%{fn} must be composed only of lowercase latters (a to z), numbers (0 to 9), "_" and "-"')
14 14  
15 15 # checks validations that could not be expressed using Rails' predefined
16 16 # validations. In particular:
... ...
app/models/enterprise.rb
... ... @@ -7,6 +7,8 @@ class Enterprise &lt; Organization
7 7 has_many :products, :dependent => :destroy, :order => 'name ASC'
8 8 has_many :inputs, :through => :products
9 9  
  10 + has_and_belongs_to_many :fans, :class_name => 'Person', :join_table => 'favorite_enteprises_people'
  11 +
10 12 extra_data_for_index :product_categories
11 13  
12 14 N_('Organization website'); N_('Historic and current context'); N_('Activities short description'); N_('City'); N_('State'); N_('Country'); N_('ZIP code')
... ... @@ -145,6 +147,7 @@ class Enterprise &lt; Organization
145 147 end
146 148  
147 149 before_create do |enterprise|
  150 + enterprise.validated = enterprise.environment.enabled?('enterprises_are_validated_when_created')
148 151 if enterprise.environment.enabled?('enterprises_are_disabled_when_created')
149 152 enterprise.enabled = false
150 153 end
... ... @@ -165,4 +168,12 @@ class Enterprise &lt; Organization
165 168 enable_contact_us
166 169 end
167 170  
  171 + def control_panel_settings_button
  172 + {:title => __('Enterprise Info and settings'), :icon => 'edit-profile-enterprise'}
  173 + end
  174 +
  175 + def create_product?
  176 + true
  177 + end
  178 +
168 179 end
... ...
app/models/environment.rb
... ... @@ -9,6 +9,13 @@ class Environment &lt; ActiveRecord::Base
9 9  
10 10 has_many :tasks, :dependent => :destroy, :as => 'target'
11 11  
  12 + IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/
  13 +
  14 + def self.verify_filename(filename)
  15 + filename += '.txt' if File.extname(filename) =~ IDENTIFY_SCRIPTS
  16 + filename
  17 + end
  18 +
12 19 PERMISSIONS['Environment'] = {
13 20 'view_environment_admin_panel' => N_('View environment admin panel'),
14 21 'edit_environment_features' => N_('Edit environment features'),
... ... @@ -67,6 +74,10 @@ class Environment &lt; ActiveRecord::Base
67 74 self.affiliate(user, Environment::Roles.admin(self.id))
68 75 end
69 76  
  77 + def remove_admin(user)
  78 + self.disaffiliate(user, Environment::Roles.admin(self.id))
  79 + end
  80 +
70 81 def admins
71 82 Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', Environment::Roles.admin(self).id])
72 83 end
... ... @@ -106,6 +117,7 @@ class Environment &lt; ActiveRecord::Base
106 117 'enable_organization_url_change' => _("Allow organizations to change their URL"),
107 118 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"),
108 119 'enterprises_are_disabled_when_created' => __('Enterprises are disabled when created'),
  120 + 'enterprises_are_validated_when_created' => __('Enterprises are validated when created'),
109 121 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'),
110 122 'xmpp_chat' => _('XMPP/Jabber based chat'),
111 123 'show_zoom_button_on_article_images' => _('Show a zoom link on all article images')
... ... @@ -218,7 +230,19 @@ class Environment &lt; ActiveRecord::Base
218 230 settings_items :currency_separator, :type => String, :default => '.'
219 231 settings_items :currency_delimiter, :type => String, :default => ','
220 232  
221   - settings_items :trusted_sites_for_iframe, :type => Array, :default => ['itheora.org', 'tv.softwarelivre.org', 'stream.softwarelivre.org']
  233 + settings_items :trusted_sites_for_iframe, :type => Array, :default => %w[
  234 + developer.myspace.com
  235 + itheora.org
  236 + maps.google.com
  237 + platform.twitter.com
  238 + player.vimeo.com
  239 + stream.softwarelivre.org
  240 + tv.softwarelivre.org
  241 + www.facebook.com
  242 + www.flickr.com
  243 + www.gmodules.com
  244 + www.youtube.com
  245 + ] + ('a' .. 'z').map{|i| "#{i}.yimg.com"}
222 246  
223 247 settings_items :enabled_plugins, :type => Array, :default => []
224 248  
... ... @@ -228,17 +252,28 @@ class Environment &lt; ActiveRecord::Base
228 252  
229 253 # Enables a feature identified by its name
230 254 def enable(feature)
231   - self.settings["#{feature}_enabled"] = true
  255 + self.settings["#{feature}_enabled".to_sym] = true
  256 + end
  257 +
  258 + def enable_plugin(plugin)
  259 + self.enabled_plugins += [plugin]
  260 + self.enabled_plugins.uniq!
  261 + self.save!
232 262 end
233 263  
234 264 # Disables a feature identified by its name
235 265 def disable(feature)
236   - self.settings["#{feature}_enabled"] = false
  266 + self.settings["#{feature}_enabled".to_sym] = false
  267 + end
  268 +
  269 + def disable_plugin(plugin)
  270 + self.enabled_plugins.delete(plugin)
  271 + self.save!
237 272 end
238 273  
239 274 # Tells if a feature, identified by its name, is enabled
240 275 def enabled?(feature)
241   - self.settings["#{feature}_enabled"] == true
  276 + self.settings["#{feature}_enabled".to_sym] == true
242 277 end
243 278  
244 279 # enables the features identified by <tt>features</tt>, which is expected to
... ... @@ -494,6 +529,7 @@ class Environment &lt; ActiveRecord::Base
494 529 xss_terminate :only => [ :message_for_disabled_enterprise ], :with => 'white_list', :on => 'validation'
495 530  
496 531 validates_presence_of :theme
  532 + validates_numericality_of :reports_lower_bound, :allow_nil => false, :only_integer => true, :greater_than_or_equal_to => 0
497 533  
498 534 include WhiteListFilter
499 535 filter_iframes :message_for_disabled_enterprise, :whitelist => lambda { trusted_sites_for_iframe }
... ... @@ -517,12 +553,12 @@ class Environment &lt; ActiveRecord::Base
517 553 # If #force_www is true, adds 'www.' at the beginning of the hostname. If the
518 554 # environment has not associated domains, returns 'localhost'.
519 555 def default_hostname(email_hostname = false)
520   - if self.domains(true).empty?
521   - 'localhost'
522   - else
  556 + domain = 'localhost'
  557 + unless self.domains(true).empty?
523 558 domain = (self.domains.find_by_is_default(true) || self.domains.find(:first, :order => 'id')).name
524   - email_hostname ? domain : (force_www ? ('www.' + domain) : domain)
  559 + domain = email_hostname ? domain : (force_www ? ('www.' + domain) : domain)
525 560 end
  561 + domain
526 562 end
527 563  
528 564 def top_url
... ... @@ -671,13 +707,14 @@ class Environment &lt; ActiveRecord::Base
671 707 def create_templates
672 708 pre = self.name.to_slug + '_'
673 709 ent_id = Enterprise.create!(:name => 'Enterprise template', :identifier => pre + 'enterprise_template', :environment => self, :visible => false).id
  710 + inactive_enterprise_tmpl = Enterprise.create!(:name => 'Inactive Enterprise template', :identifier => pre + 'inactive_enterprise_template', :environment => self, :visible => false)
674 711 com_id = Community.create!(:name => 'Community template', :identifier => pre + 'community_template', :environment => self, :visible => false).id
675 712 pass = Digest::MD5.hexdigest rand.to_s
676 713 user = User.create!(:login => (pre + 'person_template'), :email => (pre + 'template@template.noo'), :password => pass, :password_confirmation => pass, :environment => self).person
677   - user.visible = false
678   - user.save!
  714 + user.update_attributes(:visible => false, :name => "Person template")
679 715 usr_id = user.id
680 716 self.settings[:enterprise_template_id] = ent_id
  717 + self.inactive_enterprise_template = inactive_enterprise_tmpl
681 718 self.settings[:community_template_id] = com_id
682 719 self.settings[:person_template_id] = usr_id
683 720 self.save!
... ... @@ -685,7 +722,7 @@ class Environment &lt; ActiveRecord::Base
685 722  
686 723 after_destroy :destroy_templates
687 724 def destroy_templates
688   - [enterprise_template, community_template, person_template].compact.each do |template|
  725 + [enterprise_template, inactive_enterprise_template, community_template, person_template].compact.each do |template|
689 726 template.destroy
690 727 end
691 728 end
... ... @@ -701,6 +738,4 @@ class Environment &lt; ActiveRecord::Base
701 738 def image_galleries
702 739 portal_community ? portal_community.image_galleries : []
703 740 end
704   -
705 741 end
706   -
... ...
app/models/fans_block.rb 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +class FansBlock < ProfileListBlock
  2 +
  3 + def self.description
  4 + _('Displays the people who like the enterprise')
  5 + end
  6 +
  7 + def default_title
  8 + n__('{#} fan', '{#} fans', profile_count)
  9 + end
  10 +
  11 + def help
  12 + _('This block presents the fans of an enterprise.')
  13 + end
  14 +
  15 + def footer
  16 + profile = self.owner
  17 + lambda do
  18 + link_to _('View all'), :profile => profile.identifier, :controller =>
  19 + 'profile', :action => 'fans'
  20 + end
  21 + end
  22 +
  23 + def profiles
  24 + owner.fans
  25 + end
  26 +
  27 + def profile_count
  28 + profiles.visible.count
  29 + end
  30 +
  31 +end
... ...
app/models/folder.rb
... ... @@ -29,7 +29,7 @@ class Folder &lt; Article
29 29 def to_html(options = {})
30 30 folder = self
31 31 lambda do
32   - render :file => 'content_viewer/folder', :locals => { :folder => folder }
  32 + render :file => 'content_viewer/folder', :locals => {:folder => folder}
33 33 end
34 34 end
35 35  
... ...
app/models/image.rb
1 1 class Image < ActiveRecord::Base
2   - belongs_to :owner, :polymorphic => true
3 2  
4 3 def self.max_size
5 4 Image.attachment_options[:max_size]
6 5 end
7 6  
  7 + sanitize_filename
  8 +
8 9 has_attachment :content_type => :image,
9 10 :storage => :file_system,
10 11 :path_prefix => 'public/image_uploads',
... ... @@ -20,4 +21,6 @@ class Image &lt; ActiveRecord::Base
20 21  
21 22 delay_attachment_fu_thumbnails
22 23  
  24 + postgresql_attachment_fu
  25 +
23 26 end
... ...
app/models/invitation.rb
... ... @@ -27,6 +27,10 @@ class Invitation &lt; Task
27 27 TaskMailer.deliver_invitation_notification(task) unless task.friend
28 28 end
29 29  
  30 + def title
  31 + _('Invitation')
  32 + end
  33 +
30 34 def validate
31 35 super
32 36 email = friend ? friend.user.email : friend_email
... ...
app/models/invite_friend.rb
... ... @@ -8,7 +8,7 @@ class InviteFriend &lt; Invitation
8 8 end
9 9  
10 10 def title
11   - _("New friend")
  11 + _("Friend invitation")
12 12 end
13 13  
14 14 def information
... ...
app/models/link_list_block.rb
... ... @@ -57,7 +57,7 @@ class LinkListBlock &lt; Block
57 57 def link_html(link)
58 58 klass = 'icon-' + link[:icon] if link[:icon]
59 59 sanitize_link(
60   - link_to(_(link[:name]), expand_address(link[:address]), :class => klass)
  60 + link_to(link[:name], expand_address(link[:address]), :class => klass)
61 61 )
62 62 end
63 63  
... ...
app/models/organization.rb
... ... @@ -95,12 +95,12 @@ class Organization &lt; Profile
95 95 []
96 96 end
97 97  
98   - N_('Display name'); N_('Description'); N_('Contact person'); N_('Contact email'); N_('Acronym'); N_('Foundation year'); N_('Legal form'); N_('Economic activity'); N_('Management information'); N_('Validated'); N_('Tag list')
99   - settings_items :display_name, :description, :contact_person, :contact_email, :acronym, :foundation_year, :legal_form, :economic_activity, :management_information, :validated, :cnpj
  98 + N_('Display name'); N_('Description'); N_('Contact person'); N_('Contact email'); N_('Acronym'); N_('Foundation year'); N_('Legal form'); N_('Economic activity'); N_('Management information'); N_('Tag list')
  99 + settings_items :display_name, :description, :contact_person, :contact_email, :acronym, :foundation_year, :legal_form, :economic_activity, :management_information
100 100  
101 101 validates_format_of :foundation_year, :with => Noosfero::Constants::INTEGER_FORMAT
102   -
103 102 validates_format_of :contact_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda { |org| !org.contact_email.blank? })
  103 + validates_as_cnpj :cnpj
104 104  
105 105 xss_terminate :only => [ :acronym, :contact_person, :contact_email, :legal_form, :economic_activity, :management_information ], :on => 'validation'
106 106  
... ... @@ -149,4 +149,12 @@ class Organization &lt; Profile
149 149 false
150 150 end
151 151  
  152 + def members_to_json
  153 + members.map { |member| {:id => member.id, :name => member.name} }.to_json
  154 + end
  155 +
  156 + def members_by_role_to_json(role)
  157 + members_by_role(role).map { |member| {:id => member.id, :name => member.name} }.to_json
  158 + end
  159 +
152 160 end
... ...
app/models/person.rb
... ... @@ -17,6 +17,8 @@ class Person &lt; Profile
17 17  
18 18 has_many :requested_tasks, :class_name => 'Task', :foreign_key => :requestor_id, :dependent => :destroy
19 19  
  20 + has_many :abuse_reports, :foreign_key => 'reporter_id', :dependent => :destroy
  21 +
20 22 has_many :mailings
21 23  
22 24 has_many :scraps_sent, :class_name => 'Scrap', :foreign_key => :sender_id, :dependent => :destroy
... ... @@ -116,6 +118,8 @@ class Person &lt; Profile
116 118 description
117 119 ]
118 120  
  121 + validates_multiparameter_assignments
  122 +
119 123 def self.fields
120 124 FIELDS
121 125 end
... ... @@ -387,6 +391,21 @@ class Person &lt; Profile
387 391 leave_hash.to_json
388 392 end
389 393  
  394 + def already_reported?(profile)
  395 + abuse_reports.any? { |report| report.abuse_complaint.reported == profile && report.abuse_complaint.opened? }
  396 + end
  397 +
  398 + def register_report(abuse_report, profile)
  399 + AbuseComplaint.create!(:reported => profile, :target => profile.environment) if !profile.opened_abuse_complaint
  400 + abuse_report.abuse_complaint = profile.opened_abuse_complaint
  401 + abuse_report.reporter = self
  402 + abuse_report.save!
  403 + end
  404 +
  405 + def control_panel_settings_button
  406 + {:title => _('Profile Info and settings'), :icon => 'edit-profile'}
  407 + end
  408 +
390 409 protected
391 410  
392 411 def followed_by?(profile)
... ...
app/models/product.rb
... ... @@ -66,7 +66,7 @@ class Product &lt; ActiveRecord::Base
66 66 end
67 67  
68 68 def default_image(size='thumb')
69   - '/images/icons-app/product-default-pic-%s.png' % size
  69 + image ? image.public_filename(size) : '/images/icons-app/product-default-pic-%s.png' % size
70 70 end
71 71  
72 72 def category_full_name
... ... @@ -149,4 +149,8 @@ class Product &lt; ActiveRecord::Base
149 149 unit.blank? ? name : "#{name} - #{unit.name.downcase}"
150 150 end
151 151  
  152 + def display_supplier_on_search?
  153 + true
  154 + end
  155 +
152 156 end
... ...
app/models/products_block.rb
... ... @@ -20,7 +20,15 @@ class ProductsBlock &lt; Block
20 20 block_title(title) +
21 21 content_tag(
22 22 'ul',
23   - products.map {|product| content_tag('li', link_to(product.name, product.url, :style => 'background-image:url(%s)' % ( product.image ? product.image.public_filename(:minor) : product.default_image('minor'))), :class => 'product' )}
  23 + products.map {|product|
  24 + content_tag('li',
  25 + link_to( product.name,
  26 + product.url,
  27 + :style => 'background-image:url(%s)' % product.default_image('minor')
  28 + ),
  29 + :class => 'product'
  30 + )
  31 + }
24 32 )
25 33 end
26 34  
... ...
app/models/profile.rb
... ... @@ -52,8 +52,9 @@ class Profile &lt; ActiveRecord::Base
52 52 acts_as_accessible
53 53  
54 54 named_scope :memberships_of, lambda { |person| { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.accessor_type = ? AND role_assignments.accessor_id = ?', person.class.base_class.name, person.id ] } }
55   - named_scope :enterprises, :conditions => "profiles.type = 'Enterprise'"
56   - named_scope :communities, :conditions => "profiles.type = 'Community'"
  55 + #FIXME: these will work only if the subclass is already loaded
  56 + named_scope :enterprises, lambda { {:conditions => (Enterprise.send(:subclasses).map(&:name) << 'Enterprise').map { |klass| "profiles.type = '#{klass}'"}.join(" OR ")} }
  57 + named_scope :communities, lambda { {:conditions => (Community.send(:subclasses).map(&:name) << 'Community').map { |klass| "profiles.type = '#{klass}'"}.join(" OR ")} }
57 58  
58 59 def members
59 60 Person.members_of(self)
... ... @@ -163,34 +164,19 @@ class Profile &lt; ActiveRecord::Base
163 164  
164 165 has_many :events, :source => 'articles', :class_name => 'Event', :order => 'name'
165 166  
166   - %w[ pending finished ].each do |status|
167   - class_eval <<-CODE
168   - def all_#{status}_tasks
169   - env_tasks = []
170   - if self.person?
171   - env_tasks = Environment.find(:all).select{ |env| self.is_admin?(env) }.map{ |env| env.tasks.#{status} }.flatten
172   - end
173   - tasks.#{status} + env_tasks
174   - end
175   - CODE
176   - end
177   -
178 167 def find_in_all_tasks(task_id)
179   - if tasks.exists?(task_id)
180   - return tasks.find(task_id)
181   - else
182   - if self.person?
183   - environments_admin = Environment.find(:all).select{ |env| self.is_admin?(env) }
184   - task = environments_admin.select{ |env| env.tasks.exists?(task_id) }.map{ |i| i.tasks.find(task_id) }
185   - return task.first unless task.empty?
186   - end
  168 + begin
  169 + Task.to(self).find(task_id)
  170 + rescue
  171 + nil
187 172 end
188   - return nil
189 173 end
190 174  
191 175 has_many :profile_categorizations, :conditions => [ 'categories_profiles.virtual = ?', false ]
192 176 has_many :categories, :through => :profile_categorizations
193 177  
  178 + has_many :abuse_complaints, :foreign_key => 'requestor_id'
  179 +
194 180 def interests
195 181 categories.select {|item| !item.is_a?(Region)}
196 182 end
... ... @@ -564,9 +550,7 @@ private :generate_url, :url_options
564 550 if self.closed? && members_count > 0
565 551 AddMember.create!(:person => person, :organization => self) unless self.already_request_membership?(person)
566 552 else
567   - if members_count == 0
568   - self.affiliate(person, Profile::Roles.admin(environment.id))
569   - end
  553 + self.affiliate(person, Profile::Roles.admin(environment.id)) if members_count == 0
570 554 self.affiliate(person, Profile::Roles.member(environment.id))
571 555 end
572 556 else
... ... @@ -809,6 +793,34 @@ private :generate_url, :url_options
809 793 "#{jid(options)}/#{short_name}"
810 794 end
811 795  
  796 + def is_on_homepage?(url, page=nil)
  797 + if page
  798 + page == self.home_page
  799 + else
  800 + url == '/' + self.identifier
  801 + end
  802 + end
  803 +
  804 + def opened_abuse_complaint
  805 + abuse_complaints.opened.first
  806 + end
  807 +
  808 + def disable
  809 + self.visible = false
  810 + user.password = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{identifier}--")
  811 + user.password_confirmation = user.password
  812 + save!
  813 + user.save!
  814 + end
  815 +
  816 + def control_panel_settings_button
  817 + {:title => _('Profile Info and settings'), :icon => 'edit-profile'}
  818 + end
  819 +
  820 + def self.identification
  821 + name
  822 + end
  823 +
812 824 protected
813 825  
814 826 def followed_by?(person)
... ...
app/models/profile_list_block.rb
1 1 class ProfileListBlock < Block
2 2  
3 3 settings_items :limit, :type => :integer, :default => 6
4   - settings_items :prioritize_profiles_with_image, :type => :boolean, :default => false
  4 + settings_items :prioritize_profiles_with_image, :type => :boolean, :default => true
5 5  
6 6 def self.description
7 7 _('Random profiles')
... ... @@ -13,21 +13,21 @@ class ProfileListBlock &lt; Block
13 13 end
14 14  
15 15 def profile_list
16   - profiles.visible.all(:limit => limit, :select => 'DISTINCT profiles.*, ' + image_prioritizer + randomizer, :joins => "LEFT OUTER JOIN images ON images.owner_id = profiles.id", :order => image_prioritizer + randomizer)
  16 + result = nil
  17 + if !prioritize_profiles_with_image
  18 + result = profiles.visible.all(:limit => limit, :order => 'updated_at DESC').sort_by{ rand }
  19 + elsif profiles.visible.with_image.count >= limit
  20 + result = profiles.visible.with_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand }
  21 + else
  22 + result = profiles.visible.with_image.sort_by{ rand } + profiles.visible.without_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand }
  23 + end
  24 + result.slice(0..limit-1)
17 25 end
18 26  
19 27 def profile_count
20 28 profiles.visible.count('DISTINCT(profiles.id)')
21 29 end
22 30  
23   - def randomizer
24   - @randomizer ||= "(profiles.id % #{rand(profile_count) + 1})"
25   - end
26   -
27   - def image_prioritizer
28   - prioritize_profiles_with_image ? '(images.id is null),' : ''
29   - end
30   -
31 31 # the title of the block. Probably will be overriden in subclasses.
32 32 def default_title
33 33 _('{#} People or Groups')
... ...
app/models/raw_html_article.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class RawHTMLArticle < TextArticle
  2 +
  3 + def self.short_description
  4 + _('Raw HTML text article.')
  5 + end
  6 +
  7 + def self.description
  8 + _('Allows HTML without filter (only for admins)')
  9 + end
  10 +
  11 + xss_terminate :only => [ ]
  12 +
  13 +end
... ...
app/models/reported_image.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class ReportedImage < ActiveRecord::Base
  2 + belongs_to :abuse_report
  3 +
  4 + validates_presence_of :abuse_report
  5 +
  6 + has_attachment :content_type => :image,
  7 + :storage => :file_system,
  8 + :max_size => 5.megabytes
  9 +
  10 +end
... ...
app/models/suggest_article.rb
1 1 class SuggestArticle < Task
2 2  
3   - has_captcha
4   -
5 3 validates_presence_of :target_id, :article_name, :email, :name, :article_body
6 4  
7 5 settings_items :email, :type => String
... ...
app/models/task.rb
... ... @@ -20,11 +20,14 @@ class Task &lt; ActiveRecord::Base
20 20 # the status of a task that was cancelled.
21 21 CANCELLED = 2
22 22  
23   - # the status os a task that was successfully finished
  23 + # the status of a task that was successfully finished
24 24 FINISHED = 3
25 25  
  26 + # the status of a task that was created but is not displayed yet
  27 + HIDDEN = 4
  28 +
26 29 def self.names
27   - [nil, N_('Active'), N_('Cancelled'), N_('Finished')]
  30 + [nil, N_('Active'), N_('Cancelled'), N_('Finished'), N_('Hidden')]
28 31 end
29 32 end
30 33  
... ... @@ -38,7 +41,7 @@ class Task &lt; ActiveRecord::Base
38 41  
39 42 def initialize(*args)
40 43 super
41   - self.status ||= Task::Status::ACTIVE
  44 + self.status = (args.first ? args.first[:status] : nil) || Task::Status::ACTIVE
42 45 end
43 46  
44 47 attr_accessor :code_length
... ... @@ -52,20 +55,26 @@ class Task &lt; ActiveRecord::Base
52 55 end
53 56  
54 57 after_create do |task|
55   - begin
56   - task.send(:send_notification, :created)
57   - rescue NotImplementedError => ex
58   - RAILS_DEFAULT_LOGGER.info ex.to_s
59   - end
60   -
61   - begin
62   - target_msg = task.target_notification_message
63   - TaskMailer.deliver_target_notification(task, target_msg) if target_msg
64   - rescue NotImplementedError => ex
65   - RAILS_DEFAULT_LOGGER.info ex.to_s
  58 + unless task.status == Task::Status::HIDDEN
  59 + begin
  60 + task.send(:send_notification, :created)
  61 + rescue NotImplementedError => ex
  62 + RAILS_DEFAULT_LOGGER.info ex.to_s
  63 + end
  64 +
  65 + begin
  66 + target_msg = task.target_notification_message
  67 + TaskMailer.deliver_target_notification(task, target_msg) if target_msg
  68 + rescue NotImplementedError => ex
  69 + RAILS_DEFAULT_LOGGER.info ex.to_s
  70 + end
66 71 end
67 72 end
68 73  
  74 + def self.all_types
  75 + %w[Invitation EnterpriseActivation AddMember Ticket SuggestArticle AddFriend CreateCommunity AbuseComplaint ApproveArticle CreateEnterprise ChangePassword EmailActivation InviteFriend InviteMember]
  76 + end
  77 +
69 78 # this method finished the task. It calls #perform, which must be overriden
70 79 # by subclasses. At the end a message (as returned by #finish_message) is
71 80 # sent to the requestor with #notify_requestor.
... ... @@ -175,6 +184,12 @@ class Task &lt; ActiveRecord::Base
175 184 raise NotImplementedError, "#{self} does not implement #task_cancelled_message"
176 185 end
177 186  
  187 + # The message that will be sent to the requestor of the task when its
  188 + # activated.
  189 + def task_activated_message
  190 + raise NotImplementedError, "#{self} does not implement #task_cancelled_message"
  191 + end
  192 +
178 193 # The message that will be sent to the *target* of the task when it is
179 194 # created. The indent of this message is to notify the target about the
180 195 # request that was just created for him/her.
... ... @@ -199,6 +214,23 @@ class Task &lt; ActiveRecord::Base
199 214 self.target.environment unless self.target.nil?
200 215 end
201 216  
  217 + def activate
  218 + self.status = Task::Status::ACTIVE
  219 + save!
  220 + begin
  221 + self.send(:send_notification, :activated)
  222 + rescue NotImplementedError => ex
  223 + RAILS_DEFAULT_LOGGER.info ex.to_s
  224 + end
  225 +
  226 + begin
  227 + target_msg = target_notification_message
  228 + TaskMailer.deliver_target_notification(self, target_msg) if target_msg
  229 + rescue NotImplementedError => ex
  230 + RAILS_DEFAULT_LOGGER.info ex.to_s
  231 + end
  232 + end
  233 +
202 234 protected
203 235  
204 236 # This method must be overrided in subclasses, and its implementation must do
... ... @@ -232,6 +264,23 @@ class Task &lt; ActiveRecord::Base
232 264  
233 265 named_scope :pending, :conditions => { :status => Task::Status::ACTIVE }
234 266 named_scope :finished, :conditions => { :status => [Task::Status::CANCELLED, Task::Status::FINISHED] }
  267 + named_scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] }
  268 + named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} }
  269 + named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} }
  270 +
  271 + named_scope :to, lambda { |profile|
  272 + environment_condition = nil
  273 + if profile.person?
  274 + envs_ids = Environment.find(:all).select{ |env| profile.is_admin?(env) }.map { |env| "target_id = #{env.id}"}.join(' OR ')
  275 + environment_condition = envs_ids.blank? ? nil : "(target_type = 'Environment' AND (#{envs_ids}))"
  276 + end
  277 + profile_condition = "(target_type = 'Profile' AND target_id = #{profile.id})"
  278 + { :conditions => [environment_condition, profile_condition].compact.join(' OR ') }
  279 + }
  280 +
  281 + def opened?
  282 + status == Task::Status::ACTIVE || status == Task::Status::HIDDEN
  283 + end
235 284  
236 285 class << self
237 286  
... ... @@ -254,6 +303,10 @@ class Task &lt; ActiveRecord::Base
254 303 self.find(:first, :conditions => { :code => code, :status => Task::Status::ACTIVE })
255 304 end
256 305  
  306 + def per_page
  307 + 15
  308 + end
  309 +
257 310 end
258 311  
259 312 end
... ...
app/models/text_article.rb
1 1 # a base class for all text article types.
2 2 class TextArticle < Article
3 3  
4   - xss_terminate :only => [ :name, :abstract, :body ], :on => 'validation'
  4 + xss_terminate :only => [ :name ], :on => 'validation'
5 5  
6 6 include Noosfero::TranslatableContent
7 7  
... ...
app/models/textile_article.rb
... ... @@ -9,11 +9,28 @@ class TextileArticle &lt; TextArticle
9 9 end
10 10  
11 11 def to_html(options ={})
12   - RedCloth.new(self.body|| '').to_html
  12 + convert_to_html(body)
  13 + end
  14 +
  15 + def lead
  16 + if abstract.blank?
  17 + super
  18 + else
  19 + convert_to_html(abstract)
  20 + end
13 21 end
14 22  
15 23 def notifiable?
16 24 true
17 25 end
18 26  
  27 + protected
  28 +
  29 + def convert_to_html(textile)
  30 + @@sanitizer ||= HTML::WhiteListSanitizer.new
  31 + converter = RedCloth.new(textile|| '')
  32 + converter.hard_breaks = false
  33 + @@sanitizer.sanitize(converter.to_html)
  34 + end
  35 +
19 36 end
... ...
app/models/thumbnail.rb
... ... @@ -2,4 +2,8 @@ class Thumbnail &lt; ActiveRecord::Base
2 2 has_attachment :storage => :file_system,
3 3 :content_type => :image, :max_size => 5.megabytes
4 4 validates_as_attachment
  5 +
  6 + sanitize_filename
  7 +
  8 + postgresql_attachment_fu
5 9 end
... ...
app/models/ticket.rb
1 1 class Ticket < Task
2   - settings_items :title, :message
  2 + settings_items :name, :message
  3 +
  4 + def title
  5 + _('Ticket') + (name ? ': '+name : '')
  6 + end
3 7 end
... ...
app/models/uploaded_file.rb
... ... @@ -18,6 +18,8 @@ class UploadedFile &lt; Article
18 18  
19 19 validates_size_of :title, :maximum => 60, :if => (lambda { |file| !file.title.blank? })
20 20  
  21 + sanitize_filename
  22 +
21 23 before_create do |uploaded_file|
22 24 uploaded_file.is_image = true if uploaded_file.image?
23 25 end
... ... @@ -52,6 +54,8 @@ class UploadedFile &lt; Article
52 54  
53 55 delay_attachment_fu_thumbnails
54 56  
  57 + postgresql_attachment_fu
  58 +
55 59 def self.icon_name(article = nil)
56 60 if article
57 61 article.image? ? article.public_filename(:icon) : (article.mime_type ? article.mime_type.gsub(/[\/+.]/, '-') : 'upload-file')
... ... @@ -132,4 +136,8 @@ class UploadedFile &lt; Article
132 136 def gallery?
133 137 self.parent && self.parent.folder? && self.parent.gallery?
134 138 end
  139 +
  140 + def uploaded_file?
  141 + true
  142 + end
135 143 end
... ...
app/models/user.rb
... ... @@ -21,6 +21,8 @@ class User &lt; ActiveRecord::Base
21 21 end
22 22 end
23 23  
  24 + before_create :make_activation_code
  25 +
24 26 before_create do |user|
25 27 if user.environment.nil?
26 28 user.environment = Environment.default
... ... @@ -31,8 +33,11 @@ class User &lt; ActiveRecord::Base
31 33 user.person ||= Person.new
32 34 user.person.attributes = user.person_data.merge(:identifier => user.login, :user_id => user.id, :environment_id => user.environment_id)
33 35 user.person.name ||= user.login
  36 + user.person.visible = false unless user.activated?
34 37 user.person.save!
35 38 end
  39 + after_create :deliver_activation_code
  40 + after_create :delay_activation_check
36 41  
37 42 attr_writer :person_data
38 43 def person_data
... ... @@ -55,6 +60,17 @@ class User &lt; ActiveRecord::Base
55 60 :environment => user.environment.name,
56 61 :url => url_for(:host => user.environment.default_hostname, :controller => 'home')
57 62 end
  63 +
  64 + def activation_code(user)
  65 + recipients user.email
  66 +
  67 + from "#{user.environment.name} <#{user.environment.contact_email}>"
  68 + subject _("[%s] Activate your account") % [user.environment.name]
  69 + body :recipient => user.name,
  70 + :activation_code => user.activation_code,
  71 + :environment => user.environment.name,
  72 + :url => user.environment.top_url
  73 + end
58 74 end
59 75  
60 76 def signup!
... ... @@ -67,6 +83,8 @@ class User &lt; ActiveRecord::Base
67 83 has_one :person, :dependent => :destroy
68 84 belongs_to :environment
69 85  
  86 + attr_protected :activated_at
  87 +
70 88 # Virtual attribute for the unencrypted password
71 89 attr_accessor :password
72 90  
... ... @@ -87,10 +105,22 @@ class User &lt; ActiveRecord::Base
87 105 # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
88 106 def self.authenticate(login, password, environment = nil)
89 107 environment ||= Environment.default
90   - u = find_by_login_and_environment_id(login, environment.id) # need to get the salt
  108 + u = first :conditions => ['login = ? AND environment_id = ? AND activated_at IS NOT NULL', login, environment.id] # need to get the salt
91 109 u && u.authenticated?(password) ? u : nil
92 110 end
93 111  
  112 + # Activates the user in the database.
  113 + def activate
  114 + self.activated_at = Time.now.utc
  115 + self.activation_code = nil
  116 + self.person.visible = true
  117 + self.person.save! && self.save
  118 + end
  119 +
  120 + def activated?
  121 + self.activation_code.nil? && !self.activated_at.nil?
  122 + end
  123 +
94 124 class UnsupportedEncryptionType < Exception; end
95 125  
96 126 def self.system_encryption_method
... ... @@ -195,7 +225,7 @@ class User &lt; ActiveRecord::Base
195 225 end
196 226  
197 227 def name
198   - person.name
  228 + person ? person.name : login
199 229 end
200 230  
201 231 def enable_email!
... ... @@ -253,4 +283,16 @@ class User &lt; ActiveRecord::Base
253 283 def password_required?
254 284 crypted_password.blank? || !password.blank?
255 285 end
  286 +
  287 + def make_activation_code
  288 + self.activation_code = Digest::SHA1.hexdigest(Time.now.to_s.split(//).sort_by{rand}.join)
  289 + end
  290 +
  291 + def deliver_activation_code
  292 + User::Mailer.deliver_activation_code(self) unless self.activation_code.blank?
  293 + end
  294 +
  295 + def delay_activation_check
  296 + Delayed::Job.enqueue(UserActivationJob.new(self.id), 0, 72.hours.from_now)
  297 + end
256 298 end
... ...
app/sweepers/article_sweeper.rb
... ... @@ -21,7 +21,7 @@ protected
21 21 blocks = article.profile.blocks
22 22 blocks += article.profile.environment.blocks if article.profile.environment
23 23 blocks = blocks.select{|b|[RecentDocumentsBlock, BlogArchivesBlock].any?{|c| b.kind_of?(c)}}
24   - blocks.map(&:cache_keys).each{|ck|expire_timeout_fragment(ck)}
  24 + blocks.map(&:cache_key).each{|ck|expire_timeout_fragment(ck)}
25 25 env = article.profile.environment
26 26 if env && (env.portal_community == article.profile)
27 27 expire_fragment(env.portal_news_cache_key)
... ...
app/sweepers/friendship_sweeper.rb
... ... @@ -35,7 +35,7 @@ protected
35 35 end
36 36  
37 37 blocks = profile.blocks.select{|b| b.kind_of?(FriendsBlock)}
38   - blocks.map(&:cache_keys).each{|ck|expire_timeout_fragment(ck)}
  38 + blocks.map(&:cache_key).each{|ck|expire_timeout_fragment(ck)}
39 39 end
40 40  
41 41 end
... ...
app/sweepers/profile_sweeper.rb
... ... @@ -23,12 +23,25 @@ protected
23 23 expire_profile_index(profile) if profile.person?
24 24  
25 25 profile.blocks.each do |block|
26   - expire_timeout_fragment(block.cache_keys)
  26 + expire_timeout_fragment(block.cache_key)
27 27 end
  28 +
  29 + expire_blogs(profile) if profile.organization?
28 30 end
29 31  
30 32 def expire_statistics_block_cache(profile)
31 33 blocks = profile.environment.blocks.select { |b| b.kind_of?(EnvironmentStatisticsBlock) }
32   - blocks.map(&:cache_keys).each{|ck|expire_timeout_fragment(ck)}
  34 + blocks.map(&:cache_key).each{|ck|expire_timeout_fragment(ck)}
33 35 end
  36 +
  37 + def expire_blogs(profile)
  38 + profile.blogs.select{|b| !b.empty?}.each do |blog|
  39 + pages = blog.posts.count / blog.posts_per_page + 1
  40 + ([nil] + (1..pages).to_a).each do |i|
  41 + expire_timeout_fragment(blog.cache_key({:npage => i}))
  42 + expire_timeout_fragment(blog.cache_key({:npage => i}, profile))
  43 + end
  44 + end
  45 + end
  46 +
34 47 end
... ...
app/sweepers/role_assignment_sweeper.rb
... ... @@ -25,7 +25,7 @@ protected
25 25  
26 26 profile.blocks_to_expire_cache.each { |block|
27 27 blocks = profile.blocks.select{|b| b.kind_of?(block)}
28   - blocks.map(&:cache_keys).each{|ck|expire_timeout_fragment(ck)}
  28 + blocks.map(&:cache_key).each{|ck|expire_timeout_fragment(ck)}
29 29 }
30 30 end
31 31  
... ...
app/views/account/_signup_form.rhtml
... ... @@ -7,8 +7,7 @@
7 7 </div>
8 8 <% end %>
9 9  
10   -<% labelled_form_for :user, @user do |f| %>
11   -<%= icaptcha_field() %>
  10 +<% labelled_form_for :user, @user, :html => { :multipart => true } do |f| %>
12 11  
13 12 <%= hidden_field_tag :invitation_code, @invitation_code %>
14 13  
... ...
app/views/account/login.rhtml
... ... @@ -5,9 +5,11 @@
5 5 <% @user ||= User.new %>
6 6 <% is_thickbox ||= false %>
7 7  
  8 +<%= @message %>
  9 +
8 10 <% labelled_form_for :user, @user, :url => login_url do |f| %>
9 11  
10   - <%= f.text_field :login, :id => 'main_user_login', :onchange => 'this.value = convToValidLogin( this.value )' %>
  12 + <%= f.text_field :login, :id => 'main_user_login', :onchange => 'this.value = convToValidLogin( this.value )', :value => params[:userlogin] %>
11 13  
12 14 <%= f.password_field :password %>
13 15  
... ...
app/views/account/signup.rhtml
1 1 <h1><%= _('Signup') %></h1>
2   -<%= render :partial => 'signup_form' %>
  2 +<% if @register_pending %>
  3 + <%= _('Thanks for signing up! Now check your e-mail to activate your account!') %>
  4 + <p style="text-align: center"><%= link_to(_('Go to the homepage'), '/') %></p>
  5 +<% else %>
  6 + <%= render :partial => 'signup_form' %>
  7 +<% end %>
... ...
app/views/admin_panel/edit_templates.rhtml
1 1 <h1><%= _('Edit Templates') %></h1>
2 2  
3 3 <ul>
4   -<li><%= link_to _('Edit Person Template'), :controller => 'profile_editor', :profile => environment.person_template.identifier %></li>
5   -<li><%= link_to _('Edit Community Template'), :controller => 'profile_editor', :profile => environment.community_template.identifier %></li>
6   -<li><%= link_to __('Edit Enterprise Template'), :controller => 'profile_editor', :profile => environment.enterprise_template.identifier %></li>
  4 +<% [[_('Edit Person Template'), environment.person_template],
  5 + [_('Edit Community Template'), environment.community_template],
  6 + [__('Edit Enterprise Template'), environment.enterprise_template],
  7 + [__('Edit Inactive Enterprise Template'), environment.inactive_enterprise_template]].select{|i| i[1]}.each do |row| %>
  8 +<li><%= link_to row[0], :controller => 'profile_editor', :profile => row[1].identifier %></li>
  9 +<% end %>
7 10 </ul>
... ...
app/views/admin_panel/index.rhtml
... ... @@ -3,7 +3,7 @@
3 3 <p><%= _('You, as an environment administrator, has the following options:')%></p>
4 4  
5 5 <table>
6   - <tr><td><%= link_to _('Edit site info'), :action => 'site_info' %></td></tr>
  6 + <tr><td><%= link_to _('Edit environment settings'), :action => 'site_info' %></td></tr>
7 7 <tr><td><%= link_to __('Edit message for disabled enterprises'), :action => 'message_for_disabled_enterprise' %></td></tr>
8 8 <tr><td><%= link_to _('Enable/disable features'), :controller => 'features' %></td></tr>
9 9 <tr><td><%= link_to _('Enable/disable plugins'), :controller => 'plugins' %></td></tr>
... ... @@ -16,4 +16,7 @@
16 16 <tr><td><%= link_to _('Manage Fields'), :controller => 'features', :action => 'manage_fields' %></td></tr>
17 17 <tr><td><%= link_to _('Set Portal'), :action => 'set_portal_community' %></td></tr>
18 18 <tr><td><%= link_to _('Terms of use'), :action => 'terms_of_use' %></td></tr>
  19 + <% @plugins.map(:admin_panel_links).each do |link| %>
  20 + <tr><td><%= link_to link[:title], link[:url] %></td></tr>
  21 + <% end %>
19 22 </table>
... ...
app/views/admin_panel/site_info.rhtml
1   -<h2><%= _('Site info') %></h2>
  1 +<h2><%= _('Environment settings') %></h2>
2 2  
3   -<%= render :file => 'shared/tiny_mce' %>
4   -
5   -<% labelled_form_for :environment, @environment do |f| %>
  3 +<%= error_messages_for :environment %>
6 4  
7   - <%= labelled_form_field(_('Site name'), text_field(:environment, :name)) %>
  5 +<%= render :file => 'shared/tiny_mce' %>
8 6  
  7 +<% labelled_form_for :environment, @environment, :url => {:host => @environment.default_hostname} do |f| %>
  8 + <%= required labelled_form_field(_('Site name'), text_field(:environment, :name)) %>
  9 + <%= required f.text_field(:reports_lower_bound, :size => 3) %>
9 10 <%= labelled_form_field _('Homepage content'), text_area(:environment, :description, :cols => 40, :style => 'width: 90%', :class => 'mceEditor') %>
10 11  
11 12 <% button_bar do %>
12   - <%= submit_button(:save, _('Save')) %>
13   - <%= button(:cancel, _('Cancel'), :action => 'index') %>
  13 + <%= submit_button(:save, _('Save'), :cancel => {:action => 'index'}) %>
14 14 <% end %>
15   -
16 15 <% end %>
... ...
app/views/blocks/my_network.rhtml
... ... @@ -6,7 +6,7 @@
6 6 <li><%= link_to(__('Homepage'), owner.url, :class => 'url') %></li>
7 7 <li><%= link_to(_('View profile'), owner.public_profile_url) %></li>
8 8 <% if !user.nil? and owner.organization? and user.has_permission?('edit_profile', profile) %>
9   - <li><%= link_to _('Control panel'), :controller => 'profile_editor' %></li>
  9 + <li><%= link_to _('Control panel'), :controller => 'profile_editor', :profile => profile.identifier %></li>
10 10 <% end %>
11 11 </ul>
12 12  
... ...
app/views/blocks/profile_image.rhtml
... ... @@ -20,5 +20,5 @@
20 20  
21 21 </div><!-- end class="vcard" -->
22 22 <script type="text/javascript">
23   - <%= remote_function :url => { :controller => 'profile', :action => 'profile_info', :block_id => block.id } %>
  23 + <%= remote_function :url => { :controller => 'profile', :profile => profile.identifier, :action => 'profile_info', :block_id => block.id } %>
24 24 </script>
... ...
app/views/blocks/profile_info.rhtml
... ... @@ -41,5 +41,5 @@
41 41  
42 42 </div><!-- end class="vcard" -->
43 43 <script type="text/javascript">
44   - <%= remote_function :url => { :controller => 'profile', :action => 'profile_info', :block_id => block.id } %>
  44 + <%= remote_function :url => { :controller => 'profile', :profile => profile.identifier, :action => 'profile_info', :block_id => block.id } %>
45 45 </script>
... ...
app/views/blocks/profile_info_actions/community.rhtml
1 1 <ul>
2 2 <% if logged_in? %>
3   -
4 3 <% if profile.members.include?(user) %>
5 4 <li>
6   - <%= button(:delete, content_tag('span', __('Leave')), profile.leave_url, :class => 'leave-community', :title => _("Leave community"), :style => 'position: relative;') %>
7   - <%= button(:add, content_tag('span', __('Join')), profile.join_url, :class => 'join-community', :title => _("Join community"), :style => 'position: relative; display: none;') %>
  5 + <%= button(:delete, content_tag('span', __('Leave')), profile.leave_url,
  6 + :class => 'leave-community',
  7 + :title => _("Leave community"),
  8 + :style => 'position: relative;') %>
  9 + <%= button(:add, content_tag('span', __('Join')), profile.join_url,
  10 + :class => 'join-community',
  11 + :title => _("Join community"),
  12 + :style => 'position: relative; display: none;') %>
8 13 </li>
9 14 <% else %>
10 15 <% unless profile.already_request_membership?(user) %>
11 16 <li>
12   - <%= button(:delete, content_tag('span', __('Leave')), profile.leave_url, :class => 'leave-community', :title => _("Leave community"), :style => 'position: relative; display: none;') %>
13   - <%= button(:add, content_tag('span', __('Join')), profile.join_url, :class => 'join-community', :title => _("Join community"), :style => 'position: relative;') %>
  17 + <%= button(:delete, content_tag('span', __('Leave')), profile.leave_url,
  18 + :class => 'leave-community',
  19 + :title => _("Leave community"),
  20 + :style => 'position: relative; display: none;') %>
  21 + <%= button(:add, content_tag('span', __('Join')), profile.join_url,
  22 + :class => 'join-community',
  23 + :title => _("Join community"),
  24 + :style => 'position: relative;') %>
14 25 </li>
15 26 <% end %>
16 27 <% end %>
  28 +
17 29 <% if profile.enable_contact? %>
18 30 <li>
19   - <%= link_to content_tag('span', _('Send an e-mail')), {:profile => profile.identifier, :controller => 'contact', :action => 'new'}, :class => 'button with-text icon-menu-mail' %>
  31 + <%= link_to content_tag('span', _('Send an e-mail')),
  32 + { :profile => profile.identifier,
  33 + :controller => 'contact',
  34 + :action => 'new' },
  35 + :class => 'button with-text icon-menu-mail' %>
20 36 </li>
21 37 <% end %>
22   - <%= render_environment_features(:profile_actions) %>
23 38  
24   - <% else %>
  39 + <li><%= report_abuse(profile, :button) %></li>
25 40  
  41 + <%= render_environment_features(:profile_actions) %>
  42 + <% else %>
26 43 <li>
27   - <%= link_to content_tag('span', _('Join')), profile.join_not_logged_url, :class => 'button with-text icon-add', :title => _('Join this community') %>
  44 + <%= link_to content_tag('span', _('Join')), profile.join_not_logged_url,
  45 + :class => 'button with-text icon-add',
  46 + :title => _('Join this community') %>
28 47 </li>
29   -
30 48 <% end %>
31 49 </ul>
... ...
app/views/blocks/profile_info_actions/enterprise.rhtml
... ... @@ -7,4 +7,6 @@
7 7 <% if profile.enable_contact? %>
8 8 <li> <%= link_to content_tag('span', _('Send an e-mail')), {:profile => profile.identifier, :controller => 'contact', :action => 'new'}, {:id => 'enterprise-contact-button', :class => 'button with-text icon-menu-mail'} %> </li>
9 9 <% end %>
  10 +
  11 + <li><%= report_abuse(profile, :button) %></li>
10 12 </ul>
... ...
app/views/blocks/profile_info_actions/person.rhtml
... ... @@ -11,5 +11,6 @@
11 11 <li> <%= link_to content_tag('span', _('Send an e-mail')), {:profile => profile.identifier, :controller => 'contact', :action => 'new'}, :class => 'button with-text icon-menu-mail' %> </li>
12 12 <% end %>
13 13  
  14 + <li><%= report_abuse(profile, :button) %></li>
14 15 <% end %>
15 16 </ul>
... ...
app/views/box_organizer/_article_block.rhtml
... ... @@ -5,6 +5,6 @@
5 5 </p>
6 6 <% else %>
7 7 <% articles = @block.available_articles.select {|article| !article.folder? } %>
8   - <%= select_tag('block[article_id]', options_for_select_with_title(articles.map {|item| [item.path, item.id]})) %>
  8 + <%= select_tag('block[article_id]', options_for_select_with_title(articles.map {|item| [item.path, item.id]}, @block.article ? @block.article.id : nil)) %>
9 9 <% end %>
10 10 </div>
... ...
app/views/box_organizer/edit.rhtml
... ... @@ -14,6 +14,9 @@
14 14 <%= radio_button(:block, :display, 'home_page_only') %>
15 15 <%= label_tag('block_display_home_page_only', _('Only in the homepage')) %>
16 16 <br/>
  17 + <%= radio_button(:block, :display, 'except_home_page') %>
  18 + <%= label_tag('block_display_except_home_page', _('In all pages, except in the homepage')) %>
  19 + <br/>
17 20 <%= radio_button(:block, :display, 'never') %>
18 21 <%= label_tag('block_display_never', _("Don't display")) %>
19 22 </div>
... ...
app/views/browse/_article.rhtml 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<li class="<%= 'browse-results-type-content ' + icon_for_article(result) %>">
  2 + <strong><%= link_to(result.title, result.view_url) %></strong>
  3 + <div class="item_meta">
  4 + <span class="item_by">
  5 + <%= _('by %s') % link_to(result.author.name, result.author.url) %>
  6 + </span>
  7 + <span class="extra-info">
  8 + <%= (@filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label')) %>
  9 + </span>
  10 + </div>
  11 +</li>
... ...
app/views/browse/_display_results.rhtml
... ... @@ -8,7 +8,7 @@
8 8 <% end %>
9 9 <ul class='common-profile-list-block'>
10 10 <% @results.each do |result| %>
11   - <%= render :partial => partial_for_class(result.class), :locals => {:profile => result} %>
  11 + <%= render :partial => partial_for_class(result.class), :locals => {:result => result} %>
12 12 <% end %>
13 13 </ul>
14 14 <br style='clear: both;'>
... ...
app/views/browse/_person.rhtml
1   -<%= profile_image_link profile, :portrait, 'li',
2   - "<span class='adr'>#{profile.city}</span>" +
3   - (@filter == 'more_recent' ? profile.send(@filter + '_label') + show_date(profile.created_at) : profile.send(@filter + '_label')) %>
  1 +<%= profile_image_link result, :portrait, 'li',
  2 + "<span class='adr'>#{result.city}</span>" +
  3 + (@filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label')) %>
... ...
app/views/browse/_profile.rhtml
1   -<%= profile_image_link profile, :portrait, 'li', @filter == 'more_recent' ? profile.send(@filter + '_label') + show_date(profile.created_at) : profile.send(@filter + '_label') %>
  1 +<%= profile_image_link result, :portrait, 'li', @filter == 'more_recent' ? result.send(@filter + '_label') + show_date(result.created_at) : result.send(@filter + '_label') %>
... ...
app/views/browse/contents.rhtml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<%= search_page_title( @title, { :query => @query} ) %>
  2 +
  3 +<%= render :partial => 'search_form', :locals => {:action => 'contents'} %>
  4 +
  5 +<%= render :partial => 'display_results' %>
  6 +
  7 +<%= pagination_links @results %>
  8 +
  9 +<br style="clear:both" />
... ...
app/views/categories/_category.rhtml
... ... @@ -2,12 +2,12 @@
2 2 <div class='treeitem'>
3 3 <%= display_color_for_category(category) %>
4 4 <%= category.name %>
5   - <div class='button' id="show_button_<%= category.id %>">
6   - <%= link_to_function(_('Show'), "['category_#{category.id}','hide_button_#{category.id}'].each(Element.show); Element.hide('show_button_#{category.id}');") unless category.children.empty? %>
7   - </div>
8   - <div class='button' id="hide_button_<%= category.id %>" style='display:none;'>
9   - <%= link_to_function(_('Hide'), "['category_#{category.id}','hide_button_#{category.id}'].each(Element.hide); Element.show('show_button_#{category.id}') ") %>
10   - </div>
  5 + <% if category.children.count > 0 %>
  6 + <div class='button' id="category-loading-<%= category.id %>" style="position: relative;">
  7 + <a href="#" id="show-button-<%= category.id %>" class="show-button" onclick="return false;" data-category="<%= category.id %>"><%= _('Show') %></a>
  8 + </div>
  9 + <a href="#" id="hide-button-<%= category.id %>" class="hide-button" onclick="return false;" data-category="<%= category.id %>" style="display: none;"><%= _('Hide') %></a>
  10 + <% end %>
11 11  
12 12 <div>
13 13 <%= link_to _('Add subcategory'), :action => 'new', :parent_id => category %>
... ... @@ -16,12 +16,6 @@
16 16 </div>
17 17 </div>
18 18  
19   - <div id="category_<%= category.id %>" style='display:none;'>
20   - <% unless category.children.empty? %>
21   - <ul class='tree'>
22   - <%= render :partial => 'category', :collection => category.children %>
23   - </ul>
24   - <% end %>
25   - </div>
  19 +<ul id="category-sub-items-<%= category.id %>" class="tree" style='display:none;'></ul>
26 20  
27 21 </li>
... ...
app/views/categories/_category_children.rhtml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<% children.each do |category| %>
  2 + <%= render :partial => 'category', :locals => {:category => category} %>
  3 +<% end %>
... ...
app/views/categories/index.rhtml
... ... @@ -25,3 +25,4 @@
25 25 <%= link_to _('New category'), :action => 'new', :type => 'Region' %>
26 26 </div>
27 27  
  28 +<%= javascript_include_tag 'manage-categories' %>
... ...
app/views/cms/_blog.rhtml
... ... @@ -50,7 +50,7 @@
50 50 %>
51 51 </div>
52 52  
53   -<%= labelled_form_field(_('Description:'), text_area(:article, :body, :cols => 64, :rows => 10)) %>
  53 +<%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 10)) %>
54 54  
55 55 <%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [ [ _('Full post'), 'full'], [ _('First paragraph'), 'short'] ])) %>
56 56  
... ...
app/views/cms/_document_link.rhtml
... ... @@ -1,10 +0,0 @@
1   -<div id='media-listing-folder-documents' >
2   - <ul>
3   - <% documents.each do |document| %>
4   - <li><%= link_to(document.name, document.view_url, :class => icon_for_article(document)) %></li>
5   - <% end %>
6   - </ul>
7   - <div id='pagination-documents'>
8   - <%= pagination_links documents, :param_name => 'dpage', :params => {:document_folder_id => params[:document_folder_id]} %>
9   - </div>
10   -</div>
app/views/cms/_drag_and_drop_note.rhtml 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +<p>
  2 +<em><%= _('Drag images to add them to the text.') unless @article.is_a?(TextileArticle) %>
  3 +<%= _('Drag item names to the text to add links.') %></em>
  4 +</p>
... ...
app/views/cms/_image_thumb.rhtml
... ... @@ -1,10 +0,0 @@
1   -<div id='media-listing-folder-images' >
2   - <ul>
3   - <% images.each do |image| %>
4   - <li><%= image_tag image.public_filename %></li>
5   - <% end %>
6   - </ul>
7   - <div id='pagination-images'>
8   - <%= pagination_links images, :param_name => 'ipage', :params => {:image_folder_id => params[:image_folder_id]} %>
9   - </div>
10   -</div>
app/views/cms/_media_listing.rhtml
... ... @@ -1,3 +0,0 @@
1   -<iframe id='media-listing-iframe' src="<%= url_for(:controller => 'cms', :action => 'media_listing', :profile => profile.identifier, :type => @type) %>">
2   - <p><%= _('Your browser does not support iframes.') %></p>
3   -</iframe>