Commit 1e0d9a57a82715de413e761eef7acfe380dce483

Authored by Antonio Terceiro
2 parents 1d97315c a125dc04

Merge branch 'master' into rails-2.3.5

Conflicts:
	app/controllers/application.rb
	lib/noosfero.rb
	lib/noosfero/i18n.rb
	test/test_helper.rb
	test/unit/consumption_test.rb
Showing 1595 changed files with 105574 additions and 92651 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 1595 files displayed.

.gitignore.example
@@ -1,28 +0,0 @@ @@ -1,28 +0,0 @@
1 -.gitignore  
2 -vendor/rails  
3 -doc/api  
4 -doc/plugins  
5 -tmp  
6 -config/database.yml  
7 -config/session.secret  
8 -config/mail.yml  
9 -config/ferret_server.yml  
10 -config/mongrel_cluster.yml  
11 -index  
12 -locale  
13 -log  
14 -public/articles  
15 -public/images/0000  
16 -public/thumbnails  
17 -public/user_themes  
18 -public/designs/themes/default  
19 -public/javascripts/cache*.js  
20 -public/stylesheets/cache*.css  
21 -db/development.db  
22 -db/production.db  
23 -db/test.db  
24 -doc/noosfero/*.xhtml  
25 -doc/noosfero/*/*.xhtml  
26 -tags  
27 -pkg  
28 -*~  
1 Antonio Terceiro <terceiro@colivre.coop.br> <AntonioTerceiro@3f533792-8f58-4932-b0fe-aaf55b0a4547> 1 Antonio Terceiro <terceiro@colivre.coop.br> <AntonioTerceiro@3f533792-8f58-4932-b0fe-aaf55b0a4547>
2 Antonio Terceiro <terceiro@colivre.coop.br> <terceiro@softwarelivre.org> 2 Antonio Terceiro <terceiro@colivre.coop.br> <terceiro@softwarelivre.org>
3 Aurelio A. Heckert <aurelio@colivre.coop.br> <AurelioAHeckert@3f533792-8f58-4932-b0fe-aaf55b0a4547> 3 Aurelio A. Heckert <aurelio@colivre.coop.br> <AurelioAHeckert@3f533792-8f58-4932-b0fe-aaf55b0a4547>
  4 +Aurelio A. Heckert <aurelio@colivre.coop.br> <aurelio@colivre.coop.br>
  5 +Aurelio A. Heckert <aurelio@colivre.coop.br> <aurium@gmail.com>
  6 +Caio SBA <caiosba@gmail.com> <caiosba@safernet.org.br>
4 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <DanielaFeitosa@3f533792-8f58-4932-b0fe-aaf55b0a4547> 7 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <DanielaFeitosa@3f533792-8f58-4932-b0fe-aaf55b0a4547>
5 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <danielafeitosa@colivre.coop.br> 8 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <danielafeitosa@colivre.coop.br>
6 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <daniela@sede.colivre.coop.br> 9 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <daniela@sede.colivre.coop.br>
7 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <dani@lindinha.(none)> 10 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <dani@lindinha.(none)>
  11 +Daniel Cunha <daniel@colivre.coop.br> <daniel.ccunha@gmail.com>
  12 +Grazieno Pellegrino <grazieno@gmail.com> <GrazienoPellegrino@3f533792-8f58-4932-b0fe-aaf55b0a4547>
8 Joenio Costa <joenio@colivre.coop.br> <JoenioCosta@3f533792-8f58-4932-b0fe-aaf55b0a4547> 13 Joenio Costa <joenio@colivre.coop.br> <JoenioCosta@3f533792-8f58-4932-b0fe-aaf55b0a4547>
9 Joenio Costa <joenio@colivre.coop.br> <joenio@perl.org.br> 14 Joenio Costa <joenio@colivre.coop.br> <joenio@perl.org.br>
10 -Grazieno Pellegrino <grazieno@gmail.com> <GrazienoPellegrino@3f533792-8f58-4932-b0fe-aaf55b0a4547>  
11 Leandro Nunes dos Santos <leandronunes@gmail.com> <LeandroNunes@3f533792-8f58-4932-b0fe-aaf55b0a4547> 15 Leandro Nunes dos Santos <leandronunes@gmail.com> <LeandroNunes@3f533792-8f58-4932-b0fe-aaf55b0a4547>
12 Moises Machado <moises@colivre.coop.br> <MoisesMachado@3f533792-8f58-4932-b0fe-aaf55b0a4547> 16 Moises Machado <moises@colivre.coop.br> <MoisesMachado@3f533792-8f58-4932-b0fe-aaf55b0a4547>
13 Valessio Brito <valessio@gmail.com> <ValessioBrito@3f533792-8f58-4932-b0fe-aaf55b0a4547> 17 Valessio Brito <valessio@gmail.com> <ValessioBrito@3f533792-8f58-4932-b0fe-aaf55b0a4547>
@@ -8,13 +8,18 @@ Developers @@ -8,13 +8,18 @@ Developers
8 8
9 Antonio Terceiro <terceiro@colivre.coop.br> 9 Antonio Terceiro <terceiro@colivre.coop.br>
10 Aurelio A. Heckert <aurelio@colivre.coop.br> 10 Aurelio A. Heckert <aurelio@colivre.coop.br>
  11 +Bráulio Bhavamitra <brauliobo@gmail.com>
  12 +Caio SBA <caiosba@gmail.com>
11 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> 13 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>
  14 +Daniel Cunha <daniel@colivre.coop.br>
12 Fernanda Lopes <nanda.listas+psl@gmail.com> 15 Fernanda Lopes <nanda.listas+psl@gmail.com>
13 Grazieno Pellegrino <grazieno@gmail.com> 16 Grazieno Pellegrino <grazieno@gmail.com>
14 Italo Valcy <italo@dcc.ufba.br> 17 Italo Valcy <italo@dcc.ufba.br>
15 Joenio Costa <joenio@colivre.coop.br> 18 Joenio Costa <joenio@colivre.coop.br>
16 Josef Spillner <josef.spillner@tu-dresden.de> 19 Josef Spillner <josef.spillner@tu-dresden.de>
  20 +Keilla Menezes <keilla@colivre.coop.br>
17 Leandro Nunes dos Santos <leandronunes@gmail.com> 21 Leandro Nunes dos Santos <leandronunes@gmail.com>
  22 +LinguÁgil 2010 <linguagil.bahia@gmail.com>
18 Martín Olivera <molivera@solar.org.ar> 23 Martín Olivera <molivera@solar.org.ar>
19 Moises Machado <moises@colivre.coop.br> 24 Moises Machado <moises@colivre.coop.br>
20 Nanda Lopes <nanda.listas+psl@gmail.com> 25 Nanda Lopes <nanda.listas+psl@gmail.com>
@@ -22,6 +27,7 @@ Raphaël Rousseau &lt;raph@r4f.org&gt; @@ -22,6 +27,7 @@ Raphaël Rousseau &lt;raph@r4f.org&gt;
22 Raquel Lira <raquel.lira@gmail.com> 27 Raquel Lira <raquel.lira@gmail.com>
23 Rodrigo Souto <rodrigo@colivre.coop.br> 28 Rodrigo Souto <rodrigo@colivre.coop.br>
24 Ronny Kursawe <kursawe.ronny@googlemail.com> 29 Ronny Kursawe <kursawe.ronny@googlemail.com>
  30 +Samuel R. C. Vale <srcvale@holoscopio.com>
25 Valessio Brito <valessio@gmail.com> 31 Valessio Brito <valessio@gmail.com>
26 Yann Lugrin <yann.lugrin@liquid-concept.ch> 32 Yann Lugrin <yann.lugrin@liquid-concept.ch>
27 33
@@ -61,3 +61,26 @@ The above command makes the server available at http://localhost:9999/ @@ -61,3 +61,26 @@ The above command makes the server available at http://localhost:9999/
61 61
62 The sample-data data scripts creates one administrator user with login "ze" and 62 The sample-data data scripts creates one administrator user with login "ze" and
63 password "test". 63 password "test".
  64 +
  65 +Note that some operations, like generating image thumbnails, sending e-mails,
  66 +etc, are done in background in the context of a service independent from the
  67 +Rails application server. To have those tasks performed in a development
  68 +environment, you must run the delayed_job server like this:
  69 +
  70 + ./script/delayed_job run
  71 +
  72 +This will block your terminal. To stop the delayed_job server, hit Control-C.
  73 +
  74 +== Enabling exceptions notification
  75 +
  76 +By default, exception notifications are disabled in development environment. If
  77 +you want to enable it then you need to change some files:
  78 +
  79 +1) Add in config/environments/development.rb:
  80 + config.action_controller.consider_all_requests_local = false
  81 +
  82 +2) Add in app/controller/application.rb:
  83 + local_addresses.clear
  84 +
  85 +3) Add in config/noosfero.yml at development section:
  86 + exception_recipients: [admin@example.com]
@@ -13,13 +13,14 @@ You need to install some packages Noosfero depends on. On Debian GNU/Linux or @@ -13,13 +13,14 @@ You need to install some packages Noosfero depends on. On Debian GNU/Linux or
13 Debian-based systems, all of these packages are available through the Debian 13 Debian-based systems, all of these packages are available through the Debian
14 archive. You can install them with the following command: 14 archive. You can install them with the following command:
15 15
16 - # aptitude install ruby rake libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby libferret-ruby libdaemons-ruby mongrel mongrel-cluster tango-icon-theme 16 + # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby-data libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby libferret-ruby libdaemons-ruby mongrel mongrel-cluster tango-icon-theme libhpricot-ruby
17 17
18 On other systems, they may or may not be available through your regular package 18 On other systems, they may or may not be available through your regular package
19 management system. Below are the links to their homepages. 19 management system. Below are the links to their homepages.
20 20
21 * Ruby: http://www.ruby-lang.org/ 21 * Ruby: http://www.ruby-lang.org/
22 * Rake: http://rake.rubyforge.org/ 22 * Rake: http://rake.rubyforge.org/
  23 +* po4a: http://po4a.alioth.debian.org/
23 * Ruby-GetText: http://www.yotabanana.com/hiki/ruby-gettext.html?ruby-gettext (at least version 1.9.0) 24 * Ruby-GetText: http://www.yotabanana.com/hiki/ruby-gettext.html?ruby-gettext (at least version 1.9.0)
24 * Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby 25 * Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby
25 * rcov: http://eigenclass.org/hiki/rcov 26 * rcov: http://eigenclass.org/hiki/rcov
@@ -32,79 +33,268 @@ management system. Below are the links to their homepages. @@ -32,79 +33,268 @@ management system. Below are the links to their homepages.
32 * Daemons - http://daemons.rubyforge.org/ 33 * Daemons - http://daemons.rubyforge.org/
33 * Mongrel: http://mongrel.rubyforge.org/ 34 * Mongrel: http://mongrel.rubyforge.org/
34 * tango-icon-theme: http://tango.freedesktop.org/Tango_Icon_Library 35 * tango-icon-theme: http://tango.freedesktop.org/Tango_Icon_Library
  36 +* Hpricot: http://hpricot.com/
35 37
36 -If you manage to install Noosfero successfully, please feel free to contact the  
37 -Noosfero development mailing with the instructions for doing so, and we'll  
38 -include it here. 38 +Note: the tango-icon-theme package is not available in Debian Lenny's main
  39 +repository, because back then it was not DFSG-free (Debian Squeeze will have it
  40 +in main). You can either add the non-free repository to your sources.list file,
  41 +or download the .deb directly and install it manually with `dpkg -i`. In this
  42 +case will need do install hicolor-icon-theme as well bacause tango-icon-theme
  43 +depends on it. After that you can try the command line above again, but
  44 +without "tango-icon-theme".
  45 +
  46 +If you manage to install Noosfero successfully on other systems than Debian,
  47 +please feel free to contact the Noosfero development mailing with the
  48 +instructions for doing so, and we'll include it here.
39 49
40 === Setting up a production environment 50 === Setting up a production environment
41 51
  52 +DISCLAIMER: this installation procedure is tested with Debian stable, which is
  53 +currently the only recommended operating system for production usage. It is
  54 +possible that you can install it on other systems, and if you do so, please
  55 +report it on one of the Noosfero mailing lists, and please send a patch
  56 +updating these instructions.
  57 +
  58 +As root user
  59 +============
  60 +
42 NOTE: these instructions are for seting up a *production* environment. If you 61 NOTE: these instructions are for seting up a *production* environment. If you
43 -are going to do Noosfero development, you don't need to do these steps. See the  
44 -HACKING file instead. 62 +are going to do Noosfero development, you don't need to do these steps. Stop
  63 +here and see the HACKING file instead.
  64 +
  65 +Install memcached. On Debian:
  66 +
  67 +# apt-get install memcached
  68 +
  69 +Study whether you need to raise the ammount of memory it uses for caching,
  70 +depending on the demand you expect for your site. If you are going to run a
  71 +high-traffic site, you will want to raise the ammount of memory reserved for
  72 +caching.
  73 +
  74 +It is recommended that you run noosfero with its own user account. To create
  75 +such an account, please do the following:
  76 +
  77 +# adduser --system --group noosfero --shell /bin/sh --home /var/lib/noosfero
  78 +
  79 +(note that you can change the $HOME directory of the user if you wish, here we
  80 +are using /var/lib/noosfero)
  81 +
  82 +The --system option will tell adduser to create a system user, i.e. this user
  83 +will not have a password and cannot login to the system directly. To become
  84 +this user, you have to use sudo:
  85 +
  86 +# sudo -u noosfero -i
  87 +
  88 +or
  89 +
  90 +# su - noosfero
  91 +
  92 +As noosfero user
  93 +================
  94 +
  95 +downloading from git
  96 +--------------------
  97 +
  98 +Here we are cloning the noosfero repository from git. Note: you will need to
  99 +install git before.
  100 +
  101 +$ git clone git://git.colivre.coop.br/noosfero.git current
  102 +$ cd current
  103 +$ git checkout -b stable origin/stable
  104 +
  105 +downloading tarball
  106 +-------------------
  107 +
  108 +Note: replace 0.27.1 below from the latest stable version.
  109 +
  110 +$ wget http://noosfero.org/pub/Development/NoosferoVersion00x27x01/noosfero-0.27.1.tar.gz
  111 +$ tar -zxvf noosfero-0.27.1.tar.gz
  112 +$ ln -s noosfero-0.27.1 current
  113 +$ cd current
  114 +
  115 +Copy config/ferret_server.yml.dist to config/ferret_server.yml. You will
  116 +probably not need to customize this configuration, but have a look at it.
  117 +
  118 +Create the mongrel configuration file:
  119 +
  120 +$ mongrel_rails cluster::configure
  121 +
  122 +Edit config/mongrel_cluster.yml to suit your needs. Make sure your apache
  123 +configuration matches the mongrel cluster configuration, specially in respect
  124 +to the ports and numbers of mongrel instances.
  125 +
  126 +Note: currently Noosfero only supports Rails 2.1.0, which is the version in
  127 +Debian Lenny. If you have a Rails version newer than that, Noosfero will
  128 +probably not work. You can install Rails 2.1.0 into your Noosfero installation
  129 +with the following procedure:
  130 +
  131 +$ cd /var/lib/noosfero/current/vendor
  132 +$ wget http://ftp.de.debian.org/debian/pool/main/r/rails/rails_2.1.0.orig.tar.gz
  133 +$ tar xzf rails_2.1.0.orig.tar.gz
  134 +$ ln -s rails-2.1.0 rails
  135 +
  136 +As root user
  137 +============
  138 +
  139 +Setup Noosfero log and tmp directories:
  140 +
  141 +# cd /var/lib/noosfero/current
  142 +# ./etc/init.d/noosfero setup
  143 +
  144 +Now it's time to setup the database. In this example we are using PostgreSQL,
  145 +so if you are planning to use a different database this steps won't apply.
  146 +
  147 +# apt-get install postgresql libpgsql-ruby
  148 +# su postgres -c 'createuser noosfero -S -d -R'
  149 +
  150 +By default Rails will try to connect on postgresql through 5432 port, but
  151 +Debian start postgresql on port 5433, then is needed to change postgresql to
  152 +start on port 5432 in /etc/postgresql/8.3/main/postgresql.conf file.
  153 +
  154 +Restart postgresql:
  155 +
  156 +# invoke-rc.d postgresql-8.3 restart
  157 +
  158 +Noosfero needs a functional e-mail setup to work: the local mail system should
  159 +be able to deliver e-mail to the internet, either directly or through an
  160 +external SMTP server.
  161 +
  162 +If you know mail systems well, you just need to make sure thet the local MTA,
  163 +listening on localhost:25, is able to deliver e-mails to the internet. Any mail
  164 +server will do it.
  165 +
  166 +If you are not a mail specialist, we suggest that you use the Postfix mail
  167 +server, since it is easy to configure and very reliable. Just follow the
  168 +instructions below.
  169 +
  170 +To install Postfix:
  171 +
  172 +# apt-get install postfix
  173 +
  174 +During the installation process, you will be asked a few questions. Your answer
  175 +to them will vary in 2 cases:
  176 +
  177 +Case 1: you can send e-mails directly to the internet. This will be the case
  178 +for most commercial private servers. Your answers should be:
  179 +
  180 + General type of mail configuration: Internet site
  181 + System mail name: the name of your domain, e.g. "mysocialnetwork.com"
  182 +
  183 +Case 2: you cannot, or don't want to, send e-mail directly to the internet.
  184 +This happens for example if your server is not allowed to make outbound
  185 +connections on port 25, or if you want to concentrate all your outbound mail
  186 +through a single SMTP server. Your answers in this case should be:
  187 +
  188 + General type of mail configuration: Internet with smarthost
  189 + System mail name: the name of your domain, e.g. "mysocialnetwork.com"
  190 + SMTP relay host: smtp.yourprovider.com
  191 +
  192 +Note that smtp.yourprovider.com must allow your server to deliver e-mails
  193 +through it. You should probably ask your servive provider about this.
  194 +
  195 +There is another possibility: if you are installing on a shared server, and
  196 +don't have permission to configure the local MTA, you can instruct Noosfero to
  197 +send e-mails directly through an external server. Please note that this should
  198 +be your last option, since contacting an external SMTP server directly may slow
  199 +down your Noosfero application server. To configure Noosfero to send e-mails
  200 +through an external SMTP server, follow the instructions on
  201 +http://noosfero.org/Development/SMTPMailSending
45 202
46 -* install memcached. Study whether you need to raise the ammount of memory it uses for caching, depending on the demand you expect for your site.  
47 -* enter the directory where you unpacked noosfero  
48 -* copy config/ferret_server.yml.dist to config/ferret_server.yml  
49 -* create the mongrel configuration file: `mongrel_rails cluster::configure`  
50 -** then edit config/mongrel_cluster.yml to suit your environment. Make sure your apache configuration matches the mongrel cluster configuration, specially in respect to the ports and numbers of mongrel instances.  
51 -* create needed temporary directories:  
52 - mkdir tmp  
53 - mkdir tmp/pids  
54 - mkdir log  
55 -* create database (example using PostgreSQL, YMMV) 203 +As noosfero user
  204 +================
56 205
57 - root user  
58 - =========  
59 - # sudo apt-get install postgresql libpgsql-ruby  
60 - # su - postgres 206 +Now create the databases:
61 207
62 - postgres user  
63 - =============  
64 - postgres@HOST:~$ createuser noosfero  
65 - Shall the new role be a superuser? (y/n) n  
66 - Shall the new role be allowed to create databases? (y/n) y  
67 - Shall the new role be allowed to create more new roles? (y/n) n 208 +$ cd /var/lib/noosfero/current
  209 +$ createdb noosfero_production
  210 +$ createdb noosfero_development
  211 +$ createdb noosfero_test
68 212
69 - noosfero_user  
70 - =============  
71 - createdb noosfero_production  
72 - createdb noosfero_development  
73 - createdb noosfero_test 213 +The development and test databases are actually optional. If you are creating a
  214 +stricly production server, you will probably not need them.
74 215
75 -* configure database access in config/database.yml 216 +Now we want to configure Noosfero for accessing the database we just created.
  217 +To do that, you can 1) copy config/database.yml.pgsql to config/database.yml,
  218 +or create config/database.yml from scratch with the following content:
76 219
77 -* test database access:  
78 -** first create the development database  
79 - rake db:schema:load  
80 -** if everything goes right, then create the production database:  
81 - RAILS_ENV=production rake db:schema:load 220 + production:
  221 + adapter: postgresql
  222 + encoding: unicode
  223 + database: noosfero_production
  224 + username: noosfero
82 225
83 -* create sample data:  
84 - RAILS_ENV=production rake db:populate 226 +Now, to test the database access, you can fire the Rails database console:
85 227
86 -* Add the domain name you will be using for your noosfero site to the list of  
87 - environment domain, like this: 228 +$ ./script/dbconsole production
88 229
89 - ./script/runner "Environment.default.domains << Domain.new(:name => 'your.domain.com')" 230 +If it connects to your database, then everything is fine. If you got an error
  231 +message, then you have to check your database configuration.
90 232
91 - (of course, replace your.domain.com with your real domain) 233 +Create the database structure:
92 234
93 -* compile the translations:  
94 - rake makemo 235 +$ RAILS_ENV=production rake db:schema:load
95 236
96 -* start the server:  
97 - ./script/production start 237 +Now we have to create some initial data. To create your default environment
  238 +(the first one), run the command below:
98 239
99 -* to stop the server: 240 +$ RAILS_ENV=production ./script/runner 'Environment.create!(:name => "My environment", :is_default => true)'
100 241
101 - ./script/production stop 242 +(of course, replace "My environment" with your environment's name!)
102 243
103 -* to restart the server: 244 +And now you have to add the domain name you will be using for your noosfero
  245 +site to the list of domains of that default environment you just created:
104 246
105 - ./script/production restart 247 +$ RAILS_ENV=production ./script/runner "Environment.default.domains << Domain.new(:name => 'your.domain.com')"
106 248
107 -* enable the following apache modules: 249 +(replace "your.domain.com" with your actual domain name)
  250 +
  251 +Add at least one user as admin of environment:
  252 +
  253 +$ RAILS_ENV=production ./script/runner "User.create(:login => 'adminuser', :email => 'admin@example.com', :password => 'admin', :password_confirmation => 'admin', :environment => Environment.default)"
  254 +
  255 +(replace "adminuser", "admin@example.com", "admin" with the login, email
  256 +and password of your environment admin)
  257 +
  258 +Compile the translations:
  259 +
  260 +$ RAILS_ENV=production rake noosfero:translations:compile
  261 +
  262 +To start the Noosfero application servers:
  263 +
  264 +$ ./script/production start
  265 +
  266 +At this point you have a functional Noosfero installation running, the only
  267 +thing left is to configure your webserver as a reverse proxy to pass requests
  268 +to them.
  269 +
  270 +Enabling exception notifications
  271 +================================
  272 +
  273 +This is an optional step. You will need it only if you want to receive e-mail
  274 +notifications when some exception occurs on Noosfero.
  275 +
  276 +First, install this version of the gem.
  277 +Others versions may not be compatible with Noosfero:
  278 +
  279 +# gem install exception_notification -v 1.0.20090728
  280 +
  281 +You can configure the e-mails that will receive the notifications.
  282 +Change the file config/noosfero.yml as the following example, replacing the
  283 +e-mails by real ones:
  284 +
  285 + production:
  286 + exception_recipients: [admin@example.com, you@example.com]
  287 +
  288 +==================
  289 +Apache instalation
  290 +==================
  291 +
  292 +# apt-get install apache2
  293 +
  294 +Apache configuration
  295 +--------------------
  296 +
  297 +First you have to enable the following some apache modules:
108 298
109 deflate 299 deflate
110 expires 300 expires
@@ -113,26 +303,32 @@ HACKING file instead. @@ -113,26 +303,32 @@ HACKING file instead.
113 proxy_http 303 proxy_http
114 rewrite 304 rewrite
115 305
116 - On Debian GNU/Linux system, these modules can be enabled with the following  
117 - command line, as root: 306 +On Debian GNU/Linux system, these modules can be enabled with the following
  307 +command line, as root:
118 308
119 - a2enmod deflate expires proxy proxy_balancer proxy_http rewrite 309 +# a2enmod deflate expires proxy proxy_balancer proxy_http rewrite
120 310
121 - In other systems the way by which you enable apache modules may be different. 311 +In other systems the way by which you enable apache modules may be different.
122 312
123 -* Configure apache web server as a reverse proxy. You can use the template  
124 - below, replacing /path/to/noosfero with the directory in which your noosfero  
125 - installation is, your.domain.com with the domain name of your noosfero site.  
126 - We are assuming that you are running two mongrel instances on ports 4000 and  
127 - 4001. If your setup is different you'll need to adjust <Proxy> section. If  
128 - you don't understand something in the configuration, please refer to the  
129 - apache documentation. 313 +Now with the Apache configuration. You can use the template below, replacing
  314 +/var/lib/noosfero/current with the directory in which your noosfero
  315 +installation is, your.domain.com with the domain name of your noosfero site.
  316 +We are assuming that you are running two mongrel instances on ports 3000 and
  317 +3001. If your setup is different you'll need to adjust <Proxy> section. If you
  318 +don't understand something in the configuration, please refer to the apache
  319 +documentation.
  320 +
  321 +Add a file called "mysite" (or whatever name you want to give to your noosfero
  322 +site) to /etc/apache2/sites-available with the following content, and customize
  323 +as needed (as usual, make sure you replace "your.domain.com" with you actual
  324 +domain name, and "/var/lib/noosfero/current" with the directory where Noosfero
  325 +is installed):
130 326
131 <VirtualHost *:80> 327 <VirtualHost *:80>
132 ServerName your.domain.com 328 ServerName your.domain.com
133 329
134 - DocumentRoot "/path/to/noosfero/public"  
135 - <Directory "/path/to/noosfero/public"> 330 + DocumentRoot "/var/lib/noosfero/current/public"
  331 + <Directory "/var/lib/noosfero/current/public">
136 Options FollowSymLinks 332 Options FollowSymLinks
137 AllowOverride None 333 AllowOverride None
138 Order Allow,Deny 334 Order Allow,Deny
@@ -156,18 +352,110 @@ HACKING file instead. @@ -156,18 +352,110 @@ HACKING file instead.
156 LogLevel warn 352 LogLevel warn
157 CustomLog /var/log/apache2/noosfero.access.log combined 353 CustomLog /var/log/apache2/noosfero.access.log combined
158 354
159 - Include /path/to/noosfero/etc/noosfero/apache/cache.conf 355 + Include /var/lib/noosfero/current/etc/noosfero/apache/cache.conf
160 356
161 </VirtualHost> 357 </VirtualHost>
162 358
163 <Proxy balancer://noosfero> 359 <Proxy balancer://noosfero>
164 - BalancerMember http://127.0.0.1:4000  
165 - BalancerMember http://127.0.0.1:4001 360 + BalancerMember http://127.0.0.1:3000
  361 + BalancerMember http://127.0.0.1:3001
166 Order Allow,Deny 362 Order Allow,Deny
167 Allow from All 363 Allow from All
168 </Proxy> 364 </Proxy>
169 365
170 - The cache.conf file included in the end of the <VirtualHost> section is  
171 - important, since it will tell apache to pass expiration and cache headers to  
172 - clients so that the site feels faster for users. Do we need to say that using  
173 - that configuration is strongly recommended? 366 +The cache.conf file included in the end of the <VirtualHost> section is
  367 +important, since it will tell apache to pass expiration and cache headers to
  368 +clients so that the site feels faster for users. Do we need to say that using
  369 +that configuration is strongly recommended?
  370 +
  371 +Enable that site with (as root, replace "mysite" with the actual name you gave
  372 +to your site configuration):
  373 +
  374 +# a2ensite mysite
  375 +
  376 +Now restart your apache server (as root):
  377 +
  378 +# invoke-rc.d apache2 restart
  379 +
  380 +============
  381 +Maintainance
  382 +============
  383 +
  384 +To ease the maintainance, install a symbolic link for the Noosfero startup
  385 +script in your server and add it to the system initialization and shutdown
  386 +sequences (as root):
  387 +
  388 +# ln -s /var/lib/noosfero/current/etc/init.d/noosfero /etc/init.d/noosfero
  389 +# update-rc.d noosfero defaults
  390 + Adding system startup for /etc/init.d/noosfero ...
  391 + /etc/rc0.d/K20noosfero -> ../init.d/noosfero
  392 + /etc/rc1.d/K20noosfero -> ../init.d/noosfero
  393 + /etc/rc6.d/K20noosfero -> ../init.d/noosfero
  394 + /etc/rc2.d/S20noosfero -> ../init.d/noosfero
  395 + /etc/rc3.d/S20noosfero -> ../init.d/noosfero
  396 + /etc/rc4.d/S20noosfero -> ../init.d/noosfero
  397 + /etc/rc5.d/S20noosfero -> ../init.d/noosfero
  398 +
  399 +Now to start Noosfero, you do as root:
  400 +
  401 +# invoke-rc.d noosfero start
  402 +
  403 +To stop Noosfero:
  404 +
  405 +# invoke-rc.d noosfero start
  406 +
  407 +To restart Noosfero:
  408 +
  409 +# invoke-rc.d noosfero restart
  410 +
  411 +Noosfero will be automatically started during system boot, and automatically
  412 +stopped if the system shuts down for some reason (or during the shutdown part
  413 +of a reboot).
  414 +
  415 +=============
  416 +Rotating logs
  417 +=============
  418 +
  419 +Noosfero provides an example logrotate configuation to rotate its logs. To use
  420 +it, create a symbolic link in /etc/logrotate.d/:
  421 +
  422 +# cd /etc/logrotate.d/
  423 +# ln -s /var/lib/noosfero/current/etc/logrotate.d/noosfero
  424 +
  425 +Note that the provided file assumes Noosfero logging is being done in
  426 +/var/log/noosfero (which is the case if you followed the instructions above
  427 +correctly). If the logs are stored elsewhere, it's recommended that you copy
  428 +the file over to /etc/logrotate.d/ and modify it to point to your local log
  429 +directly.
  430 +
  431 +=========
  432 +Upgrading
  433 +=========
  434 +
  435 +If you followed the steps in this document and installed Noosfero from the git
  436 +repository, then upgrading is easy. First, you need to allow the noosfero user
  437 +to restart the memcached server with sudo, by adding the following line in
  438 +/etc/sudoers:
  439 +
  440 +noosfero ALL=NOPASSWD: /etc/init.d/memcached
  441 +
  442 +Then, to perform an upgrade, do the following as the noosfero user:
  443 +
  444 +$ cd /var/lib/noosfero/current
  445 +$ ./script/git-upgrade
  446 +
  447 +The git-upgrade script will take care of everything for you. It will first stop
  448 +the service, then fetch the current source code, upgrade database, compile
  449 +translations, and then start the service again.
  450 +
  451 +Note 1: make sure your local git repository is following the "stable" branch,
  452 +just like the instructions above. The "master" branch is not recommended for
  453 +use in production environments.
  454 +
  455 +Note 2: always read the release notes before upgrading. Sometimes there will be
  456 +steps that must be performed manually. If that is the case, you can invoke the
  457 +git-upgrade script with the special parameter "--shell" that will give you a
  458 +shell after the upgrade, which you can use to perform any manual steps
  459 +required:
  460 +
  461 +$ ./script/git-upgrade --shell
INSTALL.chat 0 → 100644
@@ -0,0 +1,255 @@ @@ -0,0 +1,255 @@
  1 +== XMPP/Chat Client Setup
  2 +
  3 +To configure XMPP/BOSH in Noosfero you need:
  4 +
  5 +* REST Client - http://github.com/archiloque/rest-client
  6 +* SystemTimer - http://ph7spot.com/musings/system-timer
  7 +* Pidgin data files - http://www.pidgin.im/
  8 +
  9 +If you use Debian Lenny:
  10 +
  11 +# apt-get install librestclient-ruby (from backports)
  12 +# apt-get install pidgin-data
  13 +# apt-get install ruby1.8-dev
  14 +# gem install SystemTimer
  15 +
  16 +Take a look at util/chat directory to see samples of config file to configure a
  17 +XMPP/BOSH server with ejabberd, postgresql and apache2.
  18 +
  19 +== XMPP/Chat Server Setup
  20 +
  21 +This is a step-by-step guide to get a XMPP service working, in a Debian system.
  22 +
  23 +1. Install the required packages
  24 +
  25 +# apt-get -t lenny-backports install ejabberd
  26 +# apt-get install odbc-postgresql
  27 +
  28 +2. Ejabberd configuration
  29 +
  30 +All the following changes must be done in config file:
  31 +
  32 + /etc/ejabberd/ejabberd.cfg
  33 +
  34 + 2.1. Set the default admin user
  35 +
  36 +{ acl, admin, { user, "john", "www.example.com" } }.
  37 +{ acl, admin, { user, "bart", "www.example.com" } }.
  38 +
  39 + 2.2. Set the default host
  40 +
  41 +{ hosts, [ "www.example.com" ] }.
  42 +
  43 + 2.3. Http-Bind activation
  44 +
  45 +{ 5280, ejabberd_http, [
  46 + http_bind,
  47 + web_admin
  48 + ]
  49 +}
  50 +
  51 +(...)
  52 +
  53 +{ modules, [
  54 + {mod_http_bind, []},
  55 + ...
  56 +] }.
  57 +
  58 +Ejabberd creates semi-anonymous rooms by default, but Noosfero's Jabber client
  59 +needs non-anonymous room, then we need to change default params of creation
  60 +rooms in ejabberd to create non-anonymous rooms.
  61 +
  62 +In non-anonymous rooms the jabber service sends the new occupant's full JID to
  63 +all occupants in the room[1].
  64 +
  65 +Add option "{default_room_options, [{anonymous, false}]}" to
  66 +/etc/ejabberd/ejabberd.cfg in mod_muc session. See below:
  67 +
  68 +{ mod_muc, [
  69 + %%{host, "conference.@HOST@"},
  70 + {access, muc},
  71 + {access_create, muc},
  72 + {access_persistent, muc},
  73 + {access_admin, muc_admin},
  74 + {max_users, 500},
  75 + {default_room_options, [{anonymous, false}]}
  76 +]},
  77 +
  78 +[1] - http://xmpp.org/extensions/xep-0045.html#enter-nonanon
  79 +
  80 +
  81 + 2.4. Authentication method
  82 +
  83 +To use Postgresql through ODBC, the following modifications must be done:
  84 +
  85 + * Disable the default method:
  86 +
  87 +{auth_method, internal}.
  88 +
  89 + * Enable autheticantion through ODBC:
  90 +
  91 +{auth_method, odbc}.
  92 +
  93 + * Set database server name
  94 +
  95 +{odbc_server, "DSN=PostgreSQLEjabberdNoosfero"}.
  96 +
  97 +
  98 + 2.5. Increase the shaper traffic limit
  99 +
  100 +{ shaper, normal, { maxrate, 10000000 } }.
  101 +
  102 +
  103 + 2.6. Disable unused modules
  104 +
  105 +Unused modules can be disabled, for example:
  106 +
  107 + * s2s
  108 + * web_admin
  109 + * mod_pubsub
  110 + * mod_irc
  111 + * mod_offine
  112 + * mod_admin_extra
  113 + * mod_register
  114 +
  115 +
  116 + 2.7. Enable ODBC modules
  117 +
  118 + * mod_privacy -> mod_privacy_odbc
  119 + * mod_private -> mod_private_odbc
  120 + * mod_roster -> mod_roster_odbc
  121 +
  122 +3. Configuring Postgresql
  123 +
  124 +Login as noosfero user, and execute:
  125 +
  126 + $ psql noosfero < /path/to/noosfero/util/chat/postgresql/ejabberd.sql
  127 +
  128 +Where 'noosfero' may need to be replace by the name of the database used for
  129 +Noosfero.
  130 +
  131 +This will create a new schema inside the noosfero database, called 'ejabberd'.
  132 +
  133 +Note 'noosfero' user should have permission to create Postgresql schemas. Also,
  134 +there should be at least one domain with 'is_default = true' in 'domains'
  135 +table, otherwise people couldn't see your friends online.
  136 +
  137 +
  138 +4. ODBC configuration
  139 +
  140 +The following files must be created:
  141 +
  142 + * /etc/odbc.ini
  143 +
  144 +[PostgreSQLEjabberdNoosfero]
  145 +Description = PostgreSQL Noosfero ejabberd database
  146 +Driver = PostgreSQL Unicode
  147 +Trace = No
  148 +TraceFile = /tmp/psqlodbc.log
  149 +Database = noosfero
  150 +Servername = localhost
  151 +UserName = <DBUSER>
  152 +Password = <DBPASS>
  153 +Port =
  154 +ReadOnly = No
  155 +RowVersioning = No
  156 +ShowSystemTables = No
  157 +ShowOidColumn = No
  158 +FakeOidIndex = No
  159 +ConnSettings = SET search_path TO ejabberd
  160 +
  161 + * /etc/odbcinst.ini
  162 +
  163 +[PostgreSQL Unicode]
  164 +Description = PostgreSQL ODBC driver (Unicode version)
  165 +Driver = /usr/lib/odbc/psqlodbcw.so
  166 +Setup = /usr/lib/odbc/libodbcpsqlS.so
  167 +Debug = 0
  168 +CommLog = 1
  169 +UsageCount = 3
  170 +
  171 + * testing all:
  172 +
  173 +# isql 'PostgreSQLEjabberdNoosfero' DBUSER
  174 +
  175 +
  176 +5. Enabling kernel polling and SMP in /etc/default/ejabberd
  177 +
  178 +POLL=true
  179 +SMP=auto
  180 +
  181 +
  182 +6. Increase the file descriptors limit for user ejabberd
  183 +
  184 + 6.1. Uncomment this line in file /etc/pam.d/su:
  185 +
  186 +session required pam_limits.so
  187 +
  188 +
  189 + 6.2. Add this lines to file /etc/security/limits.conf:
  190 +
  191 +ejabberd hard nofile 65536
  192 +ejabberd soft nofile 65536
  193 +
  194 +Now, test the configuration:
  195 +
  196 +# cat /proc/<EJABBERD_BEAM_PROCESS_PID>/limits
  197 +
  198 +
  199 +7. Apache Configuration
  200 +
  201 +Apache server must be configurated as follow:
  202 +
  203 + * /etc/apache2/sites-enabled/noosfero
  204 +
  205 +RewriteEngine On
  206 +Include /usr/share/noosfero/util/chat/apache/xmpp.conf
  207 +
  208 + * /etc/apache2/apache2.conf:
  209 +
  210 +<IfModule mpm_worker_module>
  211 + StartServers 8
  212 + MinSpareThreads 25
  213 + MaxSpareThreads 75
  214 + ThreadLimit 128
  215 + ThreadsPerChild 128
  216 + MaxClients 2048
  217 + MaxRequestsPerChild 0
  218 +</IfModule>
  219 +
  220 +Note: module proxy_http must be enabled:
  221 +
  222 +# a2enmod proxy_http
  223 +
  224 +
  225 +8. DNS configuration
  226 +
  227 + * /etc/bind/db.colivre:
  228 +
  229 +_xmpp-client._tcp SRV 5 100 5222 master
  230 +(...)
  231 +conference CNAME master
  232 +_xmpp-client._tcp.conference SRV 5 100 5222 master
  233 +
  234 +
  235 +9. Testing this Setup
  236 +
  237 +Adjust shell limits to proceed with some benchmarks and load tests:
  238 +
  239 +# ulimit −s 256
  240 +# ulimit −n 8192
  241 +# echo 10 > /proc/sys/net/ipv4/tcp_syn_retries
  242 +
  243 +To measure the bandwidth between server and client:
  244 +
  245 + * at server side:
  246 +
  247 +# iperf −s
  248 +
  249 + * at client side:
  250 +
  251 +# iperf −c server_ip
  252 +
  253 +For heavy load tests, clone and use this software:
  254 +
  255 +git clone http://git.holoscopio.com/git/metal/tester.git
INSTALL.varnish 0 → 100644
@@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
  1 += Setting up Varnish for your Noosfero site
  2 +
  3 +Varnish is a HTTP caching server, and using it together with Noosfero is highly
  4 +recommended. See http://www.varnish-cache.org/ for more information on Varnish.
  5 +
  6 +Varnish can be set up to use with Noosfero with the following steps:
  7 +
  8 +1) setup Noosfero with apache according to the INSTALL file.
  9 +
  10 +2) install Varnish
  11 +
  12 + # apt-get install varnish
  13 +
  14 +Noosfero was tested with Varnish 2.x. If you are using a Debian Lenny (and you
  15 +should, unless Debian already released Squeeze by now), make sure you install
  16 +varnish from the lenny-backports suite.
  17 +
  18 +3) Enable varnish logging:
  19 +
  20 +3a) Edit /etc/default/varnishncsa and uncomment the line that contains:
  21 +
  22 +VARNISHNCSA_ENABLED=1
  23 +
  24 +The varnish log will be written to /var/log/varnish/varnishncsa.log in an
  25 +apache-compatible format. You should change your statistics generation software
  26 +(e.g. awstats) to use that instead of apache logs.
  27 +
  28 +3b) Restart Varnish Logging service
  29 +
  30 + # invoke-rc.d varnishncsa start
  31 +
  32 +4) Change Apache to listen on port 8080 instead of 80
  33 +
  34 +4a) Edit /etc/apache2/ports.conf, and:
  35 +
  36 + * change 'Listen 80' to 'Listen 127.0.0.1:8080'
  37 + * change 'NameVirtualHost *:80' to 'NameVirtualHost *:8080'
  38 +
  39 +4b) Edit /etc/apache2/sites-enabled/*, and change '<VirtualHost *:80>' to '<VirtualHost *:8080>'
  40 +
  41 +4c) Restart apache
  42 +
  43 + # invoke-rc.d apache2 restart
  44 +
  45 +5) Change Varnish to listen on port 80
  46 +
  47 +5a) Edit /etc/default/varnish and change '-a :6081' to '-a :80'
  48 +
  49 +5b) Restart Varnish
  50 +
  51 + # invoke-rc.d varnish restart
  52 +
  53 +6) Configure varnish to store separate caches for each language
  54 +
  55 +6a) Add the following line to your /etc/varnish/default.vcl file (assuming
  56 +Noosfero is installed in /var/lib/noosfero):
  57 +
  58 + include "/var/lib/noosfero/etc/noosfero/varnish-accept-language.vcl";
  59 +
  60 +6b) Restart Varnish
  61 +
  62 + # invoke-rc.d varnish restart
  63 +
  64 +Thanks to Cosimo Streppone for varnish-accept-language. See
  65 +http://github.com/cosimo/varnish-accept-language for more information.
  66 +
  67 + -- Antonio Terceiro <terceiro@colivre.coop.br> Sat, 04 Sep 2010 17:29:27 -0300
Rakefile.pkg
@@ -1,101 +0,0 @@ @@ -1,101 +0,0 @@
1 -require(File.join(File.dirname(__FILE__), 'config', 'boot'))  
2 -  
3 -require 'rake'  
4 -require 'rake/testtask'  
5 -require 'rake/rdoctask'  
6 -require 'rake/packagetask'  
7 -require 'noosfero'  
8 -  
9 -Rake::PackageTask.new(Noosfero::PROJECT, Noosfero::VERSION) do |p|  
10 - p.need_tar_gz = true  
11 -  
12 - # cleaning temporary files  
13 - FileUtils.rm_rf('tmp')  
14 - FileUtils.mkdir_p('tmp/cache')  
15 - FileUtils.mkdir_p('tmp/sessions')  
16 - FileUtils.mkdir_p('tmp/sockets')  
17 -  
18 - # application files  
19 - p.package_files.include('app/**/*.{rb,rhtml,rjs,rxml,erb}')  
20 - p.package_files.include('config/**/*.{rb,sqlite3}')  
21 - p.package_files.include('config/ferret_server.yml.dist')  
22 - p.package_files.include('db/migrate/*.rb')  
23 - p.package_files.include('db/schema.rb')  
24 - p.package_files.include('doc/README_FOR_APP')  
25 - p.package_files.include('lib/**/*.{rake,rb}')  
26 - p.package_files.include('Rakefile')  
27 - p.package_files.include('Rakefile.pkg')  
28 -  
29 - # translation files  
30 - p.package_files.include('po/*/*.po')  
31 - p.package_files.include('po/noosfero.pot')  
32 -  
33 - # templates  
34 - p.package_files.include('public/designs/templates/**/*')  
35 -  
36 - # icon sets  
37 - p.package_files.include('public/designs/icons/tango/**/*')  
38 - p.package_files.exclude('public/designs/icons/tango/Tango')  
39 - p.package_files.exclude('public/designs/icons/default')  
40 -  
41 - # themes  
42 - p.package_files.include('public/designs/themes/noosfero/**/*')  
43 - p.package_files.include('public/designs/themes/base/**/*')  
44 - p.package_files.exclude('public/designs/themes/default')  
45 -  
46 - # static files  
47 - p.package_files.include('public/dispatch.*')  
48 - p.package_files.include('public/favicon.ico')  
49 - p.package_files.include('public/*.html')  
50 - p.package_files.include('public/*.html.erb')  
51 - p.package_files.include('public/images/**/*')  
52 - p.package_files.include('public/javascripts/**/*')  
53 - p.package_files.include('public/robots.txt')  
54 - p.package_files.include('public/stylesheets/**/*')  
55 -  
56 - # top-level docs  
57 - p.package_files.include('README')  
58 - p.package_files.include('COPYING')  
59 - p.package_files.include('COPYRIGHT')  
60 - p.package_files.include('INSTALL')  
61 - p.package_files.include('HACKING')  
62 -  
63 - # scripts  
64 - p.package_files.include('script/**/*')  
65 -  
66 - # test files  
67 - p.package_files.include('test/**/*.{rb,yml}')  
68 - p.package_files.include('test/fixtures/files/*')  
69 - p.package_files.include('features/**/*')  
70 - p.package_files.include('config/cucumber.yml')  
71 -  
72 - # empty directories that must exist  
73 - p.package_files.include('tmp/cache')  
74 - p.package_files.include('tmp/sessions')  
75 - p.package_files.include('tmp/sockets')  
76 - p.package_files.include('log')  
77 -  
78 - # symbolic links  
79 - p.package_files.include('app/views/profile_design/*')  
80 - p.package_files.include('app/views/environment_design/*')  
81 -  
82 - # util  
83 - p.package_files.include('util/**/*')  
84 -  
85 - # external resources  
86 - p.package_files.include('vendor/**/*')  
87 - p.package_files.exclude('vendor/rails')  
88 -  
89 - # online documentation  
90 - p.package_files.include('doc/noosfero/**/*')  
91 -  
92 - # do not install locally generated files  
93 - p.package_files.exclude('coverage/**/*')  
94 - p.package_files.exclude('public/images/[0-9][0-9][0-9][0-9]/**/*')  
95 -  
96 -end  
97 -  
98 -task :default => :package  
99 -task :clean => :clobber_package  
100 -  
101 -# vim: ft=ruby  
@@ -1,49 +0,0 @@ @@ -1,49 +0,0 @@
1 -0 - All modified code have the tag UPGRADE on it.  
2 -  
3 -1 - I Changed acts_as_versioned plugin code to the new one:  
4 -  
5 - http://github.com/technoweenie/acts_as_versioned/tree/master  
6 -  
7 -2 - I removed the 'build_person' calls after create the user on UserTest  
8 -  
9 -3 - The settings_items method generated a lot of bugs. Theses bugs are explained here:  
10 - Solution 1: http://casperfabricius.com/site/2008/06/12/serialize-doesnt-play-nice-with-rails-21-partial-updates/  
11 - Solution 2: http://www.kalzumeus.com/2008/06/28/rails-fails-to-update-serialized-columns-on-save/  
12 -  
13 -The problem is how rails is working with ActiveRecord. The serialization data was not save on database.  
14 -  
15 -I add this code  
16 -  
17 - send(self.class.settings_field.to_s + '_will_change!')  
18 -  
19 -on lib/acts_as_having_settings  
20 -  
21 -I used the solution 1.  
22 -  
23 -I choose solution number 2 for Environment model. I think it is more clear in this situation.  
24 -I put the code:  
25 -  
26 - self.partial_updates = false  
27 -  
28 -on Environment model.  
29 -  
30 -  
31 -4 - acts_as_searchable -> find_by_contents  
32 -  
33 -I solved an incompatibility between paginate and geokit with an work around like this the one explained on the link:  
34 -  
35 - http://groups.google.com/group/will_paginate/browse_thread/thread/1bbb1f0c5810f1c1  
36 -  
37 -5 - The test of this method 'theme_include' of application helper was not passing.  
38 -  
39 -I changed the tests  
40 - - "should 'render theme footer' do"  
41 - - " should 'ignore unexisting theme footer' do"  
42 -  
43 - to make the test usefull  
44 -  
45 -  
46 -6 - I changed the way as the product is created on ManageProducts controller. I removed the line:  
47 - @product.build_image unless @product.image  
48 -  
49 -because the image was never saved. It don't have valid values to be saved.  
app/controllers/admin/admin_panel_controller.rb
@@ -40,7 +40,7 @@ class AdminPanelController &lt; AdminController @@ -40,7 +40,7 @@ class AdminPanelController &lt; AdminController
40 end 40 end
41 redirect_to :action => 'set_portal_folders' 41 redirect_to :action => 'set_portal_folders'
42 else 42 else
43 - flash[:notice] = __('Community not found. You must insert the identifier of a community from this environment') 43 + session[:notice] = __('Community not found. You must insert the identifier of a community from this environment')
44 end 44 end
45 end 45 end
46 end 46 end
@@ -54,7 +54,7 @@ class AdminPanelController &lt; AdminController @@ -54,7 +54,7 @@ class AdminPanelController &lt; AdminController
54 folders = params[:folders].map{|fid| Folder.find(:first, :conditions => {:profile_id => env.portal_community, :id => fid})} if params[:folders] 54 folders = params[:folders].map{|fid| Folder.find(:first, :conditions => {:profile_id => env.portal_community, :id => fid})} if params[:folders]
55 env.portal_folders = folders 55 env.portal_folders = folders
56 if env.save 56 if env.save
57 - flash[:notice] = _('Saved the portal folders') 57 + session[:notice] = _('Saved the portal folders')
58 redirect_to :action => 'set_portal_news_amount' 58 redirect_to :action => 'set_portal_news_amount'
59 end 59 end
60 end 60 end
@@ -63,7 +63,7 @@ class AdminPanelController &lt; AdminController @@ -63,7 +63,7 @@ class AdminPanelController &lt; AdminController
63 def set_portal_news_amount 63 def set_portal_news_amount
64 if request.post? 64 if request.post?
65 if @environment.update_attributes(params[:environment]) 65 if @environment.update_attributes(params[:environment])
66 - flash[:notice] = _('Saved the number of news on folders') 66 + session[:notice] = _('Saved the number of news on folders')
67 redirect_to :action => 'index' 67 redirect_to :action => 'index'
68 end 68 end
69 end 69 end
app/controllers/admin/categories_controller.rb
@@ -5,9 +5,9 @@ class CategoriesController &lt; AdminController @@ -5,9 +5,9 @@ class CategoriesController &lt; AdminController
5 helper :categories 5 helper :categories
6 6
7 def index 7 def index
8 - # WORKAROUND: restricting the category trees to display. Region and  
9 - # ProductCategory have VERY LARGE trees.  
10 @categories = environment.categories.find(:all, :conditions => "parent_id is null AND type is null") 8 @categories = environment.categories.find(:all, :conditions => "parent_id is null AND type is null")
  9 + @regions = environment.regions.find(:all, :conditions => {:parent_id => nil})
  10 + @product_categories = environment.product_categories.find(:all, :conditions => {:parent_id => nil})
11 end 11 end
12 12
13 ALLOWED_TYPES = CategoriesHelper::TYPES.map {|item| item[1] } 13 ALLOWED_TYPES = CategoriesHelper::TYPES.map {|item| item[1] }
app/controllers/admin/environment_design_controller.rb
@@ -3,7 +3,7 @@ class EnvironmentDesignController &lt; BoxOrganizerController @@ -3,7 +3,7 @@ class EnvironmentDesignController &lt; BoxOrganizerController
3 protect 'edit_environment_design', :environment 3 protect 'edit_environment_design', :environment
4 4
5 def available_blocks 5 def available_blocks
6 - @available_blocks ||= [ ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, PeopleBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock ] 6 + @available_blocks ||= [ ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, PeopleBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, FeaturedProductsBlock, CategoriesBlock, RawHTMLBlock ]
7 end 7 end
8 8
9 end 9 end
app/controllers/admin/environment_role_manager_controller.rb
@@ -14,9 +14,9 @@ class EnvironmentRoleManagerController &lt; AdminController @@ -14,9 +14,9 @@ class EnvironmentRoleManagerController &lt; AdminController
14 @roles = params[:roles] ? Role.find(params[:roles]) : [] 14 @roles = params[:roles] ? Role.find(params[:roles]) : []
15 @person = Person.find(params[:person]) 15 @person = Person.find(params[:person])
16 if @person.define_roles(@roles, environment) 16 if @person.define_roles(@roles, environment)
17 - flash[:notice] = _('Roles successfuly updated') 17 + session[:notice] = _('Roles successfuly updated')
18 else 18 else
19 - flash[:notice] = _('Couldnt change the roles') 19 + session[:notice] = _('Couldnt change the roles')
20 end 20 end
21 redirect_to :action => :index 21 redirect_to :action => :index
22 end 22 end
@@ -42,9 +42,9 @@ class EnvironmentRoleManagerController &lt; AdminController @@ -42,9 +42,9 @@ class EnvironmentRoleManagerController &lt; AdminController
42 def remove_role 42 def remove_role
43 @association = RoleAssignment.find(params[:id]) 43 @association = RoleAssignment.find(params[:id])
44 if @association.destroy 44 if @association.destroy
45 - flash[:notice] = _('Member succefully unassociated') 45 + session[:notice] = _('Member succefully unassociated')
46 else 46 else
47 - flash[:notice] = _('Failed to unassociate member') 47 + session[:notice] = _('Failed to unassociate member')
48 end 48 end
49 redirect_to :aciton => 'index' 49 redirect_to :aciton => 'index'
50 end 50 end
@@ -52,9 +52,9 @@ class EnvironmentRoleManagerController &lt; AdminController @@ -52,9 +52,9 @@ class EnvironmentRoleManagerController &lt; AdminController
52 def unassociate 52 def unassociate
53 @association = RoleAssignment.find(params[:id]) 53 @association = RoleAssignment.find(params[:id])
54 if @association.destroy 54 if @association.destroy
55 - flash[:notice] = _('Member succefully unassociated') 55 + session[:notice] = _('Member succefully unassociated')
56 else 56 else
57 - flash[:notice] = _('Failed to unassociate member') 57 + session[:notice] = _('Failed to unassociate member')
58 end 58 end
59 redirect_to :aciton => 'index' 59 redirect_to :aciton => 'index'
60 end 60 end
app/controllers/admin/features_controller.rb
@@ -8,7 +8,7 @@ class FeaturesController &lt; AdminController @@ -8,7 +8,7 @@ class FeaturesController &lt; AdminController
8 post_only :update 8 post_only :update
9 def update 9 def update
10 if @environment.update_attributes(params[:environment]) 10 if @environment.update_attributes(params[:environment])
11 - flash[:notice] = _('Features updated successfully.') 11 + session[:notice] = _('Features updated successfully.')
12 redirect_to :action => 'index' 12 redirect_to :action => 'index'
13 else 13 else
14 render :action => 'index' 14 render :action => 'index'
@@ -24,7 +24,7 @@ class FeaturesController &lt; AdminController @@ -24,7 +24,7 @@ class FeaturesController &lt; AdminController
24 def manage_person_fields 24 def manage_person_fields
25 environment.custom_person_fields = params[:person_fields] 25 environment.custom_person_fields = params[:person_fields]
26 if environment.save! 26 if environment.save!
27 - flash[:notice] = _('Person fields updated successfully.') 27 + session[:notice] = _('Person fields updated successfully.')
28 else 28 else
29 flash[:error] = _('Person fields not updated successfully.') 29 flash[:error] = _('Person fields not updated successfully.')
30 end 30 end
@@ -34,9 +34,9 @@ class FeaturesController &lt; AdminController @@ -34,9 +34,9 @@ class FeaturesController &lt; AdminController
34 def manage_enterprise_fields 34 def manage_enterprise_fields
35 environment.custom_enterprise_fields = params[:enterprise_fields] 35 environment.custom_enterprise_fields = params[:enterprise_fields]
36 if environment.save! 36 if environment.save!
37 - flash[:notice] = _('Enterprise fields updated successfully.') 37 + session[:notice] = __('Enterprise fields updated successfully.')
38 else 38 else
39 - flash[:error] = _('Enterprise fields not updated successfully.') 39 + flash[:error] = __('Enterprise fields not updated successfully.')
40 end 40 end
41 redirect_to :action => 'manage_fields' 41 redirect_to :action => 'manage_fields'
42 end 42 end
@@ -44,7 +44,7 @@ class FeaturesController &lt; AdminController @@ -44,7 +44,7 @@ class FeaturesController &lt; AdminController
44 def manage_community_fields 44 def manage_community_fields
45 environment.custom_community_fields = params[:community_fields] 45 environment.custom_community_fields = params[:community_fields]
46 if environment.save! 46 if environment.save!
47 - flash[:notice] = _('Community fields updated successfully.') 47 + session[:notice] = _('Community fields updated successfully.')
48 else 48 else
49 flash[:error] = _('Community fields not updated successfully.') 49 flash[:error] = _('Community fields not updated successfully.')
50 end 50 end
app/controllers/admin/plugins_controller.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +class PluginsController < AdminController
  2 +
  3 + def index
  4 + @active_plugins = Noosfero::Plugin.all.map {|plugin_name| plugin_name.constantize }.compact
  5 + end
  6 +
  7 + post_only :update
  8 + def update
  9 + params[:environment][:enabled_plugins].delete('')
  10 + if @environment.update_attributes(params[:environment])
  11 + session[:notice] = _('Plugins updated successfully.')
  12 + else
  13 + session[:error] = _('Plugins were not updated successfully.')
  14 + end
  15 + redirect_to :action => 'index'
  16 + end
  17 +
  18 +end
app/controllers/admin/role_controller.rb
@@ -19,7 +19,7 @@ class RoleController &lt; AdminController @@ -19,7 +19,7 @@ class RoleController &lt; AdminController
19 if @role.save 19 if @role.save
20 redirect_to :action => 'show', :id => @role 20 redirect_to :action => 'show', :id => @role
21 else 21 else
22 - flash[:notice] = _('Failed to create role') 22 + session[:notice] = _('Failed to create role')
23 render :action => 'new' 23 render :action => 'new'
24 end 24 end
25 end 25 end
@@ -33,7 +33,7 @@ class RoleController &lt; AdminController @@ -33,7 +33,7 @@ class RoleController &lt; AdminController
33 if @role.update_attributes(params[:role]) 33 if @role.update_attributes(params[:role])
34 redirect_to :action => 'show', :id => @role 34 redirect_to :action => 'show', :id => @role
35 else 35 else
36 - flash[:notice] = _('Failed to edit role') 36 + session[:notice] = _('Failed to edit role')
37 render :action => 'edit' 37 render :action => 'edit'
38 end 38 end
39 end 39 end
@@ -43,7 +43,7 @@ class RoleController &lt; AdminController @@ -43,7 +43,7 @@ class RoleController &lt; AdminController
43 if @role.destroy 43 if @role.destroy
44 redirect_to :action => 'index' 44 redirect_to :action => 'index'
45 else 45 else
46 - flash[:notice] = _('Failed to edit role') 46 + session[:notice] = _('Failed to edit role')
47 redirect_to :action => 'index' 47 redirect_to :action => 'index'
48 end 48 end
49 end 49 end
app/controllers/admin/users_controller.rb 0 → 100644
@@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
  1 +class UsersController < AdminController
  2 +
  3 + protect 'manage_environment_users', :environment
  4 +
  5 + def index
  6 + @users = environment.users
  7 + respond_to do |format|
  8 + format.html
  9 + format.xml do
  10 + render :xml => @users.to_xml(
  11 + :skip_types => true,
  12 + :only => %w[email login created_at updated_at],
  13 + :include => { :person => {:only => %w[name updated_at created_at address birth_date contact_phone identifier lat lng] } }
  14 + )
  15 + end
  16 + format.csv do
  17 + render :template => "users/index_csv.rhtml", :content_type => 'text/csv', :layout => false
  18 + end
  19 + end
  20 + end
  21 +
  22 + def send_mail
  23 + @mailing = environment.mailings.build(params[:mailing])
  24 + if request.post?
  25 + @mailing.locale = locale
  26 + @mailing.person = user
  27 + if @mailing.save
  28 + session[:notice] = _('The e-mails are being sent')
  29 + redirect_to :action => 'index'
  30 + else
  31 + session[:notice] = _('Could not create the e-mail')
  32 + end
  33 + end
  34 + end
  35 +
  36 +end
app/controllers/box_organizer_controller.rb
@@ -96,7 +96,7 @@ class BoxOrganizerController &lt; ApplicationController @@ -96,7 +96,7 @@ class BoxOrganizerController &lt; ApplicationController
96 expire_timeout_fragment(@block.cache_keys) 96 expire_timeout_fragment(@block.cache_keys)
97 redirect_to :action => 'index' 97 redirect_to :action => 'index'
98 else 98 else
99 - flash[:notice] = _('Failed to remove block') 99 + session[:notice] = _('Failed to remove block')
100 end 100 end
101 end 101 end
102 102
app/controllers/my_profile/cms_controller.rb
@@ -14,7 +14,8 @@ class CmsController &lt; MyProfileController @@ -14,7 +14,8 @@ class CmsController &lt; MyProfileController
14 end 14 end
15 end 15 end
16 16
17 - protect_if :except => [:set_home_page, :edit, :destroy, :publish] do |c, user, profile| 17 + before_filter :login_required, :except => [:suggest_an_article]
  18 + protect_if :except => [:suggest_an_article, :set_home_page, :edit, :destroy, :publish] do |c, user, profile|
18 user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)) 19 user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile))
19 end 20 end
20 21
@@ -40,15 +41,11 @@ class CmsController &lt; MyProfileController @@ -40,15 +41,11 @@ class CmsController &lt; MyProfileController
40 def available_article_types 41 def available_article_types
41 articles = [ 42 articles = [
42 TinyMceArticle, 43 TinyMceArticle,
43 - TextileArticle 44 + TextileArticle,
  45 + Event
44 ] 46 ]
45 - articles << Event unless profile.environment.enabled?(:disable_asset_events) 47 + articles += special_article_types if params && params[:cms]
46 parent_id = params ? params[:parent_id] : nil 48 parent_id = params ? params[:parent_id] : nil
47 - if !parent_id or !Article.find(parent_id).blog?  
48 - articles += [  
49 - RssFeed  
50 - ]  
51 - end  
52 if profile.enterprise? 49 if profile.enterprise?
53 articles << EnterpriseHomepage 50 articles << EnterpriseHomepage
54 end 51 end
@@ -56,22 +53,31 @@ class CmsController &lt; MyProfileController @@ -56,22 +53,31 @@ class CmsController &lt; MyProfileController
56 end 53 end
57 54
58 def special_article_types 55 def special_article_types
59 - [Folder, Blog, UploadedFile] 56 + [Folder, Blog, UploadedFile, Forum, Gallery, RssFeed]
60 end 57 end
61 58
62 def view 59 def view
63 @article = profile.articles.find(params[:id]) 60 @article = profile.articles.find(params[:id])
64 - @subitems = @article.children.reject {|item| item.folder? }  
65 - if @article.blog?  
66 - @subitems.reject! {|item| item.class == RssFeed } 61 + conditions = []
  62 + if @article.has_posts?
  63 + conditions = ['type != ?', 'RssFeed']
67 end 64 end
68 - @folders = @article.children.select {|item| item.folder? } 65 +
  66 + @articles = @article.children.paginate(
  67 + :order => "case when type = 'Folder' then 0 when type ='Blog' then 1 else 2 end, updated_at DESC",
  68 + :conditions => conditions,
  69 + :per_page => per_page,
  70 + :page => params[:npage]
  71 + )
69 end 72 end
70 73
71 def index 74 def index
72 @article = nil 75 @article = nil
73 - @subitems = profile.top_level_articles.reject {|item| item.folder? }  
74 - @folders = profile.top_level_articles.select {|item| item.folder?} 76 + @articles = profile.top_level_articles.paginate(
  77 + :order => "case when type = 'Folder' then 0 when type ='Blog' then 1 else 2 end, updated_at DESC",
  78 + :per_page => per_page,
  79 + :page => params[:npage]
  80 + )
75 render :action => 'view' 81 render :action => 'view'
76 end 82 end
77 83
@@ -79,17 +85,21 @@ class CmsController &lt; MyProfileController @@ -79,17 +85,21 @@ class CmsController &lt; MyProfileController
79 @article = profile.articles.find(params[:id]) 85 @article = profile.articles.find(params[:id])
80 @parent_id = params[:parent_id] 86 @parent_id = params[:parent_id]
81 @type = params[:type] || @article.class.to_s 87 @type = params[:type] || @article.class.to_s
  88 + translations if @article.translatable?
  89 + continue = params[:continue]
82 90
83 refuse_blocks 91 refuse_blocks
84 - if !@article.nil? && @article.blog? || !@type.nil? && @type == 'Blog'  
85 - @back_url = url_for(:controller => 'profile_editor', :profile => profile.identifier)  
86 - end  
87 - record_coming_from_public_view 92 + record_coming
88 if request.post? 93 if request.post?
89 @article.last_changed_by = user 94 @article.last_changed_by = user
90 if @article.update_attributes(params[:article]) 95 if @article.update_attributes(params[:article])
91 - redirect_back  
92 - return 96 + if !continue
  97 + if @article.content_type.nil? || @article.image?
  98 + redirect_to @article.view_url
  99 + else
  100 + redirect_to :action => (@article.parent ? 'view' : 'index'), :id => @article.parent
  101 + end
  102 + end
93 end 103 end
94 end 104 end
95 end 105 end
@@ -99,6 +109,7 @@ class CmsController &lt; MyProfileController @@ -99,6 +109,7 @@ class CmsController &lt; MyProfileController
99 109
100 # user must choose an article type first 110 # user must choose an article type first
101 111
  112 + record_coming
102 @type = params[:type] 113 @type = params[:type]
103 if @type.blank? 114 if @type.blank?
104 @article_types = [] 115 @article_types = []
@@ -110,12 +121,9 @@ class CmsController &lt; MyProfileController @@ -110,12 +121,9 @@ class CmsController &lt; MyProfileController
110 }) 121 })
111 end 122 end
112 @parent_id = params[:parent_id] 123 @parent_id = params[:parent_id]
113 - render :action => 'select_article_type', :layout => false 124 + render :action => 'select_article_type', :layout => false, :back_to => @back_to
114 return 125 return
115 else 126 else
116 - if @type == 'Blog'  
117 - @back_url = url_for(:controller => 'profile_editor', :profile => profile.identifier)  
118 - end  
119 refuse_blocks 127 refuse_blocks
120 end 128 end
121 129
@@ -131,13 +139,19 @@ class CmsController &lt; MyProfileController @@ -131,13 +139,19 @@ class CmsController &lt; MyProfileController
131 @parent_id = parent.id 139 @parent_id = parent.id
132 end 140 end
133 141
134 - record_creating_from_public_view 142 + translations if @article.translatable?
135 143
136 @article.profile = profile 144 @article.profile = profile
137 @article.last_changed_by = user 145 @article.last_changed_by = user
  146 +
  147 + continue = params[:continue]
138 if request.post? 148 if request.post?
139 if @article.save 149 if @article.save
140 - redirect_back 150 + if continue
  151 + redirect_to :action => 'edit', :id => @article
  152 + else
  153 + redirect_to @article.view_url
  154 + end
141 return 155 return
142 end 156 end
143 end 157 end
@@ -150,7 +164,7 @@ class CmsController &lt; MyProfileController @@ -150,7 +164,7 @@ class CmsController &lt; MyProfileController
150 @article = profile.articles.find(params[:id]) 164 @article = profile.articles.find(params[:id])
151 profile.home_page = @article 165 profile.home_page = @article
152 profile.save(false) 166 profile.save(false)
153 - flash[:notice] = _('"%s" configured as home page.') % @article.name 167 + session[:notice] = _('"%s" configured as home page.') % @article.name
154 redirect_to :action => 'view', :id => @article.id 168 redirect_to :action => 'view', :id => @article.id
155 end 169 end
156 170
@@ -159,29 +173,36 @@ class CmsController &lt; MyProfileController @@ -159,29 +173,36 @@ class CmsController &lt; MyProfileController
159 @article = @parent = check_parent(params[:parent_id]) 173 @article = @parent = check_parent(params[:parent_id])
160 @target = @parent ? ('/%s/%s' % [profile.identifier, @parent.full_name]) : '/%s' % profile.identifier 174 @target = @parent ? ('/%s/%s' % [profile.identifier, @parent.full_name]) : '/%s' % profile.identifier
161 @folders = Folder.find(:all, :conditions => { :profile_id => profile }) 175 @folders = Folder.find(:all, :conditions => { :profile_id => profile })
162 - record_coming_from_public_view if @article 176 + @media_listing = params[:media_listing]
  177 + if @article && !@media_listing
  178 + record_coming
  179 + end
163 if request.post? && params[:uploaded_files] 180 if request.post? && params[:uploaded_files]
164 params[:uploaded_files].each do |file| 181 params[:uploaded_files].each do |file|
165 @uploaded_files << UploadedFile.create(:uploaded_data => file, :profile => profile, :parent => @parent) unless file == '' 182 @uploaded_files << UploadedFile.create(:uploaded_data => file, :profile => profile, :parent => @parent) unless file == ''
166 end 183 end
167 @errors = @uploaded_files.select { |f| f.errors.any? } 184 @errors = @uploaded_files.select { |f| f.errors.any? }
168 - @back_to = params[:back_to]  
169 if @errors.any? 185 if @errors.any?
170 - if @back_to && @back_to == 'media_listing' 186 + if @media_listing
171 flash[:notice] = _('Could not upload all files') 187 flash[:notice] = _('Could not upload all files')
172 - redirect_back 188 + redirect_to :action => 'media_listing'
173 else 189 else
174 render :action => 'upload_files', :parent_id => @parent_id 190 render :action => 'upload_files', :parent_id => @parent_id
175 end 191 end
176 else 192 else
177 - if params[:back_to]  
178 - redirect_back 193 + if @media_listing
  194 + flash[:notice] = _('All files were uploaded successfully')
  195 + redirect_to :action => 'media_listing'
179 else 196 else
180 - redirect_to( if @parent  
181 - {:action => 'view', :id => @parent.id} 197 + if @back_to
  198 + redirect_to @back_to
182 else 199 else
183 - {:action => 'index'}  
184 - end) 200 + redirect_to( if @parent
  201 + {:action => 'view', :id => @parent.id}
  202 + else
  203 + {:action => 'index'}
  204 + end)
  205 + end
185 end 206 end
186 end 207 end
187 end 208 end
@@ -212,11 +233,11 @@ class CmsController &lt; MyProfileController @@ -212,11 +233,11 @@ class CmsController &lt; MyProfileController
212 233
213 def publish 234 def publish
214 @article = profile.articles.find(params[:id]) 235 @article = profile.articles.find(params[:id])
215 - record_coming_from_public_view 236 + record_coming
216 @groups = profile.memberships - [profile] 237 @groups = profile.memberships - [profile]
217 @marked_groups = [] 238 @marked_groups = []
218 groups_ids = profile.memberships.map{|m|m.id.to_s} 239 groups_ids = profile.memberships.map{|m|m.id.to_s}
219 - @marked_groups = params[:marked_groups].map do |item| 240 + @marked_groups = params[:marked_groups].map do |key, item|
220 if groups_ids.include?(item[:group_id]) 241 if groups_ids.include?(item[:group_id])
221 item.merge :group => Profile.find(item.delete(:group_id)) 242 item.merge :group => Profile.find(item.delete(:group_id))
222 end 243 end
@@ -232,8 +253,47 @@ class CmsController &lt; MyProfileController @@ -232,8 +253,47 @@ class CmsController &lt; MyProfileController
232 end 253 end
233 end 254 end
234 if @failed.blank? 255 if @failed.blank?
235 - flash[:notice] = _("Your publish request was sent successfully")  
236 - redirect_back 256 + session[:notice] = _("Your publish request was sent successfully")
  257 + if @back_to
  258 + redirect_to @back_to
  259 + else
  260 + redirect_to @article.view_url
  261 + end
  262 + end
  263 + end
  264 + end
  265 +
  266 + def publish_on_portal_community
  267 + @article = profile.articles.find(params[:id])
  268 + if request.post?
  269 + if environment.portal_community
  270 + task = ApproveArticle.create!(:article => @article, :name => params[:name], :target => environment.portal_community, :requestor => user)
  271 + begin
  272 + task.finish unless environment.portal_community.moderated_articles?
  273 + flash[:notice] = _("Your publish request was sent successfully")
  274 + rescue
  275 + flash[:error] = _("Your publish request couldn't be sent.")
  276 + end
  277 + else
  278 + flash[:notice] = _("There is no portal community to publish your article.")
  279 + end
  280 +
  281 + if @back_to
  282 + redirect_to @back_to
  283 + else
  284 + redirect_to @article.view_url
  285 + end
  286 + end
  287 + end
  288 +
  289 + def suggest_an_article
  290 + @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url)
  291 + @task = SuggestArticle.new(params[:task])
  292 + if request.post?
  293 + @task.target = profile
  294 + if @task.save
  295 + session[:notice] = _('Thanks for your suggestion. The community administrators were notified.')
  296 + redirect_to @back_to
237 end 297 end
238 end 298 end
239 end 299 end
@@ -241,24 +301,24 @@ class CmsController &lt; MyProfileController @@ -241,24 +301,24 @@ class CmsController &lt; MyProfileController
241 def media_listing 301 def media_listing
242 if params[:image_folder_id] 302 if params[:image_folder_id]
243 folder = profile.articles.find(params[:image_folder_id]) if !params[:image_folder_id].blank? 303 folder = profile.articles.find(params[:image_folder_id]) if !params[:image_folder_id].blank?
244 - @images = (folder ? folder.children : UploadedFile.find(:all, :order => 'created_at desc', :conditions => ["profile_id = ? AND parent_id is NULL", profile ])).select { |c| c.image? } 304 + @images = (folder ? folder.children : profile.top_level_articles).images
245 elsif params[:document_folder_id] 305 elsif params[:document_folder_id]
246 folder = profile.articles.find(params[:document_folder_id]) if !params[:document_folder_id].blank? 306 folder = profile.articles.find(params[:document_folder_id]) if !params[:document_folder_id].blank?
247 - @documents = (folder ? folder.children : UploadedFile.find(:all, :order => 'created_at desc', :conditions => ["profile_id = ? AND parent_id is NULL", profile ])).select { |c| c.kind_of?(UploadedFile) && !c.image? } 307 + @documents = (folder ? folder.children : profile.top_level_articles)
248 else 308 else
249 - @documents = UploadedFile.find(:all, :order => 'created_at desc', :conditions => ["profile_id = ? AND parent_id is NULL", profile ])  
250 - @images = @documents.select(&:image?) 309 + @documents = profile.articles
  310 + @images = @documents.images
251 @documents -= @images 311 @documents -= @images
252 end 312 end
253 313
254 - @images = @images.paginate(:per_page => per_page, :page => params[:ipage]) if @images  
255 - @documents = @documents.paginate(:per_page => per_page, :page => params[:dpage]) if @documents 314 + @images = @images.paginate(:per_page => per_page, :page => params[:ipage], :order => "updated_at desc") if @images
  315 + @documents = @documents.paginate(:per_page => per_page, :page => params[:dpage], :order => "updated_at desc", :conditions => {:is_image => false}) if @documents
256 316
257 - @folders = Folder.find(:all, :conditions => { :profile_id => profile }) 317 + @folders = profile.folders
258 @image_folders = @folders.select {|f| f.children.any? {|c| c.image?} } 318 @image_folders = @folders.select {|f| f.children.any? {|c| c.image?} }
259 @document_folders = @folders.select {|f| f.children.any? {|c| !c.image? && c.kind_of?(UploadedFile) } } 319 @document_folders = @folders.select {|f| f.children.any? {|c| !c.image? && c.kind_of?(UploadedFile) } }
260 320
261 - @back_to = 'media_listing' 321 + @media_listing = true
262 322
263 respond_to do |format| 323 respond_to do |format|
264 format.html { render :layout => false} 324 format.html { render :layout => false}
@@ -273,42 +333,11 @@ class CmsController &lt; MyProfileController @@ -273,42 +333,11 @@ class CmsController &lt; MyProfileController
273 333
274 protected 334 protected
275 335
276 - def redirect_back  
277 - if params[:back_to] == 'control_panel'  
278 - redirect_to :controller => 'profile_editor', :profile => @profile.identifier  
279 - elsif params[:back_to] == 'public_view'  
280 - redirect_to @article.view_url.merge(Noosfero.url_options)  
281 - elsif params[:back_to] == 'media_listing'  
282 - redirect_to :action => 'media_listing'  
283 - elsif @article.parent  
284 - redirect_to :action => 'view', :id => @article.parent  
285 - elsif @article.folder? && !@article.blog? && @article.parent  
286 - redirect_to :action => 'index' 336 + def record_coming
  337 + if request.post?
  338 + @back_to = params[:back_to]
287 else 339 else
288 - redirect_back_or_default :action => 'index'  
289 - end  
290 - end  
291 -  
292 - def record_coming_from_public_view  
293 - referer = request.referer  
294 - referer.gsub!(/\?.*/, '') unless referer.nil?  
295 - if (maybe_ssl(url_for(@article.url)).include?(referer)) || (@article == profile.home_page && maybe_ssl(url_for(profile.url)).include?(referer))  
296 - @back_to = 'public_view'  
297 - @back_url = @article.view_url  
298 - end  
299 - if !request.post? and @article.blog?  
300 - store_location(request.referer)  
301 - end  
302 - end  
303 -  
304 - def record_creating_from_public_view  
305 - referer = request.referer  
306 - if (referer =~ Regexp.new("^#{(url_for(profile.url).sub('https:', 'https?:'))}")) || params[:back_to] == 'public_view'  
307 - @back_to = 'public_view'  
308 - @back_url = referer  
309 - end  
310 - if !request.post? and @article.blog?  
311 - store_location(request.referer) 340 + @back_to = params[:back_to] || request.referer
312 end 341 end
313 end 342 end
314 343
@@ -341,5 +370,11 @@ class CmsController &lt; MyProfileController @@ -341,5 +370,11 @@ class CmsController &lt; MyProfileController
341 def per_page 370 def per_page
342 10 371 10
343 end 372 end
  373 +
  374 + def translations
  375 + @locales = Noosfero.locales.invert.reject { |name, lang| !@article.possible_translations.include?(lang) }
  376 + @selected_locale = @article.language || FastGettext.locale
  377 + end
  378 +
344 end 379 end
345 380
app/controllers/my_profile/friends_controller.rb
@@ -8,18 +8,6 @@ class FriendsController &lt; MyProfileController @@ -8,18 +8,6 @@ class FriendsController &lt; MyProfileController
8 end 8 end
9 end 9 end
10 10
11 - def add  
12 - @friend = Person.find(params[:id])  
13 - if request.post? && params[:confirmation]  
14 - # FIXME this shouldn't be in Person model?  
15 - AddFriend.create!(:person => profile, :friend => @friend, :group_for_person => params[:group])  
16 -  
17 - flash[:notice] = _('%s still needs to accept being your friend.') % @friend.name  
18 - # FIXME shouldn't redirect to the friend's page?  
19 - redirect_to :action => 'index'  
20 - end  
21 - end  
22 -  
23 def remove 11 def remove
24 @friend = profile.friends.find(params[:id]) 12 @friend = profile.friends.find(params[:id])
25 if request.post? && params[:confirmation] 13 if request.post? && params[:confirmation]
app/controllers/my_profile/mailconf_controller.rb
@@ -20,20 +20,20 @@ class MailconfController &lt; MyProfileController @@ -20,20 +20,20 @@ class MailconfController &lt; MyProfileController
20 @task = EmailActivation.new(:target => environment, :requestor => profile) 20 @task = EmailActivation.new(:target => environment, :requestor => profile)
21 begin 21 begin
22 @task.save! 22 @task.save!
23 - flash[:notice] = _('Please fill your personal information below in order to get your mailbox approved by one of the administrators') 23 + session[:notice] = _('Please fill your personal information below in order to get your mailbox approved by one of the administrators')
24 redirect_to :controller => 'profile_editor', :action => 'edit' 24 redirect_to :controller => 'profile_editor', :action => 'edit'
25 rescue Exception => ex 25 rescue Exception => ex
26 - flash[:notice] = _('e-Mail was not enabled successfully.') 26 + session[:notice] = _('e-Mail was not enabled successfully.')
27 render :action => 'index' 27 render :action => 'index'
28 end 28 end
29 end 29 end
30 post_only :disable 30 post_only :disable
31 def disable 31 def disable
32 if profile.user.disable_email! 32 if profile.user.disable_email!
33 - flash[:notice] = _('e-Mail disabled successfully.') 33 + session[:notice] = _('e-Mail disabled successfully.')
34 redirect_to :controller => 'profile_editor' 34 redirect_to :controller => 'profile_editor'
35 else 35 else
36 - flash[:notice] = _('e-Mail was not disabled successfully.') 36 + session[:notice] = _('e-Mail was not disabled successfully.')
37 redirect_to :action => 'index' 37 redirect_to :action => 'index'
38 end 38 end
39 end 39 end
app/controllers/my_profile/manage_products_controller.rb
1 class ManageProductsController < ApplicationController 1 class ManageProductsController < ApplicationController
2 needs_profile 2 needs_profile
3 3
4 - protect 'manage_products', :profile 4 + protect 'manage_products', :profile, :except => [:show]
5 before_filter :check_environment_feature 5 before_filter :check_environment_feature
  6 + before_filter :login_required, :except => [:show]
6 7
7 protected 8 protected
  9 +
8 def check_environment_feature 10 def check_environment_feature
9 if profile.environment.enabled?('disable_products_for_enterprises') 11 if profile.environment.enabled?('disable_products_for_enterprises')
10 render_not_found 12 render_not_found
@@ -13,104 +15,147 @@ class ManageProductsController &lt; ApplicationController @@ -13,104 +15,147 @@ class ManageProductsController &lt; ApplicationController
13 end 15 end
14 16
15 public 17 public
  18 +
16 def index 19 def index
17 - @products = @profile.products  
18 - @consumptions = @profile.consumptions 20 + @products = @profile.products.paginate(:per_page => 10, :page => params[:page])
19 end 21 end
20 22
21 def show 23 def show
22 @product = @profile.products.find(params[:id]) 24 @product = @profile.products.find(params[:id])
  25 + @inputs = @product.inputs
  26 + @allowed_user = user && user.has_permission?('manage_products', profile)
  27 + end
  28 +
  29 + def categories_for_selection
  30 + @category = Category.find(params[:category_id]) if params[:category_id]
  31 + @object_name = params[:object_name]
  32 + if @category
  33 + @categories = @category.children
  34 + @level = @category.leaf? ? @category.level : @categories.first.level
  35 + else
  36 + @categories = ProductCategory.top_level_for(environment)
  37 + @level = 0
  38 + end
  39 + render :partial => 'categories_for_selection', :locals => { :categories => @categories, :level => @level }
23 end 40 end
24 41
25 def new 42 def new
26 - @object = Product.new  
27 - @categories = @current_category.nil? ? ProductCategory.top_level_for(environment) : @current_category.children  
28 - @product = @profile.products.build(params[:product]) 43 + @product = @profile.products.build(:product_category_id => params[:selected_category_id])
  44 + @category = @product.product_category
  45 + @categories = ProductCategory.top_level_for(environment)
  46 + @level = 0
29 if request.post? 47 if request.post?
30 if @product.save 48 if @product.save
31 - flash[:notice] = _('Product succesfully created')  
32 - redirect_to :action => 'show', :id => @product 49 + session[:notice] = _('Product succesfully created')
  50 + render :partial => 'shared/redirect_via_javascript',
  51 + :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) }
33 else 52 else
34 - flash[:notice] = _('Could not create the product') 53 + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' }
35 end 54 end
36 end 55 end
37 end 56 end
38 57
39 def edit 58 def edit
40 @product = @profile.products.find(params[:id]) 59 @product = @profile.products.find(params[:id])
41 - @object = @profile.products.find(params[:id])  
42 - @current_category = @product.product_category  
43 - @categories = @current_category.nil? ? [] : @current_category.children 60 + field = params[:field]
  61 + if request.post?
  62 + begin
  63 + @product.update_attributes!(params[:product])
  64 + render :partial => "display_#{field}", :locals => {:product => @product}
  65 + rescue Exception => e
  66 + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => true}
  67 + end
  68 + else
  69 + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => false}
  70 + end
  71 + end
  72 +
  73 + def edit_category
  74 + @product = @profile.products.find(params[:id])
  75 + @category = @product.product_category
  76 + @categories = ProductCategory.top_level_for(environment)
  77 + @edit = true
  78 + @level = @category.level
  79 + if request.post?
  80 + if @product.update_attributes(:product_category_id => params[:selected_category_id])
  81 + render :partial => 'shared/redirect_via_javascript',
  82 + :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) }
  83 + else
  84 + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' }
  85 + end
  86 + end
  87 + end
  88 +
  89 + def add_input
  90 + @product = @profile.products.find(params[:id])
  91 + @input = @product.inputs.build
  92 + @categories = ProductCategory.top_level_for(environment)
  93 + @level = 0
44 if request.post? 94 if request.post?
45 - if @product.update_attributes(params[:product])  
46 - flash[:notice] = _('Product succesfully updated')  
47 - redirect_back_or_default :action => 'show', :id => @product 95 + if @input.update_attributes(:product_category_id => params[:selected_category_id])
  96 + @inputs = @product.inputs
  97 + render :partial => 'display_inputs'
48 else 98 else
49 - flash[:notice] = _('Could not update the product') 99 + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' }
50 end 100 end
  101 + else
  102 + render :partial => 'add_input'
51 end 103 end
52 end 104 end
53 105
54 def destroy 106 def destroy
55 @product = @profile.products.find(params[:id]) 107 @product = @profile.products.find(params[:id])
56 if @product.destroy 108 if @product.destroy
57 - flash[:notice] = _('Product succesfully removed') 109 + session[:notice] = _('Product succesfully removed')
58 redirect_back_or_default :action => 'index' 110 redirect_back_or_default :action => 'index'
59 else 111 else
60 - flash[:notice] = _('Could not remove the product') 112 + session[:notice] = _('Could not remove the product')
61 redirect_back_or_default :action => 'show', :id => @product 113 redirect_back_or_default :action => 'show', :id => @product
62 end 114 end
63 end 115 end
64 116
65 - def update_categories  
66 - @object = params[:id] ? @profile.products.find(params[:id]) : Product.new  
67 - if params[:category_id]  
68 - @current_category = Category.find(params[:category_id])  
69 - @categories = @current_category.children  
70 - else  
71 - @current_category = ProductCategory.top_level_for(environment).first  
72 - @categories = @current_category.nil? ? [] : @current_category.children 117 + def edit_input
  118 + if request.xhr?
  119 + @input = @profile.inputs.find_by_id(params[:id])
  120 + if @input
  121 + if request.post?
  122 + if @input.update_attributes(params[:input])
  123 + render :partial => 'display_input', :locals => {:input => @input}
  124 + else
  125 + render :partial => 'edit_input'
  126 + end
  127 + else
  128 + render :partial => 'edit_input'
  129 + end
  130 + else
  131 + render :text => _('The input was not found')
  132 + end
73 end 133 end
74 - render :partial => 'shared/select_categories', :locals => {:object_name => 'product', :multiple => false}, :layout => false  
75 end 134 end
76 135
77 - def update_subcategories  
78 - @current_category = ProductCategory.find(params[:id]) if params[:id]  
79 - @categories = @current_category ? @current_category.children : ProductCategory.top_level_for(environment)  
80 - render :partial => 'subcategories' 136 + def order_inputs
  137 + @product = @profile.products.find(params[:id])
  138 + @product.order_inputs!(params[:input]) if params[:input]
  139 + render :nothing => true
81 end 140 end
82 -  
83 - def new_consumption  
84 - @consumption = @profile.consumptions.build(params[:consumption]) 141 +
  142 + def remove_input
  143 + @input = @profile.inputs.find(params[:id])
  144 + @product = @input.product
85 if request.post? 145 if request.post?
86 - if @consumption.save  
87 - flash[:notice] = _('Raw material succesfully created')  
88 - redirect_to :action => 'index' 146 + if @input.destroy
  147 + @inputs = @product.inputs
  148 + render :partial => 'display_inputs'
89 else 149 else
90 - flash[:notice] = _('Could not create the raw material') 150 + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'input' }
91 end 151 end
92 end 152 end
93 end 153 end
94 154
95 - def destroy_consumption  
96 - @consumption = @profile.consumptions.find(params[:id])  
97 - if @consumption.destroy  
98 - flash[:notice] = _('Raw material succesfully removed')  
99 - else  
100 - flash[:notice] = _('Could not remove the raw material')  
101 - end  
102 - redirect_back_or_default :action => 'index'  
103 - end  
104 -  
105 - def edit_consumption  
106 - @consumption = @profile.consumptions.find(params[:id])  
107 - if request.post?  
108 - if @consumption.update_attributes(params[:consumption])  
109 - flash[:notice] = _('Raw material succesfully updated')  
110 - redirect_back_or_default :action => 'index'  
111 - else  
112 - flash[:notice] = _('Could not update the raw material')  
113 - end 155 + def certifiers_for_selection
  156 + @qualifier = Qualifier.exists?(params[:id]) ? Qualifier.find(params[:id]) : nil
  157 + render :update do |page|
  158 + page.replace_html params[:certifier_area], :partial => 'certifiers_for_selection'
114 end 159 end
115 end 160 end
116 161
app/controllers/my_profile/maps_controller.rb
@@ -8,7 +8,7 @@ class MapsController &lt; MyProfileController @@ -8,7 +8,7 @@ class MapsController &lt; MyProfileController
8 begin 8 begin
9 Profile.transaction do 9 Profile.transaction do
10 if profile.update_attributes!(params[:profile_data]) 10 if profile.update_attributes!(params[:profile_data])
11 - flash[:notice] = _('Address was updated successfully!') 11 + session[:notice] = _('Address was updated successfully!')
12 redirect_to :action => 'edit_location' 12 redirect_to :action => 'edit_location'
13 end 13 end
14 end 14 end
app/controllers/my_profile/memberships_controller.rb
@@ -7,32 +7,12 @@ class MembershipsController &lt; MyProfileController @@ -7,32 +7,12 @@ class MembershipsController &lt; MyProfileController
7 end 7 end
8 8
9 def new_community 9 def new_community
10 - @wizard = params[:wizard].blank? ? false : params[:wizard]  
11 @community = Community.new(params[:community]) 10 @community = Community.new(params[:community])
12 @community.environment = environment 11 @community.environment = environment
13 if request.post? && @community.valid? 12 if request.post? && @community.valid?
14 @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community])) 13 @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community]))
15 - if @wizard  
16 - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true  
17 - return  
18 - else  
19 - redirect_to :action => 'index'  
20 - return  
21 - end  
22 - end  
23 - if @wizard  
24 - render :layout => 'wizard' 14 + redirect_to :action => 'index'
  15 + return
25 end 16 end
26 end 17 end
27 -  
28 - def destroy_community  
29 - @community = Community.find(params[:id])  
30 - if request.post?  
31 - if @community.destroy  
32 - flash[:notice] = _('%s was removed.') % @community.short_name  
33 - redirect_to :action => 'index'  
34 - end  
35 - end  
36 - end  
37 -  
38 end 18 end
app/controllers/my_profile/profile_design_controller.rb
@@ -5,7 +5,7 @@ class ProfileDesignController &lt; BoxOrganizerController @@ -5,7 +5,7 @@ class ProfileDesignController &lt; BoxOrganizerController
5 protect 'edit_profile_design', :profile 5 protect 'edit_profile_design', :profile
6 6
7 def available_blocks 7 def available_blocks
8 - blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock ] 8 + blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock ]
9 9
10 # blocks exclusive for organizations 10 # blocks exclusive for organizations
11 if profile.has_members? 11 if profile.has_members?
@@ -24,6 +24,7 @@ class ProfileDesignController &lt; BoxOrganizerController @@ -24,6 +24,7 @@ class ProfileDesignController &lt; BoxOrganizerController
24 if profile.enterprise? 24 if profile.enterprise?
25 blocks << DisabledEnterpriseMessageBlock 25 blocks << DisabledEnterpriseMessageBlock
26 blocks << HighlightsBlock 26 blocks << HighlightsBlock
  27 + blocks << FeaturedProductsBlock
27 end 28 end
28 29
29 # product block exclusive for enterprises in environments that permits it 30 # product block exclusive for enterprises in environments that permits it
app/controllers/my_profile/profile_editor_controller.rb
1 class ProfileEditorController < MyProfileController 1 class ProfileEditorController < MyProfileController
2 2
3 - protect 'edit_profile', :profile 3 + protect 'edit_profile', :profile, :except => [:destroy_profile]
  4 + protect 'destroy_profile', :profile, :only => [:destroy_profile]
4 5
5 def index 6 def index
6 @pending_tasks = profile.all_pending_tasks.select{|i| user.has_permission?(i.permission, profile)} 7 @pending_tasks = profile.all_pending_tasks.select{|i| user.has_permission?(i.permission, profile)}
@@ -25,7 +26,7 @@ class ProfileEditorController &lt; MyProfileController @@ -25,7 +26,7 @@ class ProfileEditorController &lt; MyProfileController
25 if profile.identifier.blank? 26 if profile.identifier.blank?
26 profile.identifier = params[:profile] 27 profile.identifier = params[:profile]
27 end 28 end
28 - flash[:notice] = _('Cannot update profile') 29 + session[:notice] = _('Cannot update profile')
29 end 30 end
30 end 31 end
31 end 32 end
@@ -34,7 +35,7 @@ class ProfileEditorController &lt; MyProfileController @@ -34,7 +35,7 @@ class ProfileEditorController &lt; MyProfileController
34 @to_enable = profile 35 @to_enable = profile
35 if request.post? && params[:confirmation] 36 if request.post? && params[:confirmation]
36 unless @to_enable.update_attribute('enabled', true) 37 unless @to_enable.update_attribute('enabled', true)
37 - flash[:notice] = _('%s was not enabled.') % @to_enable.name 38 + session[:notice] = _('%s was not enabled.') % @to_enable.name
38 end 39 end
39 redirect_to :action => 'index' 40 redirect_to :action => 'index'
40 end 41 end
@@ -44,7 +45,7 @@ class ProfileEditorController &lt; MyProfileController @@ -44,7 +45,7 @@ class ProfileEditorController &lt; MyProfileController
44 @to_disable = profile 45 @to_disable = profile
45 if request.post? && params[:confirmation] 46 if request.post? && params[:confirmation]
46 unless @to_disable.update_attribute('enabled', false) 47 unless @to_disable.update_attribute('enabled', false)
47 - flash[:notice] = _('%s was not disabled.') % @to_disable.name 48 + session[:notice] = _('%s was not disabled.') % @to_disable.name
48 end 49 end
49 redirect_to :action => 'index' 50 redirect_to :action => 'index'
50 end 51 end
@@ -72,4 +73,14 @@ class ProfileEditorController &lt; MyProfileController @@ -72,4 +73,14 @@ class ProfileEditorController &lt; MyProfileController
72 end 73 end
73 end 74 end
74 75
  76 + def destroy_profile
  77 + if request.post?
  78 + if @profile.destroy
  79 + session[:notice] = _('The profile was deleted.')
  80 + redirect_to :controller => 'home'
  81 + else
  82 + session[:notice] = _('Could not delete profile')
  83 + end
  84 + end
  85 + end
75 end 86 end
app/controllers/my_profile/profile_members_controller.rb
@@ -10,23 +10,34 @@ class ProfileMembersController &lt; MyProfileController @@ -10,23 +10,34 @@ class ProfileMembersController &lt; MyProfileController
10 def update_roles 10 def update_roles
11 @roles = params[:roles] ? environment.roles.find(params[:roles].select{|r|!r.to_i.zero?}) : [] 11 @roles = params[:roles] ? environment.roles.find(params[:roles].select{|r|!r.to_i.zero?}) : []
12 @roles = @roles.select{|r| r.has_kind?('Profile') } 12 @roles = @roles.select{|r| r.has_kind?('Profile') }
13 - @person = profile.members.find { |m| m.id == params[:person].to_i }  
14 - if @person && @person.define_roles(@roles, profile)  
15 - flash[:notice] = _('Roles successfuly updated') 13 + begin
  14 + @person = profile.members.find(params[:person])
  15 + rescue ActiveRecord::RecordNotFound
  16 + @person = nil
  17 + end
  18 + if !params[:confirmation] && @person && @person.is_last_admin_leaving?(profile, @roles)
  19 + redirect_to :action => :last_admin, :roles => params[:roles], :person => @person
16 else 20 else
17 - flash[:notice] = _('Couldn\'t change the roles') 21 + if @person && @person.define_roles(@roles, profile)
  22 + session[:notice] = _('Roles successfuly updated')
  23 + else
  24 + session[:notice] = _('Couldn\'t change the roles')
  25 + end
  26 + if params[:confirmation]
  27 + redirect_to profile.url
  28 + else
  29 + redirect_to :action => :index
  30 + end
18 end 31 end
19 - redirect_to :action => :index  
20 end 32 end
21 33
22 - def change_role  
23 - @roles = Profile::Roles.organization_member_roles(environment.id)  
24 - @member = profile.members.find { |m| m.id == params[:id].to_i }  
25 - if @member  
26 - @associations = @member.find_roles(@profile)  
27 - else  
28 - redirect_to :action => :index  
29 - end 34 + 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'}
30 end 41 end
31 42
32 def add_role 43 def add_role
@@ -44,21 +55,35 @@ class ProfileMembersController &lt; MyProfileController @@ -44,21 +55,35 @@ class ProfileMembersController &lt; MyProfileController
44 def remove_role 55 def remove_role
45 @association = RoleAssignment.find(:all, :conditions => {:id => params[:id], :target_id => profile.id}) 56 @association = RoleAssignment.find(:all, :conditions => {:id => params[:id], :target_id => profile.id})
46 if @association.destroy 57 if @association.destroy
47 - flash[:notice] = 'Member succefully unassociated' 58 + session[:notice] = 'Member succefully unassociated'
48 else 59 else
49 - flash[:notice] = 'Failed to unassociate member' 60 + session[:notice] = 'Failed to unassociate member'
50 end 61 end
51 render :layout => false 62 render :layout => false
52 end 63 end
53 64
  65 + def change_role
  66 + @roles = Profile::Roles.organization_member_roles(environment.id)
  67 + begin
  68 + @member = profile.members.find(params[:id])
  69 + rescue ActiveRecord::RecordNotFound
  70 + @member = nil
  71 + end
  72 + if @member
  73 + @associations = @member.find_roles(@profile)
  74 + else
  75 + redirect_to :action => :index
  76 + end
  77 + end
  78 +
54 def unassociate 79 def unassociate
55 member = Person.find(params[:id]) 80 member = Person.find(params[:id])
56 associations = member.find_roles(profile) 81 associations = member.find_roles(profile)
57 RoleAssignment.transaction do 82 RoleAssignment.transaction do
58 if associations.map(&:destroy) 83 if associations.map(&:destroy)
59 - flash[:notice] = 'Member succefully unassociated' 84 + session[:notice] = _('Member succesfully unassociated')
60 else 85 else
61 - flash[:notice] = 'Failed to unassociate member' 86 + session[:notice] = _('Failed to unassociate member')
62 end 87 end
63 end 88 end
64 render :layout => false 89 render :layout => false
@@ -69,19 +94,61 @@ class ProfileMembersController &lt; MyProfileController @@ -69,19 +94,61 @@ class ProfileMembersController &lt; MyProfileController
69 94
70 def add_member 95 def add_member
71 if profile.enterprise? 96 if profile.enterprise?
72 - member = Person.find_by_identifier(params[:id]) 97 + member = Person.find(params[:id])
73 member.define_roles(Profile::Roles.all_roles(environment), profile) 98 member.define_roles(Profile::Roles.all_roles(environment), profile)
74 end 99 end
75 render :layout => false 100 render :layout => false
76 end 101 end
77 102
  103 + def add_admin
  104 + @title = _('Current admins')
  105 + @collection = :profile_admins
  106 +
  107 + if profile.community?
  108 + member = profile.members.find_by_identifier(params[:id])
  109 + profile.add_admin(member)
  110 + end
  111 + render :layout => false
  112 + end
  113 +
  114 + def remove_admin
  115 + @title = _('Current admins')
  116 + @collection = :profile_admins
  117 +
  118 + if profile.community?
  119 + member = profile.members.find_by_identifier(params[:id])
  120 + profile.remove_admin(member)
  121 + end
  122 + render :layout => false
  123 + end
  124 +
78 def find_users 125 def find_users
79 if !params[:query] || params[:query].length <= 2 126 if !params[:query] || params[:query].length <= 2
80 @users_found = [] 127 @users_found = []
81 - else  
82 - @users_found = Person.find_by_contents(params[:query] + '*') 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'}
83 end 136 end
84 render :layout => false 137 render :layout => false
85 end 138 end
86 139
  140 + def send_mail
  141 + @mailing = profile.mailings.build(params[:mailing])
  142 + if request.post?
  143 + @mailing.locale = locale
  144 + @mailing.person = user
  145 + if @mailing.save
  146 + session[:notice] = _('The e-mails are being sent')
  147 + redirect_to :action => 'index'
  148 + else
  149 + session[:notice] = _('Could not create the e-mail')
  150 + end
  151 + end
  152 + end
  153 +
87 end 154 end
app/controllers/my_profile/tasks_controller.rb
@@ -4,26 +4,38 @@ class TasksController &lt; MyProfileController @@ -4,26 +4,38 @@ class TasksController &lt; MyProfileController
4 4
5 def index 5 def index
6 @tasks = profile.all_pending_tasks.sort_by(&:created_at) 6 @tasks = profile.all_pending_tasks.sort_by(&:created_at)
  7 + @failed = params ? params[:failed] : {}
7 end 8 end
8 9
9 def processed 10 def processed
10 @tasks = profile.all_finished_tasks.sort_by(&:created_at) 11 @tasks = profile.all_finished_tasks.sort_by(&:created_at)
11 end 12 end
12 13
13 - VALID_DECISIONS = [ 'finish', 'cancel' ] 14 + VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ]
14 15
15 def close 16 def close
16 - decision = params[:decision]  
17 - if request.post? && VALID_DECISIONS.include?(decision) && params[:id]  
18 - task = profile.find_in_all_tasks(params[:id])  
19 - task.update_attributes!(params[:task])  
20 - begin  
21 - task.send(decision)  
22 - rescue Exception => ex  
23 - flash[:notice] = ex.clean_message 17 + failed = {}
  18 +
  19 + params[:tasks].each do |id, value|
  20 + decision = value[:decision]
  21 + if request.post? && VALID_DECISIONS.include?(decision) && id && decision != 'skip'
  22 + task = profile.find_in_all_tasks(id)
  23 + task.update_attributes!(value[:task])
  24 + begin
  25 + task.send(decision)
  26 + rescue Exception => ex
  27 + message = "#{task.title} (#{task.requestor ? task.requestor.name : task.author_name})"
  28 + failed[ex.clean_message] ? failed[ex.clean_message] << message : failed[ex.clean_message] = [message]
  29 + end
24 end 30 end
25 end 31 end
26 - redirect_to :action => 'index' 32 +
  33 + if failed.blank?
  34 + session[:notice] = _("All decisions were applied successfully.")
  35 + else
  36 + session[:notice] = _("Some decisions couldn't be applied.")
  37 + end
  38 + redirect_to params.merge!(:action => 'index', :failed => failed)
27 end 39 end
28 40
29 def new 41 def new
app/controllers/my_profile/themes_controller.rb
@@ -8,6 +8,11 @@ class ThemesController &lt; MyProfileController @@ -8,6 +8,11 @@ class ThemesController &lt; MyProfileController
8 redirect_to :action => 'index' 8 redirect_to :action => 'index'
9 end 9 end
10 10
  11 + def unset
  12 + profile.update_theme(nil)
  13 + redirect_to :action => 'index'
  14 + end
  15 +
11 def index 16 def index
12 @themes = profile.environment.themes + Theme.approved_themes(profile) 17 @themes = profile.environment.themes + Theme.approved_themes(profile)
13 @current_theme = profile.theme 18 @current_theme = profile.theme
@@ -18,7 +23,7 @@ class ThemesController &lt; MyProfileController @@ -18,7 +23,7 @@ class ThemesController &lt; MyProfileController
18 23
19 def new 24 def new
20 if !request.xhr? 25 if !request.xhr?
21 - id = params[:name].to_slug 26 + id = params[:name] ? params[:name].to_slug : 'my-theme'
22 t = Theme.new(id, :name => params[:name], :owner => profile, :public => false) 27 t = Theme.new(id, :name => params[:name], :owner => profile, :public => false)
23 t.save 28 t.save
24 redirect_to :action => 'index' 29 redirect_to :action => 'index'
app/controllers/public/account_controller.rb
1 class AccountController < ApplicationController 1 class AccountController < ApplicationController
2 2
  3 + no_design_blocks
  4 +
3 inverse_captcha :field => 'e_mail' 5 inverse_captcha :field => 'e_mail'
4 6
5 - require_ssl :except => [ :login_popup, :logout_popup, :wizard, :profile_details ] 7 + require_ssl :except => [ :login_popup, :logout_popup, :profile_details ]
6 8
7 before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise] 9 before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise]
8 before_filter :redirect_if_logged_in, :only => [:login, :signup] 10 before_filter :redirect_if_logged_in, :only => [:login, :signup]
@@ -28,10 +30,10 @@ class AccountController &lt; ApplicationController @@ -28,10 +30,10 @@ class AccountController &lt; ApplicationController
28 end 30 end
29 if redirect? 31 if redirect?
30 go_to_initial_page 32 go_to_initial_page
31 - flash[:notice] = _("Logged in successfully") 33 + session[:notice] = _("Logged in successfully")
32 end 34 end
33 else 35 else
34 - flash[:notice] = _('Incorrect username or password') if redirect? 36 + session[:notice] = _('Incorrect username or password') if redirect?
35 redirect_to :back if redirect? 37 redirect_to :back if redirect?
36 end 38 end
37 end 39 end
@@ -48,8 +50,6 @@ class AccountController &lt; ApplicationController @@ -48,8 +50,6 @@ class AccountController &lt; ApplicationController
48 # action to register an user to the application 50 # action to register an user to the application
49 def signup 51 def signup
50 @invitation_code = params[:invitation_code] 52 @invitation_code = params[:invitation_code]
51 - @wizard = params[:wizard].blank? ? false : params[:wizard]  
52 - @step = 1  
53 begin 53 begin
54 @user = User.new(params[:user]) 54 @user = User.new(params[:user])
55 @user.terms_of_use = environment.terms_of_use 55 @user.terms_of_use = environment.terms_of_use
@@ -68,38 +68,17 @@ class AccountController &lt; ApplicationController @@ -68,38 +68,17 @@ class AccountController &lt; ApplicationController
68 invitation.update_attributes!({:friend => @user.person}) 68 invitation.update_attributes!({:friend => @user.person})
69 invitation.finish 69 invitation.finish
70 end 70 end
71 - flash[:notice] = _("Thanks for signing up!")  
72 - if @wizard  
73 - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true  
74 - return  
75 - else  
76 - go_to_initial_page if redirect?  
77 - end 71 + session[:notice] = _("Thanks for signing up!")
  72 + go_to_initial_page if redirect?
78 end 73 end
79 - if @wizard  
80 - render :layout => 'wizard'  
81 - end  
82 rescue ActiveRecord::RecordInvalid 74 rescue ActiveRecord::RecordInvalid
83 @person.valid? 75 @person.valid?
84 @person.errors.delete(:identifier) 76 @person.errors.delete(:identifier)
85 @person.errors.delete(:user_id) 77 @person.errors.delete(:user_id)
86 - if @wizard  
87 - render :action => 'signup', :layout => 'wizard'  
88 - else  
89 - render :action => 'signup'  
90 - end 78 + render :action => 'signup'
91 end 79 end
92 end 80 end
93 81
94 - def wizard  
95 - render :layout => false  
96 - end  
97 -  
98 - def profile_details  
99 - @profile = Profile.find_by_identifier(params[:profile])  
100 - render :partial => 'profile_details', :layout => 'wizard'  
101 - end  
102 -  
103 # action to perform logout from the application 82 # action to perform logout from the application
104 def logout 83 def logout
105 if logged_in? 84 if logged_in?
@@ -107,7 +86,7 @@ class AccountController &lt; ApplicationController @@ -107,7 +86,7 @@ class AccountController &lt; ApplicationController
107 end 86 end
108 cookies.delete :auth_token 87 cookies.delete :auth_token
109 reset_session 88 reset_session
110 - flash[:notice] = _("You have been logged out.") 89 + session[:notice] = _("You have been logged out.")
111 redirect_to :controller => 'home', :action => 'index' 90 redirect_to :controller => 'home', :action => 'index'
112 end 91 end
113 92
@@ -118,10 +97,10 @@ class AccountController &lt; ApplicationController @@ -118,10 +97,10 @@ class AccountController &lt; ApplicationController
118 @user.change_password!(params[:current_password], 97 @user.change_password!(params[:current_password],
119 params[:new_password], 98 params[:new_password],
120 params[:new_password_confirmation]) 99 params[:new_password_confirmation])
121 - flash[:notice] = _('Your password has been changed successfully!') 100 + session[:notice] = _('Your password has been changed successfully!')
122 redirect_to :action => 'index' 101 redirect_to :action => 'index'
123 rescue User::IncorrectPassword => e 102 rescue User::IncorrectPassword => e
124 - flash[:notice] = _('The supplied current password is incorrect.') 103 + session[:notice] = _('The supplied current password is incorrect.')
125 render :action => 'change_password' 104 render :action => 'change_password'
126 end 105 end
127 else 106 else
@@ -233,6 +212,21 @@ class AccountController &lt; ApplicationController @@ -233,6 +212,21 @@ class AccountController &lt; ApplicationController
233 render :partial => 'identifier_status' 212 render :partial => 'identifier_status'
234 end 213 end
235 214
  215 + def user_data
  216 + user_data =
  217 + if logged_in?
  218 + current_user.data_hash
  219 + else
  220 + { }
  221 + end
  222 + if session[:notice]
  223 + user_data['notice'] = session[:notice]
  224 + session[:notice] = nil # consume the notice
  225 + end
  226 +
  227 + render :text => user_data.to_json, :layout => false, :content_type => "application/javascript"
  228 + end
  229 +
236 protected 230 protected
237 231
238 def redirect? 232 def redirect?
app/controllers/public/browse_controller.rb 0 → 100644
@@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
  1 +class BrowseController < PublicController
  2 +
  3 + no_design_blocks
  4 +
  5 + FILTERS = %w(
  6 + more_recent
  7 + more_active
  8 + more_popular
  9 + )
  10 +
  11 + def people
  12 + @filter = filter
  13 + @title = self.filter_description(params[:action] + '_' + @filter )
  14 +
  15 + @results = @environment.people.visible.send(@filter)
  16 +
  17 + if params[:query].blank?
  18 + @results = @results.paginate(:per_page => 27, :page => params[:page])
  19 + else
  20 + @results = @results.find_by_contents(params[:query]).paginate(:per_page => 27, :page => params[:page])
  21 + end
  22 + end
  23 +
  24 + def communities
  25 + @filter = filter
  26 + @title = self.filter_description(params[:action] + '_' + @filter )
  27 +
  28 + @results = @environment.communities.visible.send(@filter)
  29 +
  30 + if params[:query].blank?
  31 + @results = @results.paginate(:per_page => 27, :page => params[:page])
  32 + else
  33 + @results = @results.find_by_contents(params[:query]).paginate(:per_page => 27, :page => params[:page])
  34 + end
  35 + end
  36 +
  37 + protected
  38 +
  39 + def filter
  40 + if FILTERS.include?(params[:filter])
  41 + params[:filter]
  42 + else
  43 + 'more_recent'
  44 + end
  45 + end
  46 +
  47 + def filter_description(str)
  48 + {
  49 + 'people_more_recent' => _('More recent people'),
  50 + 'people_more_active' => _('More active people'),
  51 + 'people_more_popular' => _('More popular people'),
  52 + 'communities_more_recent' => _('More recent communities'),
  53 + 'communities_more_active' => _('More active communities'),
  54 + 'communities_more_popular' => _('More popular communities'),
  55 + }[str] || str
  56 + end
  57 +
  58 +end
app/controllers/public/catalog_controller.rb
1 class CatalogController < PublicController 1 class CatalogController < PublicController
2 needs_profile 2 needs_profile
  3 +
3 before_filter :check_enterprise_and_environment 4 before_filter :check_enterprise_and_environment
4 5
5 def index 6 def index
6 - @products = @profile.products 7 + @products = @profile.products.paginate(:per_page => 10, :page => params[:page])
7 end 8 end
8 9
9 - def show  
10 - @product = @profile.products.find(params[:id])  
11 - end  
12 -  
13 protected 10 protected
14 def check_enterprise_and_environment 11 def check_enterprise_and_environment
15 unless @profile.kind_of?(Enterprise) && !@profile.environment.enabled?('disable_products_for_enterprises') 12 unless @profile.kind_of?(Enterprise) && !@profile.environment.enabled?('disable_products_for_enterprises')
app/controllers/public/chat_controller.rb 0 → 100644
@@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
  1 +class ChatController < PublicController
  2 +
  3 + before_filter :login_required
  4 + before_filter :check_environment_feature
  5 +
  6 + def start_session
  7 + login = user.jid
  8 + password = current_user.crypted_password
  9 + begin
  10 + jid, sid, rid = RubyBOSH.initialize_session(login, password, "http://#{environment.default_hostname}/http-bind",
  11 + :wait => 30, :hold => 1, :window => 5)
  12 + session_data = { :jid => jid, :sid => sid, :rid => rid }
  13 + render :text => session_data.to_json, :layout => false, :content_type => 'application/javascript'
  14 + rescue
  15 + render :action => 'start_session_error', :layout => false, :status => 500
  16 + end
  17 + end
  18 +
  19 + def avatar
  20 + profile = environment.profiles.find_by_identifier(params[:id])
  21 + filename, mimetype = profile_icon(profile, :minor, true)
  22 + data = File.read(File.join(RAILS_ROOT, 'public', filename))
  23 + render :text => data, :layout => false, :content_type => mimetype
  24 + expires_in 24.hours
  25 + end
  26 +
  27 + def index
  28 + presence = current_user.last_chat_status
  29 + if presence.blank? or presence == 'chat'
  30 + render :action => 'auto_connect_online'
  31 + else
  32 + render :action => 'auto_connect_busy'
  33 + end
  34 + end
  35 +
  36 + def update_presence_status
  37 + if request.xhr?
  38 + current_user.update_attributes({:chat_status_at => DateTime.now}.merge(params[:status] || {}))
  39 + end
  40 + render :nothing => true
  41 + end
  42 +
  43 + protected
  44 +
  45 + def check_environment_feature
  46 + unless environment.enabled?('xmpp_chat')
  47 + render_not_found
  48 + return
  49 + end
  50 + end
  51 +
  52 +end
app/controllers/public/contact_controller.rb
@@ -12,10 +12,10 @@ class ContactController &lt; PublicController @@ -12,10 +12,10 @@ class ContactController &lt; PublicController
12 @contact.city = (!params[:city].blank? && City.exists?(params[:city])) ? City.find(params[:city]).name : nil 12 @contact.city = (!params[:city].blank? && City.exists?(params[:city])) ? City.find(params[:city]).name : nil
13 @contact.state = (!params[:state].blank? && State.exists?(params[:state])) ? State.find(params[:state]).name : nil 13 @contact.state = (!params[:state].blank? && State.exists?(params[:state])) ? State.find(params[:state]).name : nil
14 if @contact.deliver 14 if @contact.deliver
15 - flash[:notice] = _('Contact successfully sent') 15 + session[:notice] = _('Contact successfully sent')
16 redirect_to :action => 'new' 16 redirect_to :action => 'new'
17 else 17 else
18 - flash[:notice] = _('Contact not sent') 18 + session[:notice] = _('Contact not sent')
19 end 19 end
20 else 20 else
21 @contact = user.build_contact(profile) 21 @contact = user.build_contact(profile)
app/controllers/public/content_viewer_controller.rb
@@ -51,6 +51,8 @@ class ContentViewerController &lt; ApplicationController @@ -51,6 +51,8 @@ class ContentViewerController &lt; ApplicationController
51 return 51 return
52 end 52 end
53 53
  54 + redirect_to_translation
  55 +
54 # At this point the page will be showed 56 # At this point the page will be showed
55 @page.hit 57 @page.hit
56 58
@@ -67,9 +69,6 @@ class ContentViewerController &lt; ApplicationController @@ -67,9 +69,6 @@ class ContentViewerController &lt; ApplicationController
67 return 69 return
68 end 70 end
69 71
70 - # store location if the page is not a download  
71 - store_location  
72 -  
73 @form_div = params[:form] 72 @form_div = params[:form]
74 73
75 if request.post? && params[:comment] && params[self.icaptcha_field].blank? && params[:confirm] == 'true' && @page.accept_comments? 74 if request.post? && params[:comment] && params[self.icaptcha_field].blank? && params[:confirm] == 'true' && @page.accept_comments?
@@ -80,22 +79,28 @@ class ContentViewerController &lt; ApplicationController @@ -80,22 +79,28 @@ class ContentViewerController &lt; ApplicationController
80 remove_comment 79 remove_comment
81 end 80 end
82 81
83 - if @page.blog? 82 + if @page.has_posts?
84 posts = if params[:year] and params[:month] 83 posts = if params[:year] and params[:month]
85 filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01") 84 filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01")
86 - @page.posts.by_range(filter_date..Article.last_day_of_month(filter_date)) 85 + @page.posts.by_range(filter_date..filter_date.at_end_of_month)
87 else 86 else
88 @page.posts 87 @page.posts
89 end 88 end
90 - @posts = available_articles(posts, user).paginate :page => params[:npage], :per_page => @page.posts_per_page 89 +
  90 + posts = posts.native_translations if @page.blog? && @page.display_posts_in_current_language?
  91 +
  92 + @posts = posts.paginate({ :page => params[:npage], :per_page => @page.posts_per_page }.merge(Article.display_filter(user, profile)))
  93 +
  94 + @posts.map!{ |p| p.get_translation_to(FastGettext.locale) } if @page.blog? && @page.display_posts_in_current_language?
91 end 95 end
92 96
93 - if @page.folder? && @page.view_as == 'image_gallery' 97 + if @page.folder? && @page.gallery?
94 @images = @page.images 98 @images = @page.images
95 @images = @images.paginate(:per_page => per_page, :page => params[:npage]) unless params[:slideshow] 99 @images = @images.paginate(:per_page => per_page, :page => params[:npage]) unless params[:slideshow]
96 end 100 end
97 101
98 - @comments = @page.comments(true) 102 + @comments = @page.comments(true).as_thread
  103 + @comments_count = @page.comments.count
99 if params[:slideshow] 104 if params[:slideshow]
100 render :action => 'slideshow', :layout => 'slideshow' 105 render :action => 'slideshow', :layout => 'slideshow'
101 end 106 end
@@ -110,8 +115,9 @@ class ContentViewerController &lt; ApplicationController @@ -110,8 +115,9 @@ class ContentViewerController &lt; ApplicationController
110 if @comment.save 115 if @comment.save
111 @page.touch 116 @page.touch
112 @comment = nil # clear the comment form 117 @comment = nil # clear the comment form
  118 + redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view]
113 else 119 else
114 - @form_div = 'opened' 120 + @form_div = 'opened' if params[:comment][:reply_of_id].blank?
115 end 121 end
116 end 122 end
117 123
@@ -119,7 +125,7 @@ class ContentViewerController &lt; ApplicationController @@ -119,7 +125,7 @@ class ContentViewerController &lt; ApplicationController
119 @comment = @page.comments.find(params[:remove_comment]) 125 @comment = @page.comments.find(params[:remove_comment])
120 if (user == @comment.author || user == @page.profile || user.has_permission?(:moderate_comments, @page.profile)) 126 if (user == @comment.author || user == @page.profile || user.has_permission?(:moderate_comments, @page.profile))
121 @comment.destroy 127 @comment.destroy
122 - flash[:notice] = _('Comment succesfully deleted') 128 + session[:notice] = _('Comment succesfully deleted')
123 end 129 end
124 redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] 130 redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view]
125 end 131 end
@@ -127,4 +133,24 @@ class ContentViewerController &lt; ApplicationController @@ -127,4 +133,24 @@ class ContentViewerController &lt; ApplicationController
127 def per_page 133 def per_page
128 12 134 12
129 end 135 end
  136 +
  137 + def redirect_to_translation
  138 + locale = FastGettext.locale
  139 + if !@page.language.nil? && @page.language != locale
  140 + translations = [@page.native_translation] + @page.native_translation.translations
  141 + urls = translations.map{ |t| URI.parse(url_for(t.url)).path }
  142 + urls << URI.parse(url_for(profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }))).path
  143 + urls << URI.parse(url_for(profile.admin_url.merge(:controller => 'cms', :action => 'new'))).path
  144 + referer = URI.parse(url_for(request.referer)).path unless request.referer.blank?
  145 + unless urls.include?(referer)
  146 + translations.each do |translation|
  147 + if translation.language == locale
  148 + @page = translation
  149 + redirect_to :profile => @page.profile.identifier, :page => @page.explode_path
  150 + end
  151 + end
  152 + end
  153 + end
  154 + end
  155 +
130 end 156 end
app/controllers/public/enterprise_registration_controller.rb
@@ -16,9 +16,9 @@ class EnterpriseRegistrationController &lt; ApplicationController @@ -16,9 +16,9 @@ class EnterpriseRegistrationController &lt; ApplicationController
16 @create_enterprise.target = Profile.find(params[:create_enterprise][:target_id]) 16 @create_enterprise.target = Profile.find(params[:create_enterprise][:target_id])
17 end 17 end
18 elsif @validation == :admin || @validation == :none 18 elsif @validation == :admin || @validation == :none
19 - @create_enterprise.target = @create_enterprise.environment 19 + @create_enterprise.target = environment
20 end 20 end
21 - @create_enterprise.requestor = current_user.person 21 + @create_enterprise.requestor = user
22 the_action = 22 the_action =
23 if request.post? 23 if request.post?
24 if @create_enterprise.valid_before_selecting_target? 24 if @create_enterprise.valid_before_selecting_target?
app/controllers/public/events_controller.rb
@@ -13,7 +13,7 @@ class EventsController &lt; PublicController @@ -13,7 +13,7 @@ class EventsController &lt; PublicController
13 @events_of_the_day = profile.events.by_day(@selected_day) 13 @events_of_the_day = profile.events.by_day(@selected_day)
14 end 14 end
15 15
16 - events = profile.events.by_range(Event.first_day_of_month(date - 1.month)..Event.last_day_of_month(date + 1.month)) 16 + events = profile.events.by_range((date - 1.month).at_beginning_of_month..(date + 1.month).at_end_of_month)
17 17
18 @calendar = populate_calendar(date, events) 18 @calendar = populate_calendar(date, events)
19 @previous_calendar = populate_calendar(date - 1.month, events) 19 @previous_calendar = populate_calendar(date - 1.month, events)
app/controllers/public/home_controller.rb
@@ -7,8 +7,8 @@ class HomeController &lt; PublicController @@ -7,8 +7,8 @@ class HomeController &lt; PublicController
7 @news_cache_key = environment.portal_news_cache_key 7 @news_cache_key = environment.portal_news_cache_key
8 if !read_fragment(@news_cache_key) 8 if !read_fragment(@news_cache_key)
9 portal_community = environment.portal_community 9 portal_community = environment.portal_community
10 - @portal_news = portal_community.news(5)  
11 @highlighted_news = portal_community.news(2, true) 10 @highlighted_news = portal_community.news(2, true)
  11 + @portal_news = portal_community.news(7, true) - @highlighted_news
12 @area_news = environment.portal_folders 12 @area_news = environment.portal_folders
13 end 13 end
14 end 14 end
app/controllers/public/invite_controller.rb
@@ -2,40 +2,57 @@ class InviteController &lt; PublicController @@ -2,40 +2,57 @@ class InviteController &lt; PublicController
2 2
3 needs_profile 3 needs_profile
4 before_filter :login_required 4 before_filter :login_required
5 - before_filter :check_permissions_to_invite, :only => 'friends' 5 + before_filter :check_permissions_to_invite
6 6
7 - def friends  
8 - step = params[:step] 7 + def select_address_book
  8 + @import_from = params[:import_from] || "manual"
9 if request.post? 9 if request.post?
10 - if step == '1'  
11 - begin  
12 - @contacts = Invitation.get_contacts(params[:import_from], params[:login], params[:password])  
13 - rescue  
14 - @login = params[:login]  
15 - flash.now[:notice] = _('There was an error while looking for your contact list. Did you enter correct login and password?')  
16 - end  
17 - elsif step == '2'  
18 - manual_import_addresses = params[:manual_import_addresses]  
19 - webmail_import_addresses = params[:webmail_import_addresses]  
20 - contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses)  
21 - if !params[:mail_template].match(/<url>/)  
22 - flash.now[:notice] = _('&lt;url&gt; is needed in invitation mail.')  
23 - elsif !contacts_to_invite.empty?  
24 - Invitation.invite(current_user.person, contacts_to_invite, params[:mail_template], profile)  
25 - flash[:notice] = _('Your invitations have been sent.')  
26 - redirect_back_or_default :controller => 'profile' 10 + contact_list = ContactList.create
  11 + Delayed::Job.enqueue GetEmailContactsJob.new(@import_from, params[:login], params[:password], contact_list.id) if @import_from != 'manual'
  12 + redirect_to :action => 'select_friends', :contact_list => contact_list.id, :import_from => @import_from
  13 + end
  14 + end
  15 +
  16 + def select_friends
  17 + @contact_list = ContactList.find(params[:contact_list])
  18 + @mail_template = params[:mail_template] || environment.invitation_mail_template(profile)
  19 + @import_from = params[:import_from] || "manual"
  20 + if request.post?
  21 + manual_import_addresses = params[:manual_import_addresses]
  22 + webmail_import_addresses = params[:webmail_import_addresses]
  23 + contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses)
  24 + if !contacts_to_invite.empty?
  25 + Delayed::Job.enqueue InvitationJob.new(current_user.person.id, contacts_to_invite, params[:mail_template], profile.id, @contact_list.id, locale)
  26 + session[:notice] = _('Your invitations are being sent.')
  27 + if profile.person?
  28 + redirect_to :controller => 'profile', :action => 'friends'
27 else 29 else
28 - flash.now[:notice] = _('Please enter a valid email address.') 30 + redirect_to :controller => 'profile', :action => 'members'
29 end 31 end
30 - @contacts = params[:webmail_friends] ? params[:webmail_friends].map {|e| YAML.load(e)} : []  
31 - @manual_import_addresses = manual_import_addresses || ""  
32 - @webmail_import_addresses = webmail_import_addresses || [] 32 + return
  33 + else
  34 + session[:notice] = _('Please enter a valid email address.')
33 end 35 end
34 - else  
35 - store_location(request.referer) 36 + @manual_import_addresses = manual_import_addresses || ""
  37 + @webmail_import_addresses = webmail_import_addresses || []
36 end 38 end
37 - @import_from = params[:import_from] || "manual"  
38 - @mail_template = params[:mail_template] || environment.invitation_mail_template(profile) 39 + end
  40 +
  41 + def invitation_data
  42 + contact_list = ContactList.find(params[:contact_list])
  43 + render :text => contact_list.data.to_json, :layout => false, :content_type => "application/javascript"
  44 + end
  45 +
  46 + def add_contact_list
  47 + contact_list = ContactList.find(params[:contact_list])
  48 + contacts = contact_list.list
  49 + render :partial => 'invite/contact_list', :locals => {:contacts => contacts}
  50 + end
  51 +
  52 + def cancel_fetching_emails
  53 + contact_list = ContactList.find(params[:contact_list])
  54 + contact_list.destroy
  55 + redirect_to :action => 'select_address_book'
39 end 56 end
40 57
41 protected 58 protected
app/controllers/public/profile_controller.rb
1 class ProfileController < PublicController 1 class ProfileController < PublicController
2 2
3 needs_profile 3 needs_profile
4 - before_filter :check_access_to_profile, :except => [:join, :refuse_join, :refuse_for_now, :index]  
5 - before_filter :store_before_join, :only => [:join]  
6 - before_filter :login_required, :only => [:join, :refuse_join, :leave, :unblock] 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]
7 7
8 helper TagsHelper 8 helper TagsHelper
9 9
10 def index 10 def index
  11 + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page])
  12 + @wall_items = []
  13 + @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) : []
  14 + if logged_in? && current_person.follows?(@profile)
  15 + @network_activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) if @network_activities.empty?
  16 + @wall_items = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page])
  17 + end
11 @tags = profile.article_tags 18 @tags = profile.article_tags
12 unless profile.display_info_to?(user) 19 unless profile.display_info_to?(user)
13 profile.visible? ? private_profile : invisible_profile 20 profile.visible? ? private_profile : invisible_profile
@@ -73,20 +80,27 @@ class ProfileController &lt; PublicController @@ -73,20 +80,27 @@ class ProfileController &lt; PublicController
73 end 80 end
74 81
75 def join 82 def join
76 - @wizard = params[:wizard]  
77 - if request.post? && params[:confirmation]  
78 - profile.add_member(current_user.person)  
79 - flash[:notice] = _('%s administrator still needs to accept you as member.') % profile.name if profile.closed?  
80 - if @wizard  
81 - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true 83 + if !user.memberships.include?(profile)
  84 + profile.add_member(user)
  85 + if !profile.members.include?(user)
  86 + render :text => {:message => _('%s administrator still needs to accept you as member.') % profile.name}.to_json
82 else 87 else
83 - redirect_to_before_join 88 + render :text => {:message => _('You just became a member of %s.') % profile.name}.to_json
84 end 89 end
85 else 90 else
86 - store_location(request.referer)  
87 - if current_user.person.memberships.include?(profile)  
88 - flash[:notice] = _('You are already a member of "%s"') % profile.name  
89 - redirect_back_or_default profile.url 91 + render :text => {:message => _('You are already a member of %s.') % profile.name}.to_json
  92 + end
  93 + end
  94 +
  95 + def join_not_logged
  96 + if request.post?
  97 + profile.add_member(user)
  98 + session[:notice] = _('%s administrator still needs to accept you as member.') % profile.name if profile.closed?
  99 + redirect_to_before_join
  100 + else
  101 + if user.memberships.include?(profile)
  102 + session[:notice] = _('You are already a member of %s.') % profile.name
  103 + redirect_to profile.url
90 return 104 return
91 end 105 end
92 if request.xhr? 106 if request.xhr?
@@ -96,47 +110,109 @@ class ProfileController &lt; PublicController @@ -96,47 +110,109 @@ class ProfileController &lt; PublicController
96 end 110 end
97 111
98 def leave 112 def leave
99 - @wizard = params[:wizard]  
100 - if request.post? && params[:confirmation]  
101 - profile.remove_member(current_user.person)  
102 - if @wizard  
103 - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true 113 + if current_person.memberships.include?(profile)
  114 + if current_person.is_last_admin?(profile)
  115 + render :text => {:redirect_to => url_for({:controller => 'profile_members', :action => 'last_admin', :person => current_person.id})}.to_json
104 else 116 else
105 - redirect_back_or_default profile.url 117 + render :text => current_person.leave(profile, params[:reload])
106 end 118 end
107 else 119 else
108 - store_location(request.referer)  
109 - if request.xhr?  
110 - render :layout => false  
111 - end 120 + render :text => {:message => _('You are not a member of %s.') % profile.name}.to_json
112 end 121 end
113 end 122 end
114 123
115 - def refuse_join  
116 - p = current_user.person  
117 - p.refused_communities << profile  
118 - p.save  
119 - redirect_to profile.url 124 + def check_membership
  125 + unless logged_in?
  126 + render :text => ''
  127 + return
  128 + end
  129 + if user.memberships.include?(profile)
  130 + render :text => 'true'
  131 + else
  132 + render :text => 'false'
  133 + end
120 end 134 end
121 135
122 - def refuse_for_now  
123 - session[:no_asking] ||= []  
124 - session[:no_asking].shift if session[:no_asking].size >= 10  
125 - session[:no_asking] << profile.id  
126 - render :text => '', :layout => false 136 + def add
  137 + # FIXME this shouldn't be in Person model?
  138 + if !user.memberships.include?(profile)
  139 + AddFriend.create!(:person => user, :friend => profile)
  140 + render :text => _('%s still needs to accept being your friend.') % profile.name
  141 + else
  142 + render :text => _('You are already a friend of %s.') % profile.name
  143 + end
  144 + end
  145 +
  146 + def check_friendship
  147 + unless logged_in?
  148 + render :text => ''
  149 + return
  150 + end
  151 + if user == profile || user.already_request_friendship?(profile) || user.is_a_friend?(profile)
  152 + render :text => 'true'
  153 + else
  154 + render :text => 'false'
  155 + end
127 end 156 end
128 157
129 def unblock 158 def unblock
130 if current_user.person.is_admin?(profile.environment) 159 if current_user.person.is_admin?(profile.environment)
131 profile.unblock 160 profile.unblock
132 - flash[:notice] = _("You have unblocked %s successfully. ") % profile.name 161 + session[:notice] = _("You have unblocked %s successfully. ") % profile.name
133 redirect_to :controller => 'profile', :action => 'index' 162 redirect_to :controller => 'profile', :action => 'index'
134 else 163 else
135 - message = _('You are not allowed to unblock enterprises in this environment.') 164 + message = __('You are not allowed to unblock enterprises in this environment.')
136 render_access_denied(message) 165 render_access_denied(message)
137 end 166 end
138 end 167 end
139 168
  169 + def leave_scrap
  170 + sender = params[:sender_id].nil? ? current_user.person : Person.find(params[:sender_id])
  171 + receiver = params[:receiver_id].nil? ? @profile : Person.find(params[:receiver_id])
  172 + @scrap = Scrap.new(params[:scrap])
  173 + @scrap.sender= sender
  174 + @scrap.receiver= receiver
  175 + @tab_action = params[:tab_action]
  176 + @message = @scrap.save ? _("Message successfully sent.") : _("You can't leave an empty message.")
  177 + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) if params[:not_load_scraps].nil?
  178 + render :partial => 'leave_scrap'
  179 + end
  180 +
  181 + def view_more_scraps
  182 + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page])
  183 + render :partial => 'profile_scraps', :locals => {:scraps => @scraps}
  184 + end
  185 +
  186 + def view_more_activities
  187 + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page])
  188 + render :partial => 'profile_activities', :locals => {:activities => @activities}
  189 + end
  190 +
  191 + def view_more_network_activities
  192 + @activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page])
  193 + render :partial => 'profile_network_activities', :locals => {:network_activities => @activities}
  194 + end
  195 +
  196 + def remove_scrap
  197 + begin
  198 + scrap = current_user.person.scraps(params[:scrap_id])
  199 + scrap.destroy
  200 + render :text => _('Scrap successfully removed.')
  201 + rescue
  202 + render :text => _('You could not remove this scrap')
  203 + end
  204 + end
  205 +
  206 + def remove_activity
  207 + begin
  208 + activity = current_person.tracked_actions.find(params[:activity_id])
  209 + activity.destroy
  210 + render :text => _('Activity successfully removed.')
  211 + rescue
  212 + render :text => _('You could not remove this activity')
  213 + end
  214 + end
  215 +
140 protected 216 protected
141 217
142 def check_access_to_profile 218 def check_access_to_profile
@@ -146,7 +222,9 @@ class ProfileController &lt; PublicController @@ -146,7 +222,9 @@ class ProfileController &lt; PublicController
146 end 222 end
147 223
148 def store_before_join 224 def store_before_join
149 - session[:before_join] = request.referer unless logged_in? 225 + if session[:before_join].nil?
  226 + session[:before_join] = request.referer
  227 + end
150 end 228 end
151 229
152 def redirect_to_before_join 230 def redirect_to_before_join
@@ -155,14 +233,14 @@ class ProfileController &lt; PublicController @@ -155,14 +233,14 @@ class ProfileController &lt; PublicController
155 session[:before_join] = nil 233 session[:before_join] = nil
156 redirect_to back 234 redirect_to back
157 else 235 else
158 - redirect_back_or_default profile.url 236 + redirect_to profile.url
159 end 237 end
160 end 238 end
161 239
162 def private_profile 240 def private_profile
163 if profile.person? 241 if profile.person?
164 @action = :add_friend 242 @action = :add_friend
165 - @message = _("The content here is available to %s's friends only." % profile.short_name) 243 + @message = _("The content here is available to %s's friends only.") % profile.short_name
166 else 244 else
167 @action = :join 245 @action = :join
168 @message = _('The contents in this community is available to members only.') 246 @message = _('The contents in this community is available to members only.')
app/controllers/public/profile_search_controller.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +class ProfileSearchController < PublicController
  2 +
  3 + include SearchHelper
  4 +
  5 + needs_profile
  6 + before_filter :check_access_to_profile
  7 +
  8 + def index
  9 + @q = params[:q]
  10 + unless @q.blank?
  11 + @filtered_query = remove_stop_words(@q)
  12 + if params[:where] == 'environment'
  13 + redirect_to :controller => 'search', :query => @q
  14 + else
  15 + @results = profile.articles.published.find_by_contents(@filtered_query).paginate(:per_page => 10, :page => params[:page])
  16 + end
  17 + end
  18 + end
  19 +
  20 + protected
  21 +
  22 + def check_access_to_profile
  23 + unless profile.display_info_to?(user)
  24 + redirect_to :controller => 'profile', :action => 'index'
  25 + end
  26 + end
  27 +
  28 +end
app/controllers/public/search_controller.rb
@@ -102,7 +102,7 @@ class SearchController &lt; PublicController @@ -102,7 +102,7 @@ class SearchController &lt; PublicController
102 102
103 if month || year 103 if month || year
104 date = Date.new(year.to_i, month.to_i, 1) 104 date = Date.new(year.to_i, month.to_i, 1)
105 - result[:date_range] = (date - 1.month)..Event.last_day_of_month(date + 1.month) 105 + result[:date_range] = (date - 1.month)..(date + 1.month).at_end_of_month
106 end 106 end
107 107
108 result 108 result
@@ -148,8 +148,6 @@ class SearchController &lt; PublicController @@ -148,8 +148,6 @@ class SearchController &lt; PublicController
148 end 148 end
149 149
150 def index 150 def index
151 - @wizard = params[:wizard].blank? ? false : params[:wizard]  
152 - @step = 2  
153 @query = params[:query] || '' 151 @query = params[:query] || ''
154 @filtered_query = remove_stop_words(@query) 152 @filtered_query = remove_stop_words(@query)
155 @product_category = ProductCategory.find(params[:product_category]) if params[:product_category] 153 @product_category = ProductCategory.find(params[:product_category]) if params[:product_category]
@@ -174,20 +172,12 @@ class SearchController &lt; PublicController @@ -174,20 +172,12 @@ class SearchController &lt; PublicController
174 if respond_to?(specific_action) 172 if respond_to?(specific_action)
175 @asset_name = getterm(@names[@results.keys.first]) 173 @asset_name = getterm(@names[@results.keys.first])
176 send(specific_action) 174 send(specific_action)
177 - if @wizard  
178 - render :action => specific_action, :layout => 'wizard'  
179 - else  
180 - render :action => specific_action  
181 - end 175 + render :action => specific_action
182 return 176 return
183 end 177 end
184 end 178 end
185 179
186 - if @wizard  
187 - render :action => 'index', :layout => 'wizard'  
188 - else  
189 - render :action => 'index'  
190 - end 180 + render :action => 'index'
191 end 181 end
192 182
193 alias :assets :index 183 alias :assets :index
@@ -223,10 +213,10 @@ class SearchController &lt; PublicController @@ -223,10 +213,10 @@ class SearchController &lt; PublicController
223 end 213 end
224 214
225 def tag 215 def tag
226 - @tag = environment.tags.find_by_name(params[:tag]) 216 + @tag = params[:tag]
227 @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_env_#{environment.id.to_s}_page_#{params[:npage]}" 217 @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_env_#{environment.id.to_s}_page_#{params[:npage]}"
228 if is_cache_expired?(@tag_cache_key, true) 218 if is_cache_expired?(@tag_cache_key, true)
229 - @tagged = environment.articles.find_tagged_with(@tag.name).paginate(:per_page => 10, :page => params[:npage]) 219 + @tagged = environment.articles.find_tagged_with(@tag).paginate(:per_page => 10, :page => params[:npage])
230 end 220 end
231 end 221 end
232 222
app/helpers/account_helper.rb
1 module AccountHelper 1 module AccountHelper
2 2
3 -  
4 - def button_to_step(type, step, current_step, html_options = {})  
5 - if current_step == step  
6 - the_class = 'active'  
7 - if html_options.has_key?(:class)  
8 - html_options[:class] << " #{the_class}"  
9 - else  
10 - html_options[:class] = the_class  
11 - end  
12 - end  
13 - if step == 1  
14 - url = '#'  
15 - else  
16 - url = send('url_step_' + step.to_s)  
17 - end  
18 - button(type, step.to_s, url, html_options)  
19 - end  
20 -  
21 - def button_to_step_without_text(type, step, html_options = {})  
22 - url = 'url_step_' + step  
23 - button_without_text(type, step, send(url), html_options)  
24 - end  
25 -  
26 - def button_to_previous_step(step, html_options = {})  
27 - step = step - 1  
28 - if step > 1  
29 - button_to_step_without_text(:left, step.to_s, html_options)  
30 - end  
31 - end  
32 -  
33 - def button_to_next_step(step, html_options = {})  
34 - step = step + 1  
35 - if step < 4  
36 - button_to_step_without_text(:forward, step.to_s, html_options)  
37 - end  
38 - end  
39 -  
40 - def url_step_1  
41 - options = {:controller => 'account', :action => 'signup', :wizard => true}  
42 - Noosfero.url_options.merge(options)  
43 - end  
44 -  
45 - def url_step_2  
46 - options = {:controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true}  
47 - Noosfero.url_options.merge(options)  
48 - end  
49 -  
50 - def url_step_3  
51 - options = {:controller => 'friends', :action => 'invite', :profile => user.identifier, :wizard => true}  
52 - Noosfero.url_options.merge(options)  
53 - end  
54 -  
55 end 3 end
app/helpers/application_helper.rb
@@ -27,18 +27,7 @@ module ApplicationHelper @@ -27,18 +27,7 @@ module ApplicationHelper
27 include AccountHelper 27 include AccountHelper
28 28
29 def locale 29 def locale
30 - FastGettext.locale  
31 - end  
32 -  
33 - def load_web2_conf  
34 - if File.exists?( RAILS_ROOT + '/config/web2.0.yml')  
35 - YAML.load_file( RAILS_ROOT + '/config/web2.0.yml' )  
36 - else  
37 - {}  
38 - end  
39 - end  
40 - def web2_conf  
41 - @web_conf ||= load_web2_conf 30 + (@page && !@page.language.blank?) ? @page.language : FastGettext.locale
42 end 31 end
43 32
44 # Displays context help. You can pass the content of the help message as the 33 # Displays context help. You can pass the content of the help message as the
@@ -217,7 +206,8 @@ module ApplicationHelper @@ -217,7 +206,8 @@ module ApplicationHelper
217 if html_options.has_key?(:class) 206 if html_options.has_key?(:class)
218 the_class << ' ' << html_options[:class] 207 the_class << ' ' << html_options[:class]
219 end 208 end
220 - link_to('&nbsp;'+content_tag('span', label), url, html_options.merge(:class => the_class, :title => label)) 209 + the_title = html_options[:title] || label
  210 + link_to('&nbsp;'+content_tag('span', label), url, html_options.merge(:class => the_class, :title => the_title))
221 end 211 end
222 212
223 def button_to_function(type, label, js_code, html_options = {}, &block) 213 def button_to_function(type, label, js_code, html_options = {}, &block)
@@ -278,6 +268,19 @@ module ApplicationHelper @@ -278,6 +268,19 @@ module ApplicationHelper
278 end 268 end
279 end 269 end
280 270
  271 + def partial_for_task_class(klass, action)
  272 + if klass.nil?
  273 + raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?'
  274 + end
  275 +
  276 + name = "#{klass.name.underscore}_#{action.to_s}"
  277 + if File.exists?(File.join(RAILS_ROOT, 'app', 'views', params[:controller], "_#{name}.rhtml"))
  278 + name
  279 + else
  280 + partial_for_task_class(klass.superclass, action)
  281 + end
  282 + end
  283 +
281 def user 284 def user
282 @controller.send(:user) 285 @controller.send(:user)
283 end 286 end
@@ -339,7 +342,7 @@ module ApplicationHelper @@ -339,7 +342,7 @@ module ApplicationHelper
339 if ENV['RAILS_ENV'] == 'development' && environment.theme == 'random' 342 if ENV['RAILS_ENV'] == 'development' && environment.theme == 'random'
340 @random_theme ||= Dir.glob('public/designs/themes/*').map { |f| File.basename(f) }.rand 343 @random_theme ||= Dir.glob('public/designs/themes/*').map { |f| File.basename(f) }.rand
341 @random_theme 344 @random_theme
342 - elsif ENV['RAILS_ENV'] == 'development' && params[:theme] 345 + elsif ENV['RAILS_ENV'] == 'development' && params[:theme] && File.exists?(File.join(Rails.root, 'public/designs/themes', params[:theme]))
343 params[:theme] 346 params[:theme]
344 else 347 else
345 if profile && !profile.theme.nil? 348 if profile && !profile.theme.nil?
@@ -371,6 +374,24 @@ module ApplicationHelper @@ -371,6 +374,24 @@ module ApplicationHelper
371 nil 374 nil
372 end 375 end
373 376
  377 + def theme_favicon
  378 + return '/designs/themes/' + current_theme + '/favicon.ico' if profile.nil? || profile.theme.nil?
  379 + if File.exists?(File.join(RAILS_ROOT, 'public', theme_path, 'favicon.ico'))
  380 + '/designs/themes/' + profile.theme + '/favicon.ico'
  381 + else
  382 + favicon = profile.articles.find_by_path('favicon.ico')
  383 + if favicon
  384 + favicon.public_filename
  385 + else
  386 + '/designs/themes/' + environment.theme + '/favicon.ico'
  387 + end
  388 + end
  389 + end
  390 +
  391 + def theme_site_title
  392 + theme_include('site_title')
  393 + end
  394 +
374 def theme_header 395 def theme_header
375 theme_include('header') 396 theme_include('header')
376 end 397 end
@@ -395,6 +416,7 @@ module ApplicationHelper @@ -395,6 +416,7 @@ module ApplicationHelper
395 # 416 #
396 # If the profile has no image set yet, then a default image is used. 417 # If the profile has no image set yet, then a default image is used.
397 def profile_image(profile, size=:portrait, opt={}) 418 def profile_image(profile, size=:portrait, opt={})
  419 + return '' if profile.nil?
398 opt[:alt] ||= profile.name() 420 opt[:alt] ||= profile.name()
399 opt[:title] ||= '' 421 opt[:title] ||= ''
400 opt[:class] ||= '' 422 opt[:class] ||= ''
@@ -402,21 +424,33 @@ module ApplicationHelper @@ -402,21 +424,33 @@ module ApplicationHelper
402 image_tag(profile_icon(profile, size), opt ) 424 image_tag(profile_icon(profile, size), opt )
403 end 425 end
404 426
405 - def profile_icon( profile, size=:portrait ) 427 + def profile_icon( profile, size=:portrait, return_mimetype=false )
  428 + filename, mimetype = '', 'image/png'
406 if profile.image 429 if profile.image
407 - profile.image.public_filename( size ) 430 + filename = profile.image.public_filename( size )
  431 + mimetype = profile.image.content_type
408 else 432 else
409 - if profile.organization?  
410 - if profile.kind_of?(Community)  
411 - '/images/icons-app/users_size-'+ size.to_s() +'.png' 433 + icon =
  434 + if profile.organization?
  435 + if profile.kind_of?(Community)
  436 + '/images/icons-app/community-'+ size.to_s() +'.png'
  437 + else
  438 + '/images/icons-app/enterprise-'+ size.to_s() +'.png'
  439 + end
412 else 440 else
413 - '/images/icons-app/enterprise-default-pic-'+ size.to_s() +'.png' 441 + '/images/icons-app/person-'+ size.to_s() +'.png'
414 end 442 end
415 - else  
416 - '/images/icons-app/user_icon_size-'+ size.to_s() +'.png'  
417 - end 443 + filename = default_or_themed_icon(icon)
418 end 444 end
  445 + return_mimetype ? [filename, mimetype] : filename
  446 + end
419 447
  448 + def default_or_themed_icon(icon)
  449 + if File.exists?(File.join(Rails.root, 'public', theme_path, icon))
  450 + theme_path + icon
  451 + else
  452 + icon
  453 + end
420 end 454 end
421 455
422 def profile_sex_icon( profile ) 456 def profile_sex_icon( profile )
@@ -455,71 +489,69 @@ module ApplicationHelper @@ -455,71 +489,69 @@ module ApplicationHelper
455 end 489 end
456 end 490 end
457 491
458 - # displays a link to add the profile with its image (as generated by  
459 - # #profile_image) or only its name below.  
460 - def profile_add_link( profile, image=false, size=:portrait, tag='li')  
461 - the_class = profile.members.include?(user) ? 'profile_member' : ''  
462 - name = h(profile.short_name)  
463 - if image  
464 - display = content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +  
465 - content_tag( 'span', name, :class => 'org' ) +  
466 - profile_cat_icons( profile )  
467 - the_class << ' vcard'  
468 - else  
469 - display = content_tag( 'span', name, :class => 'org' ) 492 + def links_for_balloon(profile)
  493 + if environment.enabled?(:show_balloon_with_profile_links_when_clicked)
  494 + if profile.kind_of?(Person)
  495 + [
  496 + {_('Wall') => {:href => url_for(profile.public_profile_url)}},
  497 + {_('Friends') => {:href => url_for(:controller => :profile, :action => :friends, :profile => profile.identifier)}},
  498 + {_('Communities') => {:href => url_for(:controller => :profile, :action => :communities, :profile => profile.identifier)}},
  499 + {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}},
  500 + {_('Add') => {:href => url_for(profile.add_url), :class => 'add-friend', :style => 'display: none'}}
  501 + ]
  502 + elsif profile.kind_of?(Community)
  503 + [
  504 + {_('Wall') => {:href => url_for(profile.public_profile_url)}},
  505 + {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}},
  506 + {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}},
  507 + {_('Join') => {:href => url_for(profile.join_url), :class => 'join-community', :style => 'display: none'}},
  508 + {_('Leave') => {:href => url_for(profile.leave_url), :class => 'leave-community', :style => 'display: none'}},
  509 + {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}}
  510 + ]
  511 + elsif profile.kind_of?(Enterprise)
  512 + [
  513 + {_('Products') => {:href => catalog_path(profile.identifier)}},
  514 + {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}},
  515 + {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}},
  516 + {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}},
  517 + ]
  518 + else
  519 + []
  520 + end
470 end 521 end
471 - content_tag tag,  
472 - link_to_remote( display,  
473 - :update => 'search-results-and-pages',  
474 - :url => {:controller => 'account', :action => 'profile_details', :profile => profile.identifier},  
475 - :onclick => 'document.location.href = this.href', # work-arround for ie.  
476 - :class => 'profile_link url',  
477 - :help => _('Click on this icon to add <b>%s</b> to your network') % profile.name,  
478 - :title => profile.name ),  
479 - :class => the_class  
480 end 522 end
481 523
482 # displays a link to the profile homepage with its image (as generated by 524 # displays a link to the profile homepage with its image (as generated by
483 # #profile_image) and its name below it. 525 # #profile_image) and its name below it.
484 - def profile_image_link( profile, size=:portrait, tag='li' )  
485 - if profile.class == Person  
486 - name = profile.short_name  
487 - city = content_tag 'span', content_tag( 'span', profile.city, :class => 'locality' ), :class => 'adr' 526 + def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil )
  527 + name = profile.short_name
  528 + if profile.person?
  529 + url = url_for(profile.check_friendship_url)
  530 + trigger_class = 'person-trigger'
488 else 531 else
489 - name = profile.short_name  
490 city = '' 532 city = ''
  533 + url = url_for(profile.check_membership_url)
  534 + if profile.community?
  535 + trigger_class = 'community-trigger'
  536 + elsif profile.enterprise?
  537 + trigger_class = 'enterprise-trigger'
  538 + end
491 end 539 end
  540 + extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' )
  541 + links = links_for_balloon(profile)
492 content_tag tag, 542 content_tag tag,
  543 + (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") +
493 link_to( 544 link_to(
494 content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + 545 content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
495 content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + 546 content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
496 - city + profile_sex_icon( profile ) + profile_cat_icons( profile ), 547 + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ),
497 profile.url, 548 profile.url,
498 - :onclick => 'document.location.href = this.href', # work-arround for ie.  
499 :class => 'profile_link url', 549 :class => 'profile_link url',
500 :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, 550 :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
501 :title => profile.name ), 551 :title => profile.name ),
502 :class => 'vcard' 552 :class => 'vcard'
503 end 553 end
504 554
505 - # displays a link to the community homepage with its image (as generated by  
506 - # #profile_image) and its name and number of members beside it.  
507 - def community_image_link( profile, size=:portrait, tag='li' )  
508 - name = h(profile.name)  
509 - content_tag tag,  
510 - link_to(  
511 - content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +  
512 - content_tag( 'span', name, :class => 'org' ) +  
513 - content_tag( 'span', n_('1 member', '%s members', profile.members.count) % profile.members.count, :class => 'community-member-count' ),  
514 - profile.url,  
515 - :onclick => 'document.location.href = this.href', # work-arround for ie.  
516 - :class => 'profile_link url',  
517 - :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,  
518 - :title => profile.name ) +  
519 - '<br class="may-clear"/>',  
520 - :class => 'vcard'  
521 - end  
522 -  
523 def gravatar_url_for(email, options = {}) 555 def gravatar_url_for(email, options = {})
524 # Ta dando erro de roteamento 556 # Ta dando erro de roteamento
525 url_for( { :gravatar_id => Digest::MD5.hexdigest(email), 557 url_for( { :gravatar_id => Digest::MD5.hexdigest(email),
@@ -527,12 +559,12 @@ module ApplicationHelper @@ -527,12 +559,12 @@ module ApplicationHelper
527 :protocol => 'http://', 559 :protocol => 'http://',
528 :only_path => false, 560 :only_path => false,
529 :controller => 'avatar.php', 561 :controller => 'avatar.php',
530 - :d => web2_conf['gravatar'] ? web2_conf['gravatar']['default'] : nil 562 + :d => NOOSFERO_CONF['gravatar'] ? NOOSFERO_CONF['gravatar'] : nil
531 }.merge(options) ) 563 }.merge(options) )
532 end 564 end
533 565
534 def str_gravatar_url_for(email, options = {}) 566 def str_gravatar_url_for(email, options = {})
535 - default = web2_conf['gravatar'] ? web2_conf['gravatar']['default'] : nil 567 + default = NOOSFERO_CONF['gravatar'] ? NOOSFERO_CONF['gravatar'] : nil
536 url = 'http://www.gravatar.com/avatar.php?gravatar_id=' + 568 url = 'http://www.gravatar.com/avatar.php?gravatar_id=' +
537 Digest::MD5.hexdigest(email) 569 Digest::MD5.hexdigest(email)
538 { 570 {
@@ -591,7 +623,16 @@ module ApplicationHelper @@ -591,7 +623,16 @@ module ApplicationHelper
591 end 623 end
592 624
593 def select_folder(label, object, method, collection, html_options = {}, js_options = {}) 625 def select_folder(label, object, method, collection, html_options = {}, js_options = {})
594 - labelled_form_field(label, select(object, method, collection.map {|f| [ profile.identifier + '/' + f.full_name, f.id ] }, html_options.merge({:include_blank => "#{profile.identifier}"}), js_options)) 626 + root = profile ? profile.identifier : _("root")
  627 + labelled_form_field(label, select(object, method,
  628 + collection.map {|f| [ root + '/' + f.full_name, f.id ]},
  629 + {:include_blank => root}, html_options.merge(js_options)))
  630 + end
  631 +
  632 + def select_profile_folder(label, object, method, profile, html_options = {}, js_options = {})
  633 + labelled_form_field(label, select(object, method,
  634 + profile.folders.map {|f| [ profile.identifier + '/' + f.full_name, f.id ]},
  635 + {:include_blank => profile.identifier}, html_options.merge(js_options)))
595 end 636 end
596 637
597 def theme_option(opt = nil) 638 def theme_option(opt = nil)
@@ -851,6 +892,7 @@ module ApplicationHelper @@ -851,6 +892,7 @@ module ApplicationHelper
851 892
852 def helper_for_article(article) 893 def helper_for_article(article)
853 article_helper = ActionView::Base.new 894 article_helper = ActionView::Base.new
  895 + article_helper.controller = controller
854 article_helper.extend ArticleHelper 896 article_helper.extend ArticleHelper
855 begin 897 begin
856 class_name = article.class.name + 'Helper' 898 class_name = article.class.name + 'Helper'
@@ -877,37 +919,27 @@ module ApplicationHelper @@ -877,37 +919,27 @@ module ApplicationHelper
877 end 919 end
878 end 920 end
879 921
880 - def ask_to_join?  
881 - return if !environment.enabled?(:join_community_popup)  
882 - return if params[:action] == 'join'  
883 - return unless profile && profile.kind_of?(Community)  
884 - if (session[:no_asking] && session[:no_asking].include?(profile.id))  
885 - return false  
886 - end  
887 - if logged_in?  
888 - user.ask_to_join?(profile)  
889 - else  
890 - true  
891 - end  
892 - end  
893 -  
894 def icon_theme_stylesheet_path 922 def icon_theme_stylesheet_path
895 - theme_path = "/designs/icons/#{environment.icon_theme}/style.css"  
896 - if File.exists?(File.join(RAILS_ROOT, 'public', theme_path))  
897 - theme_path  
898 - else  
899 - '/designs/icons/default/style.css' 923 + icon_themes = []
  924 + theme_icon_themes = theme_option(:icon_theme) || []
  925 + for icon_theme in theme_icon_themes do
  926 + theme_path = "/designs/icons/#{icon_theme}/style.css"
  927 + if File.exists?(File.join(RAILS_ROOT, 'public', theme_path))
  928 + icon_themes << theme_path
  929 + end
900 end 930 end
  931 + icon_themes
901 end 932 end
902 933
903 def page_title 934 def page_title
904 - (@page ? @page.name + ' - ' : '') + 935 + (@page ? @page.title + ' - ' : '') +
905 (profile ? profile.short_name + ' - ' : '') + 936 (profile ? profile.short_name + ' - ' : '') +
906 (@topic ? @topic.title + ' - ' : '') + 937 (@topic ? @topic.title + ' - ' : '') +
907 (@section ? @section.title + ' - ' : '') + 938 (@section ? @section.title + ' - ' : '') +
908 (@toc ? _('Online Manual') + ' - ' : '') + 939 (@toc ? _('Online Manual') + ' - ' : '') +
  940 + (@controller.controller_name == 'chat' ? _('Chat') + ' - ' : '') +
909 environment.name + 941 environment.name +
910 - (@category ? "&rarr; #{@category.full_name}" : '') 942 + (@category ? " - #{@category.full_name}" : '')
911 end 943 end
912 944
913 def noosfero_javascript 945 def noosfero_javascript
@@ -946,7 +978,7 @@ module ApplicationHelper @@ -946,7 +978,7 @@ module ApplicationHelper
946 end 978 end
947 979
948 def article_to_html(article, options = {}) 980 def article_to_html(article, options = {})
949 - options.merge(:page => params[:npage]) 981 + options.merge!(:page => params[:npage])
950 content = article.to_html(options) 982 content = article.to_html(options)
951 return self.instance_eval(&content) if content.kind_of?(Proc) 983 return self.instance_eval(&content) if content.kind_of?(Proc)
952 content 984 content
@@ -960,4 +992,221 @@ module ApplicationHelper @@ -960,4 +992,221 @@ module ApplicationHelper
960 text_field_tag(name, value, options.merge(:class => 'colorpicker_field')) 992 text_field_tag(name, value, options.merge(:class => 'colorpicker_field'))
961 end 993 end
962 994
  995 + def ui_icon(icon_class, extra_class = '')
  996 + "<span class='ui-icon #{icon_class} #{extra_class}' style='float:left; margin-right:7px;'></span>"
  997 + end
  998 +
  999 + def ui_button(label, url, html_options = {})
  1000 + link_to(label, url, html_options.merge(:class => 'ui_button fg-button'))
  1001 + end
  1002 +
  1003 + def ui_button_to_remote(label, options, html_options = {})
  1004 + link_to_remote(label, options, html_options.merge(:class => 'ui_button fg-button'))
  1005 + end
  1006 +
  1007 + def jquery_theme
  1008 + theme_option(:jquery_theme) || 'smoothness_mod'
  1009 + end
  1010 +
  1011 + def jquery_ui_theme_stylesheet_path
  1012 + 'jquery.ui/' + jquery_theme + '/jquery-ui-1.8.2.custom'
  1013 + end
  1014 +
  1015 + def ui_error(message)
  1016 + content_tag('div', ui_icon('ui-icon-alert') + message, :class => 'alert fg-state-error ui-state-error')
  1017 + end
  1018 +
  1019 + def ui_highlight(message)
  1020 + content_tag('div', ui_icon('ui-icon-info') + message, :class => 'alert fg-state-highlight ui-state-highlight')
  1021 + end
  1022 +
  1023 + def float_to_currency(value)
  1024 + number_to_currency(value, :unit => environment.currency_unit, :separator => environment.currency_separator, :delimiter => environment.currency_delimiter, :format => "%u %n")
  1025 + end
  1026 +
  1027 + def collapsed_item_icon
  1028 + "<span class='ui-icon ui-icon-circlesmall-plus' style='float:left;'></span>"
  1029 + end
  1030 + def expanded_item_icon
  1031 + "<span class='ui-icon ui-icon-circlesmall-minus' style='float:left;'></span>"
  1032 + end
  1033 + def leaf_item_icon
  1034 + "<span class='ui-icon ui-icon-arrow-1-e' style='float:left;'></span>"
  1035 + end
  1036 +
  1037 + def display_category_menu(block, categories, root = true)
  1038 + categories = categories.sort{|x,y| x.name <=> y.name}
  1039 + return "" if categories.blank?
  1040 + content_tag(:ul,
  1041 + categories.map do |category|
  1042 + category_path = category.kind_of?(ProductCategory) ? {:controller => 'search', :action => 'assets', :asset => 'products', :product_category => category.id} : { :controller => 'search', :action => 'category_index', :category_path => category.explode_path }
  1043 + category.display_in_menu? ?
  1044 + content_tag(:li,
  1045 + ( !category.is_leaf_displayable_in_menu? ? content_tag(:a, collapsed_item_icon, :href => "#", :id => "block_#{block.id}_category_#{category.id}", :class => 'category-link-expand ' + (root ? 'category-root' : 'category-no-root'), :onclick => "expandCategory(#{block.id}, #{category.id}); return false", :style => 'display: none') : leaf_item_icon) +
  1046 + link_to(content_tag(:span, category.name, :class => 'category-name'), category_path, :class => ("category-leaf" if category.is_leaf_displayable_in_menu?)) +
  1047 + content_tag(:div, display_category_menu(block, category.children, false), :id => "block_#{block.id}_category_content_#{category.id}", :class => 'child-category')
  1048 + ) : ''
  1049 + end
  1050 + ) +
  1051 + content_tag(:p) +
  1052 + (root ? javascript_tag("
  1053 + jQuery('.child-category').hide();
  1054 + jQuery('.category-link-expand').show();
  1055 + var expanded_icon = \"#{ expanded_item_icon }\";
  1056 + var collapsed_icon = \"#{ collapsed_item_icon }\";
  1057 + var category_expanded = { 'block' : 0, 'category' : 0 };
  1058 + ") : '')
  1059 + end
  1060 +
  1061 + def browse_people_menu
  1062 + links = [
  1063 + {s_('people|More Recent') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_recent'})}},
  1064 + {s_('people|More Active') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_active'})}},
  1065 + {s_('people|More Popular') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_popular'})}}
  1066 + ]
  1067 + if logged_in?
  1068 + links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})})
  1069 + links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})})
  1070 + end
  1071 +
  1072 + link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "browse", :action => 'people'}, :id => 'submenu-people') +
  1073 + link_to(content_tag(:span, _('People Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-people-trigger')
  1074 + end
  1075 +
  1076 + def browse_communities_menu
  1077 + links = [
  1078 + {s_('communities|More Recent') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_recent'})}},
  1079 + {s_('communities|More Active') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_active'})}},
  1080 + {s_('communities|More Popular') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_popular'})}}
  1081 + ]
  1082 + if logged_in?
  1083 + links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})})
  1084 + links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})})
  1085 + end
  1086 +
  1087 + link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "browse", :action => 'communities'}, :id => 'submenu-communities') +
  1088 + link_to(content_tag(:span, _('Communities Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-communities-trigger')
  1089 + end
  1090 +
  1091 + def pagination_links(collection, options={})
  1092 + options = {:prev_label => '&laquo; ' + _('Previous'), :next_label => _('Next') + ' &raquo;'}.merge(options)
  1093 + will_paginate(collection, options)
  1094 + end
  1095 +
  1096 + def render_environment_features(folder)
  1097 + result = ''
  1098 + environment.enabled_features.keys.each do |feature|
  1099 + file = File.join(@controller.view_paths, 'shared', folder.to_s, "#{feature}.rhtml")
  1100 + if File.exists?(file)
  1101 + result << render(:file => file, :use_full_path => false)
  1102 + end
  1103 + end
  1104 + result
  1105 + end
  1106 +
  1107 + def usermenu_logged_in
  1108 + pending_tasks_count = ''
  1109 + if user && user.all_pending_tasks.count > 0
  1110 + pending_tasks_count = link_to(user.all_pending_tasks.count.to_s, '/myprofile/{login}/tasks', :id => 'pending-tasks-count', :title => _("Manage your pending tasks"))
  1111 + end
  1112 +
  1113 + (_('Welcome, %s') % link_to('<i></i><strong>{login}</strong>', '/{login}', :id => "homepage-link", :title => _('Go to your homepage'))) +
  1114 + render_environment_features(:usermenu) +
  1115 + 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') +
  1116 + link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', '/myprofile/{login}', :id => "controlpanel", :title => _("Configure your personal account and content")) +
  1117 + pending_tasks_count +
  1118 + link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
  1119 + end
  1120 +
  1121 + def limited_text_area(object_name, method, limit, text_area_id, options = {})
  1122 + content_tag(:div, [
  1123 + text_area(object_name, method, { :id => text_area_id, :onkeyup => "limited_text_area('#{text_area_id}', #{limit})" }.merge(options)),
  1124 + content_tag(:p, content_tag(:span, limit) + ' ' + _(' characters left'), :id => text_area_id + '_left'),
  1125 + content_tag(:p, _('Limit of characters reached'), :id => text_area_id + '_limit', :style => 'display: none')
  1126 + ], :class => 'limited-text-area')
  1127 + end
  1128 +
  1129 + def pluralize_without_count(count, singular, plural = nil)
  1130 + count == 1 ? singular : (plural || singular.pluralize)
  1131 + end
  1132 +
  1133 + def unique_with_count(list, connector = 'for')
  1134 + list.sort.inject(Hash.new(0)){|h,i| h[i] += 1; h }.collect{ |x, n| [n, connector, x].join(" ") }.sort
  1135 + end
  1136 +
  1137 + #FIXME Use time_ago_in_words instead of this method if you're using Rails 2.2+
  1138 + def time_ago_as_sentence(from_time, include_seconds = false)
  1139 + to_time = Time.now
  1140 + from_time = from_time.to_time if from_time.respond_to?(:to_time)
  1141 + to_time = to_time.to_time if to_time.respond_to?(:to_time)
  1142 + distance_in_minutes = (((to_time - from_time).abs)/60).round
  1143 + distance_in_seconds = ((to_time - from_time).abs).round
  1144 + case distance_in_minutes
  1145 + when 0..1
  1146 + return (distance_in_minutes == 0) ? _('less than a minute') : _('1 minute') unless include_seconds
  1147 + case distance_in_seconds
  1148 + when 0..4 then _('less than 5 seconds')
  1149 + when 5..9 then _('less than 10 seconds')
  1150 + when 10..19 then _('less than 20 seconds')
  1151 + when 20..39 then _('half a minute')
  1152 + when 40..59 then _('less than a minute')
  1153 + else _('1 minute')
  1154 + end
  1155 +
  1156 + when 2..44 then _('%{distance} minutes') % { :distance => distance_in_minutes }
  1157 + when 45..89 then _('about 1 hour')
  1158 + when 90..1439 then _('about %{distance} hours') % { :distance => (distance_in_minutes.to_f / 60.0).round }
  1159 + when 1440..2879 then _('1 day')
  1160 + when 2880..43199 then _('%{distance} days') % { :distance => (distance_in_minutes / 1440).round }
  1161 + when 43200..86399 then _('about 1 month')
  1162 + when 86400..525599 then _('%{distance} months') % { :distance => (distance_in_minutes / 43200).round }
  1163 + when 525600..1051199 then _('about 1 year')
  1164 + else _('over %{distance} years') % { :distance => (distance_in_minutes / 525600).round }
  1165 + end
  1166 + end
  1167 +
  1168 + def comment_balloon(options = {}, &block)
  1169 + wrapper = content_tag(:div, capture(&block), :class => 'comment-balloon-content')
  1170 + (1..8).to_a.reverse.each { |i| wrapper = content_tag(:div, wrapper, :class => "comment-wrapper-#{i}") }
  1171 + classes = options.delete(:class) || options.delete("class") || ''
  1172 + concat(content_tag('div', wrapper + tag('br', :style => 'clear: both;'), { :class => 'comment-balloon ' + classes.to_s }.merge(options)), block.binding)
  1173 + end
  1174 +
  1175 + def display_source_info(page)
  1176 + if !page.source.blank?
  1177 + source_url = link_to(page.source_name.blank? ? page.source : page.source_name, page.source)
  1178 + elsif page.reference_article
  1179 + source_url = link_to(page.reference_article.profile.name, page.reference_article.url)
  1180 + end
  1181 + content_tag(:div, _('Source: %s') % source_url, :id => 'article-source') unless source_url.nil?
  1182 + end
  1183 +
  1184 + def task_information(task)
  1185 + values = {}
  1186 + values.merge!({:requestor => link_to(task.requestor.name, task.requestor.public_profile_url)}) if task.requestor
  1187 + values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject
  1188 + values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject
  1189 + values.merge!(task.information[:variables]) if task.information[:variables]
  1190 + task.information[:message] % values
  1191 + end
  1192 +
  1193 + def add_zoom_to_images
  1194 + if environment.enabled?(:show_zoom_button_on_article_images)
  1195 + stylesheet_link_tag('fancybox') +
  1196 + javascript_include_tag('jquery.fancybox-1.3.4.pack') +
  1197 + javascript_tag("jQuery(function($) {
  1198 + $('#article .article-body img').each( function(index) {
  1199 + var original = original_image_dimensions($(this).attr('src'));
  1200 + if ($(this).width() < original['width'] || $(this).height() < original['height']) {
  1201 + $(this).wrap('<div class=\"zoomable-image\" />');
  1202 + $(this).parent('.zoomable-image').attr('style', $(this).attr('style'));
  1203 + $(this).attr('style', '');
  1204 + $(this).after(\'<a href=\"' + $(this).attr('src') + '\" class=\"zoomify-image\"><span class=\"zoomify-text\">%s</span></a>');
  1205 + }
  1206 + });
  1207 + $('.zoomify-image').fancybox();
  1208 + });" % _('Zoom in'))
  1209 + end
  1210 + end
  1211 +
963 end 1212 end
app/helpers/article_helper.rb
@@ -8,12 +8,13 @@ module ArticleHelper @@ -8,12 +8,13 @@ module ArticleHelper
8 'div', 8 'div',
9 check_box(:article, :published) + 9 check_box(:article, :published) +
10 content_tag('label', _('This article must be published (visible to other people)'), :for => 'article_published') 10 content_tag('label', _('This article must be published (visible to other people)'), :for => 'article_published')
11 - ) + 11 + ) + (article.parent && article.parent.forum? && controller.action_name == 'new' ?
  12 + hidden_field_tag('article[accept_comments]', 1) :
12 content_tag( 13 content_tag(
13 'div', 14 'div',
14 check_box(:article, :accept_comments) + 15 check_box(:article, :accept_comments) +
15 - content_tag('label', _('I want to receive comments about this article'), :for => 'article_accept_comments')  
16 - ) + 16 + content_tag('label', (article.parent && article.parent.forum? ? _('This topic is opened for replies') : _('I want to receive comments about this article')), :for => 'article_accept_comments')
  17 + )) +
17 content_tag( 18 content_tag(
18 'div', 19 'div',
19 check_box(:article, :notify_comments) + 20 check_box(:article, :notify_comments) +
app/helpers/assets_helper.rb
@@ -9,7 +9,7 @@ module AssetsHelper @@ -9,7 +9,7 @@ module AssetsHelper
9 [ options.merge(:asset => 'products'), "icon-menu-product", _('Products') ], 9 [ options.merge(:asset => 'products'), "icon-menu-product", _('Products') ],
10 [ options.merge(:asset => 'enterprises'), "icon-menu-enterprise", __('Enterprises') ], 10 [ options.merge(:asset => 'enterprises'), "icon-menu-enterprise", __('Enterprises') ],
11 [ options.merge(:asset => 'communities'), "icon-menu-community", __('Communities') ], 11 [ options.merge(:asset => 'communities'), "icon-menu-community", __('Communities') ],
12 - [ options.merge(:asset => 'events'), "icon-menu-events", __('Events') ], 12 + [ options.merge(:asset => 'events'), "icon-event", __('Events') ],
13 13
14 ].select do |target, css_class, name| 14 ].select do |target, css_class, name|
15 !environment.enabled?('disable_asset_' + target[:asset]) 15 !environment.enabled?('disable_asset_' + target[:asset])
app/helpers/block_helper.rb
@@ -3,7 +3,7 @@ module BlockHelper @@ -3,7 +3,7 @@ module BlockHelper
3 def block_title(title) 3 def block_title(title)
4 tag_class = 'block-title' 4 tag_class = 'block-title'
5 tag_class += ' empty' if title.empty? 5 tag_class += ' empty' if title.empty?
6 - content_tag 'h3', title, :class => tag_class 6 + content_tag 'h3', content_tag('span', title), :class => tag_class
7 end 7 end
8 8
9 end 9 end
app/helpers/blog_helper.rb
@@ -11,7 +11,7 @@ module BlogHelper @@ -11,7 +11,7 @@ module BlogHelper
11 end 11 end
12 12
13 def cms_label_for_edit 13 def cms_label_for_edit
14 - _('Edit blog') 14 + _('Configure blog')
15 end 15 end
16 16
17 def list_posts(articles, format = 'full') 17 def list_posts(articles, format = 'full')
@@ -48,7 +48,7 @@ module BlogHelper @@ -48,7 +48,7 @@ module BlogHelper
48 48
49 def display_short_format(article) 49 def display_short_format(article)
50 html = content_tag('div', 50 html = content_tag('div',
51 - article.first_paragraph + 51 + article.lead +
52 content_tag('div', 52 content_tag('div',
53 link_to_comments(article) + 53 link_to_comments(article) +
54 link_to( _('Read more'), article.url), 54 link_to( _('Read more'), article.url),
app/helpers/boxes_helper.rb
@@ -65,7 +65,7 @@ module BoxesHelper @@ -65,7 +65,7 @@ module BoxesHelper
65 end 65 end
66 66
67 def display_box_content(box, main_content) 67 def display_box_content(box, main_content)
68 - context = { :article => @page } 68 + context = { :article => @page, :request_path => request.path, :locale => locale }
69 box_decorator.select_blocks(box.blocks, context).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box) 69 box_decorator.select_blocks(box.blocks, context).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box)
70 end 70 end
71 71
app/helpers/catalog_helper.rb
1 module CatalogHelper 1 module CatalogHelper
2 2
3 include DisplayHelper 3 include DisplayHelper
  4 +include ManageProductsHelper
4 5
5 def display_products_list(profile, products) 6 def display_products_list(profile, products)
6 data = '' 7 data = ''
@@ -19,7 +20,7 @@ include DisplayHelper @@ -19,7 +20,7 @@ include DisplayHelper
19 content_tag('h1', _('Products/Services')) + content_tag('ul', data, :id => 'product_list') 20 content_tag('h1', _('Products/Services')) + content_tag('ul', data, :id => 'product_list')
20 end 21 end
21 22
22 -private 23 + private
23 24
24 def product_category_name(profile, product_category) 25 def product_category_name(profile, product_category)
25 if profile.enabled? 26 if profile.enabled?
app/helpers/categories_helper.rb
@@ -34,8 +34,7 @@ module CategoriesHelper @@ -34,8 +34,7 @@ module CategoriesHelper
34 34
35 def select_category_type(field) 35 def select_category_type(field)
36 value = params[field] 36 value = params[field]
37 - types = TYPES.select { |title,typename| environment.category_types.include?(typename) }  
38 - labelled_form_field(_('Type of category'), select_tag('type', options_for_select(types, value))) 37 + labelled_form_field(_('Type of category'), select_tag('type', options_for_select(TYPES, value)))
39 end 38 end
40 39
41 end 40 end
app/helpers/chat_helper.rb 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +module ChatHelper
  2 +
  3 + def user_status_menu(icon_class, status)
  4 + links = [
  5 + ['icon-menu-online', _('Online'), 'chat-connect'],
  6 + ['icon-menu-busy', _('Busy'), 'chat-busy'],
  7 + ['icon-menu-offline', _('Sign out of chat'), 'chat-disconnect'],
  8 + ]
  9 + content_tag('span',
  10 + link_to(content_tag('span', status) + ui_icon('ui-icon-triangle-1-s'),
  11 + '#',
  12 + :onclick => 'toggleMenu(this); return false',
  13 + :class => icon_class + ' simplemenu-trigger'
  14 + ) +
  15 + content_tag('ul',
  16 + links.map{|link| content_tag('li', link_to(link[1], '#', :class => link[0], :id => link[2], 'data-jid' => user.jid), :class => 'simplemenu-item') }.join("\n"),
  17 + :style => 'display: none; z-index: 100',
  18 + :class => 'simplemenu-submenu'
  19 + ),
  20 + :class => 'user-status'
  21 + )
  22 + end
  23 +
  24 +end
app/helpers/cms_helper.rb
@@ -27,4 +27,34 @@ module CmsHelper @@ -27,4 +27,34 @@ module CmsHelper
27 article_helper.custom_options_for_article(article) 27 article_helper.custom_options_for_article(article)
28 end 28 end
29 29
  30 + def link_to_article(article)
  31 + article_name = short_filename(article.title, 30)
  32 + if article.folder?
  33 + link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article)
  34 + else
  35 + if article.image?
  36 + image_tag(icon_for_article(article)) + link_to(article_name, article.url)
  37 + else
  38 + link_to article_name, article.url, :class => icon_for_article(article)
  39 + end
  40 + end
  41 + end
  42 +
  43 + def display_spread_button(profile, article)
  44 + if profile.person?
  45 + button_without_text :spread, _('Spread this'), :action => 'publish', :id => article.id
  46 + elsif profile.community? && environment.portal_community
  47 + button_without_text :spread, _('Spread this'), :action => 'publish_on_portal_community', :id => article.id
  48 + end
  49 + end
  50 +
  51 + def display_delete_button(article)
  52 + confirm_message = if article.folder?
  53 + _('Are you sure that you want to remove this folder? Note that all the items inside it will also be removed!')
  54 + else
  55 + _('Are you sure that you want to remove this item?')
  56 + end
  57 +
  58 + button_without_text :delete, _('Delete'), { :action => 'destroy', :id => article.id }, :method => :post, :confirm => confirm_message
  59 + end
30 end 60 end
app/helpers/content_viewer_helper.rb
1 module ContentViewerHelper 1 module ContentViewerHelper
2 2
3 include BlogHelper 3 include BlogHelper
  4 + include ForumHelper
4 5
5 def number_of_comments(article) 6 def number_of_comments(article)
6 n = article.comments.size 7 n = article.comments.size
@@ -20,7 +21,7 @@ module ContentViewerHelper @@ -20,7 +21,7 @@ module ContentViewerHelper
20 title = content_tag('h1', link_to(article.name, article.url), :class => 'title') 21 title = content_tag('h1', link_to(article.name, article.url), :class => 'title')
21 end 22 end
22 comments = args[:no_comments] ? '' : (("- %s") % link_to_comments(article)) 23 comments = args[:no_comments] ? '' : (("- %s") % link_to_comments(article))
23 - 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') 24 + 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')
24 end 25 end
25 title 26 title
26 end 27 end
@@ -30,8 +31,20 @@ module ContentViewerHelper @@ -30,8 +31,20 @@ module ContentViewerHelper
30 end 31 end
31 32
32 def image_label(image) 33 def image_label(image)
33 - text = image.title || image.abstract 34 + text = image.abstract || image.title
34 text && (text.first(40) + (text.size > 40 ? '…' : '')) 35 text && (text.first(40) + (text.size > 40 ? '…' : ''))
35 end 36 end
36 37
  38 + def article_translations(article)
  39 + unless article.native_translation.translations.empty?
  40 + links = (article.native_translation.translations + [article.native_translation]).map do |translation|
  41 + { Noosfero.locales[translation.language] => { :href => url_for(translation.url) } }
  42 + end
  43 + content_tag(:div, link_to(_('Translations'), '#',
  44 + :onclick => "toggleSubmenu(this, '#{_('Translations')}', #{links.to_json}); return false",
  45 + :class => 'article-translations-menu simplemenu-trigger up'),
  46 + :class => 'article-translations')
  47 + end
  48 + end
  49 +
37 end 50 end
app/helpers/dates_helper.rb
@@ -36,7 +36,7 @@ module DatesHelper @@ -36,7 +36,7 @@ module DatesHelper
36 # formats a datetime for displaying. 36 # formats a datetime for displaying.
37 def show_time(time) 37 def show_time(time)
38 if time 38 if time
39 - _('%{day} %{month} %{year}, %{hour}:%{minutes}') % { :year => time.year, :month => month_name(time.month), :day => time.day, :hour => time.hour, :minutes => time.min } 39 + _('%{day} %{month} %{year}, %{hour}:%{minutes}') % { :year => time.year, :month => month_name(time.month), :day => time.day, :hour => time.hour, :minutes => time.strftime("%M") }
40 else 40 else
41 '' 41 ''
42 end 42 end
@@ -46,7 +46,7 @@ module DatesHelper @@ -46,7 +46,7 @@ module DatesHelper
46 if (date1 == date2) || (date2.nil?) 46 if (date1 == date2) || (date2.nil?)
47 show_date(date1) 47 show_date(date1)
48 else 48 else
49 - _('from %s to %s') % [show_date(date1), show_date(date2)] 49 + _('from %{date1} to %{date2}') % {:date1 => show_date(date1), :date2 => show_date(date2)}
50 end 50 end
51 end 51 end
52 52
app/helpers/display_helper.rb
@@ -2,12 +2,16 @@ module DisplayHelper @@ -2,12 +2,16 @@ module DisplayHelper
2 2
3 def link_to_product(product, opts={}) 3 def link_to_product(product, opts={})
4 return _('No product') unless product 4 return _('No product') unless product
5 - target = product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'catalog', :action => 'show', :id => product) : product.enterprise.url 5 + target = product_path(product)
6 link_to content_tag( 'span', product.name ), 6 link_to content_tag( 'span', product.name ),
7 target, 7 target,
8 opts 8 opts
9 end 9 end
10 10
  11 + def product_path(product)
  12 + product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url
  13 + end
  14 +
11 def link_to_category(category, full = true) 15 def link_to_category(category, full = true)
12 return _('Uncategorized product') unless category 16 return _('Uncategorized product') unless category
13 name = full ? category.full_name(' &rarr; ') : category.name 17 name = full ? category.full_name(' &rarr; ') : category.name
@@ -27,9 +31,10 @@ module DisplayHelper @@ -27,9 +31,10 @@ module DisplayHelper
27 gsub( /\n\s*\n/, ' <p/> ' ). 31 gsub( /\n\s*\n/, ' <p/> ' ).
28 gsub( /\n/, ' <br/> ' ). 32 gsub( /\n/, ' <br/> ' ).
29 gsub( /(^|\s)(www\.[^\s])/, '\1http://\2' ). 33 gsub( /(^|\s)(www\.[^\s])/, '\1http://\2' ).
30 - gsub( /(https?:\/\/([^\s]+))/,  
31 - '<a href="\1" target="_blank" rel="nofolow" onclick="return confirm(\'' +  
32 - escape_javascript( _('Are you sure you want to visit this web site?') ) +  
33 - '\n\n\'+this.href)">\2</a>' ) 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?')))
  38 + end
34 end 39 end
35 end 40 end
app/helpers/events_helper.rb
@@ -6,7 +6,7 @@ module EventsHelper @@ -6,7 +6,7 @@ module EventsHelper
6 content_tag('h2', title) + 6 content_tag('h2', title) +
7 content_tag('div', 7 content_tag('div',
8 (events.any? ? 8 (events.any? ?
9 - content_tag('table', events.select { |item| item.public? }.map {|item| display_event_in_listing(item)}.join('')) : 9 + content_tag('table', events.select { |item| item.display_to?(user) }.map {|item| display_event_in_listing(item)}.join('')) :
10 content_tag('em', _('No events for this date'), :class => 'no-events') 10 content_tag('em', _('No events for this date'), :class => 'no-events')
11 ), :id => 'agenda-items' 11 ), :id => 'agenda-items'
12 ) 12 )
@@ -15,7 +15,7 @@ module EventsHelper @@ -15,7 +15,7 @@ module EventsHelper
15 def display_event_in_listing(article) 15 def display_event_in_listing(article)
16 content_tag( 16 content_tag(
17 'tr', 17 'tr',
18 - content_tag('td', link_to(image_tag(icon_for_article(article)) + article.name, article.url)), 18 + content_tag('td', link_to(article.name, article.url, :class => icon_for_article(article))),
19 :class => 'agenda-item' 19 :class => 'agenda-item'
20 ) 20 )
21 end 21 end
@@ -26,7 +26,7 @@ module EventsHelper @@ -26,7 +26,7 @@ module EventsHelper
26 # the day itself 26 # the day itself
27 date, 27 date,
28 # is there any events in this date? 28 # is there any events in this date?
29 - events.any? do |event| 29 + events.select {|event| event.display_to?(user)}.any? do |event|
30 event.date_range.include?(date) 30 event.date_range.include?(date)
31 end, 31 end,
32 # is this date in the current month? 32 # is this date in the current month?
app/helpers/float_helper.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +module FloatHelper
  2 +
  3 + def decimal_to_float( num )
  4 + if num.count('.') == 1 && num.count(',') == 0
  5 + # number like "12.34"
  6 + return num.to_f
  7 + end
  8 +
  9 + if num.count('.') == 0 && num.count(',') == 1
  10 + # number like "12,34"
  11 + return num.tr(',','.').to_f
  12 + end
  13 +
  14 + if num.count('.') > 0 && num.count(',') > 0
  15 + # number like "12.345.678,90" or "12,345,678.90"
  16 + dec_sep = num.tr('0-9','')[-1].chr
  17 + return num.tr('^0-9'+dec_sep,'').tr(dec_sep,'.').to_f
  18 + end
  19 +
  20 + # if you are here is because there is only one
  21 + # separator and this appears 2 times or more.
  22 + # number like "12.345.678" or "12,345,678"
  23 +
  24 + return num.tr(',.','').to_f
  25 + end
  26 +
  27 +end
app/helpers/folder_helper.rb
1 module FolderHelper 1 module FolderHelper
2 2
  3 + include ShortFilename
  4 +
3 def list_articles(articles, recursive = false) 5 def list_articles(articles, recursive = false)
4 if !articles.blank? 6 if !articles.blank?
5 - content_tag(  
6 - 'table',  
7 - content_tag('tr', content_tag('th', _('Title')) + content_tag('th', _('Last update'))) +  
8 - articles.map {|item| display_article_in_listing(item, recursive, 0)}.join('') 7 + articles = articles.paginate(
  8 + :order => "updated_at DESC",
  9 + :per_page => 10,
  10 + :page => params[:npage]
9 ) 11 )
  12 +
  13 + render :file => 'shared/articles_list', :locals => {:articles => articles, :recursive => recursive}
10 else 14 else
11 content_tag('em', _('(empty folder)')) 15 content_tag('em', _('(empty folder)'))
12 end 16 end
@@ -17,9 +21,14 @@ module FolderHelper @@ -17,9 +21,14 @@ module FolderHelper
17 end 21 end
18 22
19 def display_article_in_listing(article, recursive = false, level = 0) 23 def display_article_in_listing(article, recursive = false, level = 0)
  24 + article_link = if article.image?
  25 + link_to('&nbsp;' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true))
  26 + else
  27 + link_to('&nbsp;' * (level * 4) + short_filename(article.name), article.url.merge(:view => true), :class => icon_for_article(article))
  28 + end
20 result = content_tag( 29 result = content_tag(
21 'tr', 30 'tr',
22 - content_tag('td', link_to(('&nbsp;' * (level * 4) ) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true)))+ 31 + content_tag('td', article_link )+
23 content_tag('td', show_date(article.updated_at), :class => 'last-update'), 32 content_tag('td', show_date(article.updated_at), :class => 'last-update'),
24 :class => 'sitemap-item' 33 :class => 'sitemap-item'
25 ) 34 )
@@ -31,18 +40,22 @@ module FolderHelper @@ -31,18 +40,22 @@ module FolderHelper
31 end 40 end
32 41
33 def icon_for_article(article) 42 def icon_for_article(article)
34 - icon = article.icon_name 43 + icon = article.class.icon_name(article)
35 if (icon =~ /\//) 44 if (icon =~ /\//)
36 icon 45 icon
37 else 46 else
38 - if File.exists?(File.join(RAILS_ROOT, 'public', 'images', 'icons-mime', "#{icon}.png"))  
39 - "icons-mime/#{icon}.png"  
40 - else  
41 - "icons-mime/unknown.png" 47 + klasses = 'icon icon-' + icon
  48 + if article.kind_of?(UploadedFile)
  49 + klasses += ' icon-upload-file'
42 end 50 end
  51 + klasses
43 end 52 end
44 end 53 end
45 54
  55 + def icon_for_new_article(type)
  56 + "icon-new icon-new%s" % type.constantize.icon_name
  57 + end
  58 +
46 def custom_options_for_article(article) 59 def custom_options_for_article(article)
47 @article = article 60 @article = article
48 content_tag('h4', _('Options')) + 61 content_tag('h4', _('Options')) +
@@ -69,11 +82,4 @@ module FolderHelper @@ -69,11 +82,4 @@ module FolderHelper
69 _('Edit folder') 82 _('Edit folder')
70 end 83 end
71 84
72 - def short_filename(filename, limit_chars = 43)  
73 - return filename if filename.size <= limit_chars  
74 - extname = File.extname(filename)  
75 - basename = File.basename(filename,extname)  
76 - str_complement = '(...)'  
77 - return basename[0..(limit_chars - extname.size - str_complement.size - 1)] + str_complement + extname  
78 - end  
79 end 85 end
app/helpers/forms_helper.rb
@@ -41,8 +41,7 @@ module FormsHelper @@ -41,8 +41,7 @@ module FormsHelper
41 the_class << ' ' << html_options[:class] 41 the_class << ' ' << html_options[:class]
42 end 42 end
43 43
44 - # FIXME: should be in stylesheet  
45 - bt_submit = submit_tag(label, html_options.merge(:style => 'height:28px; cursor:pointer', :class => the_class)) 44 + bt_submit = submit_tag(label, html_options.merge(:class => the_class))
46 45
47 bt_submit + bt_cancel 46 bt_submit + bt_cancel
48 end 47 end
@@ -60,6 +59,10 @@ module FormsHelper @@ -60,6 +59,10 @@ module FormsHelper
60 59
61 state_id = 'state-' + FormsHelper.next_id_number 60 state_id = 'state-' + FormsHelper.next_id_number
62 city_id = 'city-' + FormsHelper.next_id_number 61 city_id = 'city-' + FormsHelper.next_id_number
  62 +
  63 + if states.length < 1
  64 + return
  65 + end
63 66
64 if simple 67 if simple
65 states = [State.new(:name => _('Select the State'))] + states 68 states = [State.new(:name => _('Select the State'))] + states
@@ -108,6 +111,18 @@ module FormsHelper @@ -108,6 +111,18 @@ module FormsHelper
108 )) 111 ))
109 end 112 end
110 113
  114 + def options_for_select_with_title(container, selected = nil)
  115 + container = container.to_a if Hash === container
  116 +
  117 + options_for_select = container.inject([]) do |options, element|
  118 + text, value = option_text_and_value(element)
  119 + selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
  120 + options << %(<option title="#{html_escape(text.to_s)}" value="#{html_escape(value.to_s)}"#{selected_attribute}>#{html_escape(text.to_s)}</option>)
  121 + end
  122 +
  123 + options_for_select.join("\n")
  124 + end
  125 +
111 protected 126 protected
112 def self.next_id_number 127 def self.next_id_number
113 if defined? @@id_num 128 if defined? @@id_num
app/helpers/forum_helper.rb 0 → 100644
@@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
  1 +module ForumHelper
  2 +
  3 + def cms_label_for_new_children
  4 + _('New discussion topic')
  5 + end
  6 +
  7 + def cms_label_for_edit
  8 + _('Configure forum')
  9 + end
  10 +
  11 + def list_forum_posts(articles)
  12 + pagination = will_paginate(articles, {
  13 + :param_name => 'npage',
  14 + :prev_label => _('&laquo; Newer posts'),
  15 + :next_label => _('Older posts &raquo;')
  16 + })
  17 + content = [content_tag('tr',
  18 + content_tag('th', _('Discussion topic')) +
  19 + content_tag('th', _('Posts')) +
  20 + content_tag('th', _('Last post'))
  21 + )
  22 + ]
  23 + artic_len = articles.length
  24 + articles.each_with_index{ |art,i|
  25 + css_add = [ 'position-'+(i+1).to_s() ]
  26 + position = (i%2 == 0) ? 'odd-post' : 'even-post'
  27 + css_add << 'first' if i == 0
  28 + css_add << 'last' if i == (artic_len-1)
  29 + css_add << 'not-published' if !art.published?
  30 + css_add << position
  31 + content << content_tag('tr',
  32 + content_tag('td', link_to(art.title, art.url)) +
  33 + content_tag('td', link_to(art.comments.count, art.url.merge(:anchor => 'comments_list'))) +
  34 + content_tag('td', last_topic_update(art)),
  35 + :class => 'forum-post ' + css_add.join(' '),
  36 + :id => "post-#{art.id}"
  37 + )
  38 + }
  39 + content_tag('table', content) + (pagination or '')
  40 + end
  41 +
  42 + def last_topic_update(article)
  43 + info = article.info_from_last_update
  44 + if info[:author_url]
  45 + time_ago_as_sentence(info[:date]) + ' ' + _('ago') + ' ' + _('by') + ' ' + link_to(info[:author_name], info[:author_url])
  46 + else
  47 + time_ago_as_sentence(info[:date]) + ' ' + _('ago') + ' ' + _('by') + ' ' + info[:author_name]
  48 + end
  49 + end
  50 +
  51 +end
app/helpers/friends_helper.rb
1 module FriendsHelper 1 module FriendsHelper
2 2
3 - def link_to_import(text, options = {})  
4 - options.merge!({:action => 'invite', :import => 1, :wizard => true})  
5 - link_to text, options  
6 - end  
7 -  
8 - def pagination_links(collection, options={})  
9 - options = {:prev_label => '&laquo; ' + _('Previous'), :next_label => _('Next') + ' &raquo;'}.merge(options)  
10 - will_paginate(collection, options)  
11 - end  
12 -  
13 end 3 end
app/helpers/manage_products_helper.rb
1 module ManageProductsHelper 1 module ManageProductsHelper
  2 +
  3 + def remote_function_to_update_categories_selection(container_id, options = {})
  4 + remote_function({
  5 + :update => container_id,
  6 + :url => { :action => "categories_for_selection" },
  7 + :loading => "loading('hierarchy_navigation', '#{ _('loading…') }'); loading('#{container_id}', '&nbsp;')",
  8 + :complete => "loading_done('hierarchy_navigation'); loading_done('#{container_id}')"
  9 + }.merge(options))
  10 + end
  11 +
  12 + def hierarchy_category_item(category, make_links, title = nil)
  13 + title ||= category.name
  14 + if make_links
  15 + link_to(title, '#',
  16 + :title => title,
  17 + :onclick => remote_function_to_update_categories_selection("categories_container_level#{ category.level + 1 }",
  18 + :with => "'category_id=#{ category.id }'"
  19 + )
  20 + )
  21 + else
  22 + title
  23 + end
  24 + end
  25 +
  26 + def hierarchy_category_navigation(current_category, options = {})
  27 + hierarchy = []
  28 + if current_category
  29 + count_chars = 0
  30 + unless options[:hide_current_category]
  31 + hierarchy << current_category.name
  32 + count_chars += current_category.name.length
  33 + end
  34 + ancestors = current_category.ancestors
  35 + toplevel = ancestors.pop
  36 + if toplevel
  37 + count_chars += toplevel.name.length
  38 + end
  39 + ancestors.each do |category|
  40 + if count_chars > 55
  41 + hierarchy << hierarchy_category_item(category, options[:make_links], '( … )')
  42 + break
  43 + else
  44 + hierarchy << hierarchy_category_item(category, options[:make_links])
  45 + end
  46 + count_chars += category.name.length
  47 + end
  48 + if toplevel
  49 + hierarchy << hierarchy_category_item(toplevel, options[:make_links])
  50 + end
  51 + end
  52 + hierarchy.reverse.join(options[:separator] || ' &rarr; ')
  53 + end
  54 +
  55 + def options_for_select_categories(categories, selected = nil)
  56 + categories.sort_by{|cat| cat.name.transliterate}.map do |category|
  57 + selected_attribute = selected.nil? ? '' : (category == selected ? "selected='selected'" : '')
  58 + "<option value='#{category.id}' title='#{category.name}' #{selected_attribute}>#{truncate(category.name, 33) + (category.leaf? ? '': ' &raquo;')}</option>"
  59 + end.join("\n")
  60 + end
  61 +
  62 + def build_selects_for_ancestors(ancestors, current_category)
  63 + current_ancestor = ancestors.shift
  64 + if current_ancestor.nil?
  65 + select_for_new_category(current_category.children, current_category.level + 1)
  66 + else
  67 + content_tag('div',
  68 + select_tag('category_id',
  69 + options_for_select_categories(current_ancestor.siblings + [current_ancestor], current_ancestor),
  70 + :size => 10,
  71 + :onchange => remote_function_to_update_categories_selection("categories_container_level#{ current_ancestor.level + 1 }", :with => "'category_id=' + this.value")
  72 + ) +
  73 + build_selects_for_ancestors(ancestors, current_category),
  74 + :class => 'categories_container',
  75 + :id => "categories_container_level#{ current_ancestor.level }"
  76 + )
  77 + end
  78 + end
  79 +
  80 + def selects_for_all_ancestors(current_category)
  81 + build_selects_for_ancestors(current_category.ancestors.reverse + [current_category], current_category)
  82 + end
  83 +
  84 + def select_for_new_category(categories, level)
  85 + content_tag('div',
  86 + render(:partial => 'categories_for_selection', :locals => { :categories => categories, :level => level }),
  87 + :class => 'categories_container',
  88 + :id => "categories_container_level#{ level }"
  89 + )
  90 + end
  91 +
  92 + def categories_container(categories_selection_html, hierarchy_html = '')
  93 + hidden_field_tag('selected_category_id') +
  94 + content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') +
  95 + content_tag('div', categories_selection_html, :id => 'categories_container_wrapper')
  96 + end
  97 +
  98 + def select_for_categories(categories, level = 0)
  99 + if categories.empty?
  100 + content_tag('div', '', :id => 'no_subcategories')
  101 + else
  102 + select_tag('category_id',
  103 + options_for_select_categories(categories),
  104 + :size => 10,
  105 + :onchange => remote_function_to_update_categories_selection("categories_container_level#{ level + 1 }", :with => "'category_id=' + this.value")
  106 + ) +
  107 + content_tag('div', '', :class => 'categories_container', :id => "categories_container_level#{ level + 1 }")
  108 + end
  109 + end
  110 +
  111 + def edit_link(label, url, html_options = {})
  112 + return '' unless (user && user.has_permission?('manage_products', profile))
  113 + link_to(label, url, html_options)
  114 + end
  115 +
  116 + def edit_product_link_to_remote(product, field, label, html_options = {})
  117 + return '' unless (user && user.has_permission?('manage_products', profile))
  118 + options = html_options.merge(:id => 'link-edit-product-' + field)
  119 + options[:class] = options[:class] ? options[:class] + ' link-to-remote' : 'link-to-remote'
  120 +
  121 + link_to_remote(label,
  122 + {:update => "product-#{field}",
  123 + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field },
  124 + :method => :get,
  125 + :loading => "loading_for_button('#link-edit-product-#{field}')"},
  126 + options)
  127 + end
  128 +
  129 + def edit_button(type, label, url, html_options = {})
  130 + return '' unless (user && user.has_permission?('manage_products', profile))
  131 + button(type, label, url, html_options)
  132 + end
  133 +
  134 + def edit_product_button_to_remote(product, field, label, html_options = {})
  135 + the_class = 'button with-text icon-edit'
  136 + if html_options.has_key?(:class)
  137 + the_class << ' ' << html_options[:class]
  138 + end
  139 + edit_product_link_to_remote(product, field, label, html_options.merge(:class => the_class))
  140 + end
  141 +
  142 + def edit_ui_button(label, url, html_options = {})
  143 + return '' unless (user && user.has_permission?('manage_products', profile))
  144 + ui_button(label, url, html_options)
  145 + end
  146 +
  147 + def edit_product_ui_button_to_remote(product, field, label, html_options = {})
  148 + return '' unless (user && user.has_permission?('manage_products', profile))
  149 + id = 'edit-product-remote-button-ui-' + field
  150 + options = html_options.merge(:id => id)
  151 +
  152 + ui_button_to_remote(label,
  153 + {:update => "product-#{field}",
  154 + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field },
  155 + :complete => "$('edit-product-button-ui-#{field}').hide()",
  156 + :method => :get,
  157 + :loading => "loading_for_button('##{id}')"},
  158 + options)
  159 + end
  160 +
  161 + def cancel_edit_product_link(product, field, html_options = {})
  162 + return '' unless (user && user.has_permission?('manage_products', profile))
  163 + button_to_function(:cancel, _('Cancel'), nil, html_options) do |page|
  164 + page.replace_html "product-#{field}", :partial => "display_#{field}", :locals => {:product => product}
  165 + end
  166 + end
  167 +
  168 + def edit_product_category_link(product, html_options = {})
  169 + return '' unless (user && user.has_permission?('manage_products', profile))
  170 + options = html_options.merge(:id => 'link-edit-product-category')
  171 + link_to(_('Change category'), { :action => 'edit_category', :id => product.id}, options)
  172 + end
  173 +
  174 + def display_value(product)
  175 + price = product.price
  176 + return '' if price.blank? || price.zero?
  177 + discount = product.discount
  178 + if discount.blank? || discount.zero?
  179 + result = display_price(_('Price: '), price)
  180 + else
  181 + result = display_price_with_discount(price, product.price_with_discount)
  182 + end
  183 + content_tag('span', content_tag('span', result, :class => 'product-price'), :class => "#{product.available? ? '' : 'un'}available-product")
  184 + end
  185 +
  186 + def display_availability(product)
  187 + if !product.available?
  188 + ui_highlight(_('Product not available!'))
  189 + end
  190 + end
  191 +
  192 + def display_price(label, price)
  193 + content_tag('span', label, :class => 'field-name') +
  194 + content_tag('span', float_to_currency(price), :class => 'field-value')
  195 + end
  196 +
  197 + def display_price_with_discount(price, price_with_discount)
  198 + original_value = content_tag('span', display_price(_('List price: '), price), :class => 'list-price')
  199 + discount_value = content_tag('span', display_price(_('On sale: '), price_with_discount), :class => 'on-sale-price')
  200 + original_value + tag('br') + discount_value
  201 + end
  202 +
  203 + def display_qualifiers(product)
  204 + data = ''
  205 + product.product_qualifiers.each do |pq|
  206 + certified_by = ''
  207 + certifier = pq.certifier
  208 + if certifier
  209 + certifier_name = certifier.link.blank? ? certifier.name : link_to(certifier.name, certifier.link)
  210 + certified_by = _('certified by %s') % certifier_name
  211 + else
  212 + certified_by = _('(Self declared)')
  213 + end
  214 + data << content_tag('li', "✔ #{pq.qualifier.name} #{certified_by}", :class => 'product-qualifiers-item')
  215 + end
  216 + content_tag('ul', data, :id => 'product-qualifiers')
  217 + end
  218 +
  219 + def qualifiers_for_select
  220 + [[_('Select...'), nil]] + environment.qualifiers.map{ |c| [c.name, c.id] }
  221 + end
  222 + def certifiers_for_select(qualifier)
  223 + [[_('Self declared'), nil]] + qualifier.certifiers.map{ |c| [c.name, c.id] }
  224 + end
  225 + def select_qualifiers(product, selected = nil)
  226 + select_tag('selected_qualifier', options_for_select(qualifiers_for_select, selected),
  227 + :onchange => remote_function(
  228 + :url => {:action => 'certifiers_for_selection'},
  229 + :with => "'id=' + value + '&certifier_area=' + jQuery(this).parent().next().attr('id')",
  230 + :before => "small_loading(jQuery(this).parent().next().attr('id'), '&nbsp;')"
  231 + ),
  232 + :id => nil
  233 + )
  234 + end
  235 + def select_certifiers(qualifier, product = nil)
  236 + if qualifier
  237 + selected = product ? product.product_qualifiers.find_by_qualifier_id(qualifier.id).certifier_id : nil
  238 + select_tag("product[qualifiers_list][#{qualifier.id}]", options_for_select(certifiers_for_select(qualifier), selected))
  239 + else
  240 + select_tag("product[qualifiers_list][nil]")
  241 + end
  242 + end
  243 +
  244 + def select_unit(object)
  245 + selected = object.unit.nil? ? '' : object.unit
  246 + select(object.class.name.downcase, 'unit', Product::UNITS.map{|unit| [_(unit[0]), unit[0]]}, {:selected => selected, :include_blank => _('Select the unit')})
  247 + end
  248 +
  249 + def input_icon(input)
  250 + if input.is_from_solidarity_economy?
  251 + hint = _('Product from solidarity economy')
  252 + image_tag("/images/solidarity-economy.png", :class => 'solidatiry-economy-icon', :alt => hint, :title => hint)
  253 + end
  254 + end
  255 +
  256 + def display_price_by(unit)
  257 + selected_unit = content_tag('span', unit, :class => 'selected-unit')
  258 + content_tag('span', _('by') + ' ' + selected_unit, :class => 'price-by-unit')
  259 + end
  260 +
  261 + def label_amount_used(input)
  262 + product_unit = input.product.unit
  263 + if product_unit.blank?
  264 + _('Amount used in this product or service')
  265 + else
  266 + _('Amount used by %s of this product or service') % _(product_unit)
  267 + end
  268 + end
  269 +
  270 + def display_unit(input)
  271 + input_amount_used = content_tag('span', input.formatted_amount, :class => 'input-amount-used')
  272 + return input_amount_used if input.unit.blank?
  273 + units = Product::UNITS.find {|unit| unit[0] == input.unit}
  274 + n_('1 %{singular_unit}', '%{num} %{plural_unit}', input.amount_used.to_f) % { :num => input_amount_used, :singular_unit => content_tag('span', units[0], :class => 'input-unit'), :plural_unit => content_tag('span', units[1], :class => 'input-unit') }
  275 + end
2 end 276 end
app/helpers/profile_helper.rb
@@ -15,9 +15,12 @@ module ProfileHelper @@ -15,9 +15,12 @@ module ProfileHelper
15 end 15 end
16 end 16 end
17 17
18 - def pagination_links(collection, options={})  
19 - options = {:prev_label => '&laquo; ' + _('Previous'), :next_label => _('Next') + ' &raquo;'}.merge(options)  
20 - will_paginate(collection, options)  
21 - end 18 + def render_tabs(tabs)
  19 + titles = tabs.inject(''){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') }
  20 + contents = tabs.inject(''){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) }
22 21
  22 + content_tag :div, :class => 'ui-tabs' do
  23 + content_tag(:ul, titles) + contents
  24 + end
  25 + end
23 end 26 end
app/helpers/search_helper.rb
@@ -98,11 +98,6 @@ module SearchHelper @@ -98,11 +98,6 @@ module SearchHelper
98 ), :class => 'profile-info') 98 ), :class => 'profile-info')
99 end 99 end
100 100
101 - def pagination_links(collection, options={})  
102 - options = {:prev_label => '&laquo; ' + _('Previous'), :next_label => _('Next') + ' &raquo;'}.merge(options)  
103 - will_paginate(collection, options)  
104 - end  
105 -  
106 def product_categories_menu(asset, product_category, object_ids = nil) 101 def product_categories_menu(asset, product_category, object_ids = nil)
107 cats = ProductCategory.menu_categories(@product_category, environment) 102 cats = ProductCategory.menu_categories(@product_category, environment)
108 cats += cats.select { |c| c.children_count > 0 }.map(&:children).flatten 103 cats += cats.select { |c| c.children_count > 0 }.map(&:children).flatten
app/models/action_tracker_notification.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +class ActionTrackerNotification < ActiveRecord::Base
  2 +
  3 + belongs_to :profile
  4 + belongs_to :action_tracker, :class_name => 'ActionTracker::Record', :foreign_key => 'action_tracker_id'
  5 +
  6 + validates_presence_of :profile_id, :action_tracker_id
  7 + validates_uniqueness_of :action_tracker_id, :scope => :profile_id
  8 +
  9 +end
  10 +
  11 +ActionTracker::Record.has_many :action_tracker_notifications, :class_name => 'ActionTrackerNotification', :foreign_key => 'action_tracker_id', :dependent => :destroy
app/models/add_friend.rb
1 class AddFriend < Task 1 class AddFriend < Task
2 2
3 - acts_as_having_settings :group_for_person, :group_for_friend, :field => :data 3 + settings_items :group_for_person, :group_for_friend
4 4
5 validates_presence_of :requestor_id, :target_id 5 validates_presence_of :requestor_id, :target_id
6 6
7 validates_uniqueness_of :target_id, :scope => [ :requestor_id ] 7 validates_uniqueness_of :target_id, :scope => [ :requestor_id ]
8 8
9 - validates_length_of :group_for_person, :group_for_friend, :maximum => 150, :allow_nil => true 9 + validates_length_of :group_for_person, :group_for_friend, :maximum => 150, :allow_nil => true
10 10
11 alias :person :requestor 11 alias :person :requestor
12 alias :person= :requestor= 12 alias :person= :requestor=
@@ -15,21 +15,37 @@ class AddFriend &lt; Task @@ -15,21 +15,37 @@ class AddFriend &lt; Task
15 alias :friend= :target= 15 alias :friend= :target=
16 16
17 def perform 17 def perform
18 - requestor.add_friend(target, group_for_person)  
19 target.add_friend(requestor, group_for_friend) 18 target.add_friend(requestor, group_for_friend)
20 - end  
21 -  
22 - def description  
23 - _('%s wants to be your friend.') % requestor.name 19 + requestor.add_friend(target, group_for_person)
24 end 20 end
25 21
26 def permission 22 def permission
27 :manage_friends 23 :manage_friends
28 end 24 end
29 25
  26 + def target_notification_description
  27 + _('%{requestor} wants to be your friend.') % {:requestor => requestor.name}
  28 + end
  29 +
30 def target_notification_message 30 def target_notification_message
31 - description + "\n\n" + 31 + target_notification_description + "\n\n" +
32 _('You need to login to %{system} in order to accept %{requestor} as your friend.') % { :system => target.environment.name, :requestor => requestor.name } 32 _('You need to login to %{system} in order to accept %{requestor} as your friend.') % { :system => target.environment.name, :requestor => requestor.name }
33 end 33 end
34 34
  35 + def title
  36 + _("New friend")
  37 + end
  38 +
  39 + def information
  40 + {:message => _('%{requestor} wants to be your friend.') }
  41 + end
  42 +
  43 + def accept_details
  44 + true
  45 + end
  46 +
  47 + def icon
  48 + {:type => :profile_image, :profile => requestor, :url => requestor.url}
  49 + end
  50 +
35 end 51 end
app/models/add_member.rb
@@ -8,23 +8,39 @@ class AddMember &lt; Task @@ -8,23 +8,39 @@ class AddMember &lt; Task
8 alias :organization :target 8 alias :organization :target
9 alias :organization= :target= 9 alias :organization= :target=
10 10
11 - acts_as_having_settings :roles, :field => :data 11 + settings_items :roles
12 12
13 def perform 13 def perform
14 self.roles ||= [Profile::Roles.member(organization.environment.id).id] 14 self.roles ||= [Profile::Roles.member(organization.environment.id).id]
15 target.affiliate(requestor, self.roles.select{|r| !r.to_i.zero? }.map{|i| Role.find(i)}) 15 target.affiliate(requestor, self.roles.select{|r| !r.to_i.zero? }.map{|i| Role.find(i)})
16 end 16 end
17 17
18 - def description  
19 - _('%s wants to be a member of "%s".') % [requestor.name, organization.name] 18 + def title
  19 + _("New member")
  20 + end
  21 +
  22 + def information
  23 + {:message => _('%{requestor} wants to be a member of this community.')}
  24 + end
  25 +
  26 + def accept_details
  27 + true
  28 + end
  29 +
  30 + def icon
  31 + {:type => :profile_image, :profile => requestor, :url => requestor.url}
20 end 32 end
21 33
22 def permission 34 def permission
23 :manage_memberships 35 :manage_memberships
24 end 36 end
25 37
  38 + def target_notification_description
  39 + _('%{requestor} wants to be a member of this community.') % {:requestor => requestor.name}
  40 + end
  41 +
26 def target_notification_message 42 def target_notification_message
27 - description + "\n\n" + 43 + target_notification_description + "\n\n" +
28 _('You will need login to %{system} in order to accept or reject %{requestor} as a member of %{organization}.') % { :system => target.environment.name, :requestor => requestor.name, :organization => organization.name } 44 _('You will need login to %{system} in order to accept or reject %{requestor} as a member of %{organization}.') % { :system => target.environment.name, :requestor => requestor.name, :organization => organization.name }
29 end 45 end
30 46
app/models/approve_article.rb
1 class ApproveArticle < Task 1 class ApproveArticle < Task
2 - serialize :data, Hash  
3 -  
4 validates_presence_of :requestor_id, :target_id 2 validates_presence_of :requestor_id, :target_id
5 3
6 - def description  
7 - _('%{author} wants to publish "%{article}" on %{community}') % { :author => requestor.name, :article => article_title, :community => target.name }  
8 - end  
9 -  
10 def article_title 4 def article_title
11 article ? article.title : _('(The original text was removed)') 5 article ? article.title : _('(The original text was removed)')
12 end 6 end
13 7
14 - def data  
15 - self[:data] ||= {}  
16 - end  
17 -  
18 def article 8 def article
19 Article.find_by_id data[:article_id] 9 Article.find_by_id data[:article_id]
20 end 10 end
@@ -24,53 +14,97 @@ class ApproveArticle &lt; Task @@ -24,53 +14,97 @@ class ApproveArticle &lt; Task
24 end 14 end
25 15
26 def name 16 def name
27 - data[:name] 17 + data[:name].blank? ? (article ? article.name : _("Article removed.")) : data[:name]
28 end 18 end
29 19
30 def name= value 20 def name= value
31 data[:name] = value 21 data[:name] = value
32 end 22 end
33 23
34 - def closing_statment  
35 - data[:closing_statment] 24 + settings_items :closing_statment, :article_parent_id, :highlighted
  25 +
  26 + def article_parent
  27 + Article.find_by_id article_parent_id.to_i
36 end 28 end
37 -  
38 - def closing_statment= value  
39 - data[:closing_statment] = value 29 +
  30 + def article_parent= value
  31 + self.article_parent_id = value.id
40 end 32 end
41 33
42 - def article_parent_id= value  
43 - data[:parent_id] = value 34 + def abstract= value
  35 + data[:abstract] = value
44 end 36 end
45 37
46 - def article_parent_id  
47 - data[:parent_id] 38 + def abstract
  39 + data[:abstract].blank? ? (article ? article.abstract : '') : data[:abstract]
48 end 40 end
49 41
50 - def article_parent  
51 - Article.find_by_id article_parent_id.to_i 42 + def body= value
  43 + data[:body] = value
52 end 44 end
53 45
54 - def article_parent= value  
55 - self.article_parent_id = value.id 46 + def body
  47 + data[:body].blank? ? (article ? article.body : "") : data[:body]
56 end 48 end
57 49
58 - def highlighted= value  
59 - data[:highlighted] = value 50 + def perform
  51 + article.copy!(:name => name, :abstract => abstract, :body => body, :profile => target, :reference_article => article, :parent => article_parent, :highlighted => highlighted, :source => article.source)
60 end 52 end
61 53
62 - def highlighted  
63 - data[:highlighted] 54 + def title
  55 + _("New article")
64 end 56 end
65 57
66 - def perform  
67 - PublishedArticle.create!(:name => name, :profile => target, :reference_article => article, :parent => article_parent, :highlighted => highlighted) 58 + def icon
  59 + result = {:type => :defined_image, :src => '/images/icons-app/article-minor.png', :name => name}
  60 + result.merge({:url => article.url}) if article
  61 + return result
  62 + end
  63 +
  64 + def linked_subject
  65 + {:text => name, :url => article.url} if article
  66 + end
  67 +
  68 + def information
  69 + if article
  70 + {:message => _('%{requestor} wants to publish the article: %{linked_subject}.')}
  71 + else
  72 + {:message => _("The article was removed.")}
  73 + end
  74 + end
  75 +
  76 + def accept_details
  77 + true
  78 + end
  79 +
  80 + def default_decision
  81 + if article
  82 + 'skip'
  83 + else
  84 + 'reject'
  85 + end
  86 + end
  87 +
  88 + def accept_disabled?
  89 + article.blank?
  90 + end
  91 +
  92 + def target_notification_description
  93 + _('%{requestor} wants to publish the article: %{article}.') % {:requestor => requestor.name, :article => article.name}
68 end 94 end
69 95
70 def target_notification_message 96 def target_notification_message
71 return nil if target.organization? && !target.moderated_articles? 97 return nil if target.organization? && !target.moderated_articles?
72 - description + "\n\n" + 98 + target_notification_description + "\n\n" +
73 _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name } 99 _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name }
74 end 100 end
75 101
  102 + def task_finished_message
  103 + if !closing_statment.blank?
  104 + _("Your request for publishing the article \"%{article}\" was approved. Here is the comment left by the admin who approved your article:\n\n%{comment} ") % {:article => name, :comment => closing_statment}
  105 + else
  106 + _('Your request for publishing the article "%{article}" was approved.') % {:article => name}
  107 + end
  108 + end
  109 +
76 end 110 end
app/models/article.rb
  1 +require 'hpricot'
  2 +
1 class Article < ActiveRecord::Base 3 class Article < ActiveRecord::Base
2 4
  5 + track_actions :create_article, :after_create, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && !a.image? }, :custom_target => :action_tracker_target
  6 + track_actions :update_article, :before_update, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && (a.body_changed? || a.name_changed?) }, :custom_target => :action_tracker_target
  7 + track_actions :remove_article, :before_destroy, :keep_params => [:name], :if => Proc.new { |a| a.is_trackable? }, :custom_target => :action_tracker_target
  8 +
3 # xss_terminate plugin can't sanitize array fields 9 # xss_terminate plugin can't sanitize array fields
4 before_save :sanitize_tag_list 10 before_save :sanitize_tag_list
5 11
@@ -19,14 +25,25 @@ class Article &lt; ActiveRecord::Base @@ -19,14 +25,25 @@ class Article &lt; ActiveRecord::Base
19 acts_as_having_settings :field => :setting 25 acts_as_having_settings :field => :setting
20 26
21 settings_items :display_hits, :type => :boolean, :default => true 27 settings_items :display_hits, :type => :boolean, :default => true
  28 + settings_items :author_name, :type => :string, :default => ""
22 29
23 belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id' 30 belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id'
24 31
  32 + has_many :translations, :class_name => 'Article', :foreign_key => :translation_of_id
  33 + belongs_to :translation_of, :class_name => 'Article', :foreign_key => :translation_of_id
  34 + before_destroy :rotate_translations
  35 +
25 before_create do |article| 36 before_create do |article|
26 article.published_at = article.created_at if article.published_at.nil? 37 article.published_at = article.created_at if article.published_at.nil?
  38 + if article.reference_article && !article.parent
  39 + parent = article.reference_article.parent
  40 + if parent && parent.blog? && article.profile.has_blog?
  41 + article.parent = article.profile.blog
  42 + end
  43 + end
27 end 44 end
28 45
29 - xss_terminate :only => [ :name ], :on => 'validation' 46 + xss_terminate :only => [ :name ], :on => 'validation', :with => 'white_list'
30 47
31 named_scope :in_category, lambda { |category| 48 named_scope :in_category, lambda { |category|
32 {:include => 'categories', :conditions => { 'categories.id' => category.id }} 49 {:include => 'categories', :conditions => { 'categories.id' => category.id }}
@@ -38,20 +55,17 @@ class Article &lt; ActiveRecord::Base @@ -38,20 +55,17 @@ class Article &lt; ActiveRecord::Base
38 ] 55 ]
39 }} 56 }}
40 57
41 - def self.first_day_of_month(date)  
42 - date ||= Date.today  
43 - Date.new(date.year, date.month, 1)  
44 - end  
45 -  
46 - def self.last_day_of_month(date)  
47 - date ||= Date.today  
48 - date >>= 1  
49 - Date.new(date.year, date.month, 1) - 1.day  
50 - end  
51 -  
52 URL_FORMAT = /\A(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\Z/ix 58 URL_FORMAT = /\A(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\Z/ix
53 59
54 validates_format_of :external_link, :with => URL_FORMAT, :if => lambda { |article| !article.external_link.blank? } 60 validates_format_of :external_link, :with => URL_FORMAT, :if => lambda { |article| !article.external_link.blank? }
  61 + validate :known_language
  62 + validate :used_translation
  63 + validate :native_translation_must_have_language
  64 + validate :translation_must_have_language
  65 +
  66 + def is_trackable?
  67 + self.published? && self.notifiable? && self.advertise?
  68 + end
55 69
56 def external_link=(link) 70 def external_link=(link)
57 if !link.blank? && link !~ /^[a-z]+:\/\//i 71 if !link.blank? && link !~ /^[a-z]+:\/\//i
@@ -60,6 +74,9 @@ class Article &lt; ActiveRecord::Base @@ -60,6 +74,9 @@ class Article &lt; ActiveRecord::Base
60 self[:external_link] = link 74 self[:external_link] = link
61 end 75 end
62 76
  77 + def action_tracker_target
  78 + self.profile
  79 + end
63 80
64 def self.human_attribute_name(attrib) 81 def self.human_attribute_name(attrib)
65 case attrib.to_sym 82 case attrib.to_sym
@@ -120,9 +137,9 @@ class Article &lt; ActiveRecord::Base @@ -120,9 +137,9 @@ class Article &lt; ActiveRecord::Base
120 137
121 # retrieves all articles belonging to the given +profile+ that are not 138 # retrieves all articles belonging to the given +profile+ that are not
122 # sub-articles of any other article. 139 # sub-articles of any other article.
123 - def self.top_level_for(profile)  
124 - self.find(:all, :conditions => [ 'parent_id is null and profile_id = ?', profile.id ])  
125 - end 140 + named_scope :top_level_for, lambda { |profile|
  141 + {:conditions => [ 'parent_id is null and profile_id = ?', profile.id ]}
  142 + }
126 143
127 # retrieves the latest +limit+ articles, sorted from the most recent to the 144 # retrieves the latest +limit+ articles, sorted from the most recent to the
128 # oldest. 145 # oldest.
@@ -182,7 +199,7 @@ class Article &lt; ActiveRecord::Base @@ -182,7 +199,7 @@ class Article &lt; ActiveRecord::Base
182 # to return their specific icons. 199 # to return their specific icons.
183 # 200 #
184 # FIXME use mime_type and generate this name dinamically 201 # FIXME use mime_type and generate this name dinamically
185 - def icon_name 202 + def self.icon_name(article = nil)
186 'text-html' 203 'text-html'
187 end 204 end
188 205
@@ -206,10 +223,24 @@ class Article &lt; ActiveRecord::Base @@ -206,10 +223,24 @@ class Article &lt; ActiveRecord::Base
206 name 223 name
207 end 224 end
208 225
  226 + include ActionView::Helpers::TextHelper
  227 + def short_title
  228 + truncate self.title, 15, '...'
  229 + end
  230 +
209 def belongs_to_blog? 231 def belongs_to_blog?
210 self.parent and self.parent.blog? 232 self.parent and self.parent.blog?
211 end 233 end
212 234
  235 + def info_from_last_update
  236 + last_comment = comments.last
  237 + if last_comment
  238 + {:date => last_comment.created_at, :author_name => last_comment.author_name, :author_url => last_comment.author_url}
  239 + else
  240 + {:date => updated_at, :author_name => author.name, :author_url => author.url}
  241 + end
  242 + end
  243 +
213 def url 244 def url
214 @url ||= self.profile.url.merge(:page => path.split('/')) 245 @url ||= self.profile.url.merge(:page => path.split('/'))
215 end 246 end
@@ -230,6 +261,73 @@ class Article &lt; ActiveRecord::Base @@ -230,6 +261,73 @@ class Article &lt; ActiveRecord::Base
230 false 261 false
231 end 262 end
232 263
  264 + def forum?
  265 + false
  266 + end
  267 +
  268 + def has_posts?
  269 + false
  270 + end
  271 +
  272 + named_scope :native_translations, :conditions => { :translation_of_id => nil }
  273 +
  274 + def translatable?
  275 + false
  276 + end
  277 +
  278 + def native_translation
  279 + self.translation_of.nil? ? self : self.translation_of
  280 + end
  281 +
  282 + def possible_translations
  283 + possibilities = Noosfero.locales.keys - self.native_translation.translations(:select => :language).map(&:language) - [self.native_translation.language]
  284 + possibilities << self.language unless self.language_changed?
  285 + possibilities
  286 + end
  287 +
  288 + def known_language
  289 + unless self.language.blank?
  290 + errors.add(:language, N_('Language not supported by Noosfero')) unless Noosfero.locales.key?(self.language)
  291 + end
  292 + end
  293 +
  294 + def used_translation
  295 + unless self.language.blank? or self.translation_of.nil?
  296 + errors.add(:language, N_('Language is already used')) unless self.possible_translations.include?(self.language)
  297 + end
  298 + end
  299 +
  300 + def translation_must_have_language
  301 + unless self.translation_of.nil?
  302 + errors.add(:language, N_('Language must be choosen')) if self.language.blank?
  303 + end
  304 + end
  305 +
  306 + def native_translation_must_have_language
  307 + unless self.translation_of.nil?
  308 + errors.add_to_base(N_('A language must be choosen for the native article')) if self.translation_of.language.blank?
  309 + end
  310 + end
  311 +
  312 + def rotate_translations
  313 + unless self.translations.empty?
  314 + rotate = self.translations
  315 + root = rotate.shift
  316 + root.update_attribute(:translation_of_id, nil)
  317 + root.translations = rotate
  318 + end
  319 + end
  320 +
  321 + def get_translation_to(locale)
  322 + if self.language.nil? || self.language == locale
  323 + self
  324 + elsif self.native_translation.language == locale
  325 + self.native_translation
  326 + else
  327 + self.native_translation.translations.first(:conditions => { :language => locale }) || self
  328 + end
  329 + end
  330 +
233 def published? 331 def published?
234 if self.published 332 if self.published
235 if self.parent && !self.parent.published? 333 if self.parent && !self.parent.published?
@@ -241,22 +339,33 @@ class Article &lt; ActiveRecord::Base @@ -241,22 +339,33 @@ class Article &lt; ActiveRecord::Base
241 end 339 end
242 end 340 end
243 341
244 - named_scope :published, :conditions => { :published => true }  
245 - named_scope :folders, :conditions => { :type => ['Folder', 'Blog'] } 342 + named_scope :published, :conditions => { :published => true }
  343 + named_scope :folders, :conditions => { :type => ['Folder', 'Blog', 'Forum', 'Gallery'] }
  344 + named_scope :galleries, :conditions => { :type => 'Gallery' }
  345 + named_scope :images, :conditions => { :is_image => true }
  346 +
  347 + def self.display_filter(user, profile)
  348 + return {:conditions => ['published = ?', true]} if !user
  349 + {:conditions => [" articles.published = ? OR
  350 + articles.last_changed_by_id = ? OR
  351 + articles.profile_id = ? OR
  352 + ?",
  353 + true, user.id, user.id, user.has_permission?(:view_private_content, profile)] }
  354 + end
246 355
247 def display_unpublished_article_to?(user) 356 def display_unpublished_article_to?(user)
248 - self.author == user || allow_view_private_content?(user) || user == self.profile ||  
249 - user.is_admin?(self.profile.environment) || user.is_admin?(self.profile) 357 + user == author || allow_view_private_content?(user) || user == profile ||
  358 + user.is_admin?(profile.environment) || user.is_admin?(profile)
250 end 359 end
251 360
252 - def display_to?(user)  
253 - if self.published?  
254 - self.profile.display_info_to?(user) 361 + def display_to?(user = nil)
  362 + if published?
  363 + profile.display_info_to?(user)
255 else 364 else
256 - if user.nil? 365 + if !user
257 false 366 false
258 else 367 else
259 - self.display_unpublished_article_to?(user) 368 + display_unpublished_article_to?(user)
260 end 369 end
261 end 370 end
262 end 371 end
@@ -286,12 +395,18 @@ class Article &lt; ActiveRecord::Base @@ -286,12 +395,18 @@ class Article &lt; ActiveRecord::Base
286 end 395 end
287 396
288 397
289 - def copy(options) 398 + def copy(options = {})
290 attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) } 399 attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) }
291 attrs.merge!(options) 400 attrs.merge!(options)
292 self.class.create(attrs) 401 self.class.create(attrs)
293 end 402 end
294 403
  404 + def copy!(options = {})
  405 + attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) }
  406 + attrs.merge!(options)
  407 + self.class.create!(attrs)
  408 + end
  409 +
295 ATTRIBUTES_NOT_COPIED = [ 410 ATTRIBUTES_NOT_COPIED = [
296 :id, 411 :id,
297 :profile_id, 412 :profile_id,
@@ -329,13 +444,28 @@ class Article &lt; ActiveRecord::Base @@ -329,13 +444,28 @@ class Article &lt; ActiveRecord::Base
329 false 444 false
330 end 445 end
331 446
332 - def display_as_gallery? 447 + def event?
  448 + false
  449 + end
  450 +
  451 + def gallery?
  452 + false
  453 + end
  454 +
  455 + def tiny_mce?
333 false 456 false
334 end 457 end
335 458
336 def author 459 def author
337 - last_changed_by ||  
338 - profile 460 + if reference_article
  461 + reference_article.author
  462 + else
  463 + last_changed_by || profile
  464 + end
  465 + end
  466 +
  467 + def author_name
  468 + setting[:author_name].blank? ? author.name : setting[:author_name]
339 end 469 end
340 470
341 alias :active_record_cache_key :cache_key 471 alias :active_record_cache_key :cache_key
@@ -348,8 +478,16 @@ class Article &lt; ActiveRecord::Base @@ -348,8 +478,16 @@ class Article &lt; ActiveRecord::Base
348 end 478 end
349 479
350 def first_paragraph 480 def first_paragraph
351 - to_html =~ /(.*<\/p>)/  
352 - $1 || '' 481 + paragraphs = Hpricot(to_html).search('p')
  482 + paragraphs.empty? ? '' : paragraphs.first.to_html
  483 + end
  484 +
  485 + def lead
  486 + abstract.blank? ? first_paragraph : abstract
  487 + end
  488 +
  489 + def short_lead
  490 + truncate sanitize_html(self.lead), 170, '...'
353 end 491 end
354 492
355 def creator 493 def creator
@@ -357,6 +495,10 @@ class Article &lt; ActiveRecord::Base @@ -357,6 +495,10 @@ class Article &lt; ActiveRecord::Base
357 creator_id && Profile.find(creator_id) 495 creator_id && Profile.find(creator_id)
358 end 496 end
359 497
  498 + def notifiable?
  499 + false
  500 + end
  501 +
360 private 502 private
361 503
362 def sanitize_tag_list 504 def sanitize_tag_list
@@ -368,4 +510,9 @@ class Article &lt; ActiveRecord::Base @@ -368,4 +510,9 @@ class Article &lt; ActiveRecord::Base
368 tag_name.gsub(/[<>]/, '') 510 tag_name.gsub(/[<>]/, '')
369 end 511 end
370 512
  513 + def sanitize_html(text)
  514 + sanitizer = HTML::FullSanitizer.new
  515 + sanitizer.sanitize(text)
  516 + end
  517 +
371 end 518 end
app/models/block.rb
@@ -19,12 +19,22 @@ class Block &lt; ActiveRecord::Base @@ -19,12 +19,22 @@ class Block &lt; ActiveRecord::Base
19 # may contain the following keys: 19 # may contain the following keys:
20 # 20 #
21 # * <tt>:article</tt>: the article being viewed currently 21 # * <tt>:article</tt>: the article being viewed currently
  22 + # * <tt>:language</tt>: in which language the block will be displayed
22 def visible?(context = nil) 23 def visible?(context = nil)
23 if display == 'never' 24 if display == 'never'
24 return false 25 return false
25 end 26 end
26 - if context && context[:article] && display == 'home_page_only'  
27 - return context[:article] == owner.home_page 27 + if context
  28 + if language != 'all' && language != context[:locale]
  29 + return false
  30 + end
  31 + if display == 'home_page_only'
  32 + if context[:article]
  33 + return context[:article] == owner.home_page
  34 + else
  35 + return context[:request_path] == '/'
  36 + end
  37 + end
28 end 38 end
29 true 39 true
30 end 40 end
@@ -37,6 +47,11 @@ class Block &lt; ActiveRecord::Base @@ -37,6 +47,11 @@ class Block &lt; ActiveRecord::Base
37 # homepage of its owner. 47 # homepage of its owner.
38 settings_items :display, :type => :string, :default => 'always' 48 settings_items :display, :type => :string, :default => 'always'
39 49
  50 + # The block can be configured to be displayed in all languages or in just one language. It can assume any locale of the environment:
  51 + #
  52 + # * <tt>'all'</tt>: the block is always displayed
  53 + settings_items :language, :type => :string, :default => 'all'
  54 +
40 # returns the description of the block, used when the user sees a list of 55 # returns the description of the block, used when the user sees a list of
41 # blocks to choose one to include in the design. 56 # blocks to choose one to include in the design.
42 # 57 #
app/models/blog.rb
1 class Blog < Folder 1 class Blog < Folder
2 2
3 - has_many :posts, :class_name => 'Article', :foreign_key => 'parent_id', :source => :children, :conditions => [ 'type != ?', 'RssFeed' ], :order => 'published_at DESC, id DESC'  
4 -  
5 - attr_accessor :feed_attrs  
6 -  
7 - after_create do |blog|  
8 - blog.children << RssFeed.new(:name => 'feed', :profile => blog.profile)  
9 - blog.feed = blog.feed_attrs  
10 - end  
11 -  
12 - settings_items :posts_per_page, :type => :integer, :default => 5 3 + acts_as_having_posts
13 4
14 def self.short_description 5 def self.short_description
15 _('Blog') 6 _('Blog')
@@ -35,21 +26,6 @@ class Blog &lt; Folder @@ -35,21 +26,6 @@ class Blog &lt; Folder
35 true 26 true
36 end 27 end
37 28
38 - def feed  
39 - self.children.find(:first, :conditions => {:type => 'RssFeed'})  
40 - end  
41 -  
42 - def feed=(attrs)  
43 - if attrs  
44 - if self.feed  
45 - self.feed.update_attributes(attrs)  
46 - else  
47 - self.feed_attrs = attrs  
48 - end  
49 - end  
50 - self.feed  
51 - end  
52 -  
53 has_one :external_feed, :foreign_key => 'blog_id', :dependent => :destroy 29 has_one :external_feed, :foreign_key => 'blog_id', :dependent => :destroy
54 30
55 attr_accessor :external_feed_data 31 attr_accessor :external_feed_data
@@ -78,17 +54,15 @@ class Blog &lt; Folder @@ -78,17 +54,15 @@ class Blog &lt; Folder
78 end 54 end
79 end 55 end
80 56
81 - def name=(value)  
82 - self.set_name(value)  
83 - if self.slug.blank?  
84 - self.slug = self.name.to_slug  
85 - else  
86 - self.slug = self.slug.to_slug  
87 - end 57 + def self.icon_name(article = nil)
  58 + 'blog'
88 end 59 end
89 60
90 settings_items :visualization_format, :type => :string, :default => 'full' 61 settings_items :visualization_format, :type => :string, :default => 'full'
91 validates_inclusion_of :visualization_format, :in => [ 'full', 'short' ], :if => :visualization_format 62 validates_inclusion_of :visualization_format, :in => [ 'full', 'short' ], :if => :visualization_format
92 63
  64 + settings_items :display_posts_in_current_language, :type => :boolean, :default => true
  65 +
  66 + alias :display_posts_in_current_language? :display_posts_in_current_language
93 67
94 end 68 end
app/models/blog_archives_block.rb
@@ -24,7 +24,7 @@ class BlogArchivesBlock &lt; Block @@ -24,7 +24,7 @@ class BlogArchivesBlock &lt; Block
24 owner_blog = self.blog 24 owner_blog = self.blog
25 return nil unless owner_blog 25 return nil unless owner_blog
26 results = '' 26 results = ''
27 - owner_blog.posts.group_by {|i| i.published_at.year }.sort_by { |year,count| -year }.each do |year, results_by_year| 27 + owner_blog.posts.native_translations.group_by {|i| i.published_at.year }.sort_by { |year,count| -year }.each do |year, results_by_year|
28 results << content_tag('li', content_tag('strong', "#{year} (#{results_by_year.size})")) 28 results << content_tag('li', content_tag('strong', "#{year} (#{results_by_year.size})"))
29 results << "<ul class='#{year}-archive'>" 29 results << "<ul class='#{year}-archive'>"
30 results_by_year.group_by{|i| [ ('%02d' % i.published_at.month()), gettext(MONTHS[i.published_at.month() - 1])]}.sort.reverse.each do |month, results_by_month| 30 results_by_year.group_by{|i| [ ('%02d' % i.published_at.month()), gettext(MONTHS[i.published_at.month() - 1])]}.sort.reverse.each do |month, results_by_month|
app/models/categories_block.rb 0 → 100644
@@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
  1 +class CategoriesBlock < Block
  2 +
  3 + CATEGORY_TYPES = {
  4 + _('Generic category') => nil,
  5 + _('Region') => 'Region',
  6 + _('Product') => 'ProductCategory'
  7 + }
  8 +
  9 + settings_items :category_types, :type => Array, :default => []
  10 +
  11 + def self.description
  12 + _("Categories Menu")
  13 + end
  14 +
  15 + def default_title
  16 + _("Categories Menu")
  17 + end
  18 +
  19 + def help
  20 + _('This block presents the categories like a web site menu.')
  21 + end
  22 +
  23 + def available_category_types
  24 + CATEGORY_TYPES
  25 + end
  26 +
  27 + def selected_categories
  28 + Category.top_level_for(self.owner).from_types(self.category_types)
  29 + end
  30 +
  31 + def content
  32 + block = self
  33 + lambda do
  34 + render :file => 'blocks/categories', :locals => { :block => block }
  35 + end
  36 + end
  37 +
  38 +end
app/models/category.rb
@@ -9,9 +9,9 @@ class Category &lt; ActiveRecord::Base @@ -9,9 +9,9 @@ class Category &lt; ActiveRecord::Base
9 validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.') 9 validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.')
10 10
11 # Finds all top level categories for a given environment. 11 # Finds all top level categories for a given environment.
12 - def self.top_level_for(environment)  
13 - self.find(:all, :conditions => ['parent_id is null and environment_id = ?', environment.id ])  
14 - end 12 + named_scope :top_level_for, lambda { |environment|
  13 + {:conditions => ['parent_id is null and environment_id = ?', environment.id ]}
  14 + }
15 15
16 acts_as_filesystem 16 acts_as_filesystem
17 17
@@ -30,6 +30,12 @@ class Category &lt; ActiveRecord::Base @@ -30,6 +30,12 @@ class Category &lt; ActiveRecord::Base
30 30
31 acts_as_having_image 31 acts_as_having_image
32 32
  33 + named_scope :from_types, lambda { |types|
  34 + types.select{ |t| t.blank? }.empty? ?
  35 + { :conditions => { :type => types } } :
  36 + { :conditions => [ "type IN (?) OR type IS NULL", types.reject{ |t| t.blank? } ] }
  37 + }
  38 +
33 def recent_articles(limit = 10) 39 def recent_articles(limit = 10)
34 self.articles.recent(limit) 40 self.articles.recent(limit)
35 end 41 end
@@ -58,4 +64,9 @@ class Category &lt; ActiveRecord::Base @@ -58,4 +64,9 @@ class Category &lt; ActiveRecord::Base
58 results 64 results
59 end 65 end
60 66
  67 + def is_leaf_displayable_in_menu?
  68 + return false if self.display_in_menu == false
  69 + self.children.find(:all, :conditions => {:display_in_menu => true}).empty?
  70 + end
  71 +
61 end 72 end
app/models/certifier.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class Certifier < ActiveRecord::Base
  2 +
  3 + belongs_to :environment
  4 +
  5 + has_many :qualifier_certifiers
  6 + has_many :qualifiers, :through => :qualifier_certifiers
  7 +
  8 + validates_presence_of :environment_id
  9 + validates_presence_of :name
  10 +
  11 + def link
  12 + self[:link] || ''
  13 + end
  14 +end
app/models/change_password.rb
1 class ChangePassword < Task 1 class ChangePassword < Task
2 2
3 - serialize :data, Hash  
4 - def data  
5 - self[:data] ||= {}  
6 - end  
7 -  
8 attr_accessor :login, :email, :password, :password_confirmation, :environment_id 3 attr_accessor :login, :email, :password, :password_confirmation, :environment_id
9 4
10 def self.human_attribute_name(attrib) 5 def self.human_attribute_name(attrib)
@@ -45,7 +40,7 @@ class ChangePassword &lt; Task @@ -45,7 +40,7 @@ class ChangePassword &lt; Task
45 end 40 end
46 41
47 before_validation_on_create do |change_password| 42 before_validation_on_create do |change_password|
48 - change_password.requestor = Person.find_by_identifier(change_password.login) 43 + change_password.requestor = Person.find_by_identifier_and_environment_id(change_password.login, change_password.environment_id)
49 end 44 end
50 45
51 ################################################### 46 ###################################################
@@ -56,9 +51,16 @@ class ChangePassword &lt; Task @@ -56,9 +51,16 @@ class ChangePassword &lt; Task
56 validates_presence_of :password_confirmation, :on => :update, :if => lambda { |change| change.status != Task::Status::CANCELLED } 51 validates_presence_of :password_confirmation, :on => :update, :if => lambda { |change| change.status != Task::Status::CANCELLED }
57 validates_confirmation_of :password, :if => lambda { |change| change.status != Task::Status::CANCELLED } 52 validates_confirmation_of :password, :if => lambda { |change| change.status != Task::Status::CANCELLED }
58 53
59 - def initialize(*args)  
60 - super(*args)  
61 - self[:data] = {} 54 + def title
  55 + _("Change password")
  56 + end
  57 +
  58 + def information
  59 + {:message => _('%{requestor} wants to change its password.')}
  60 + end
  61 +
  62 + def icon
  63 + {:type => :profile_image, :profile => requestor, :url => requestor.url}
62 end 64 end
63 65
64 def perform 66 def perform
@@ -87,8 +89,8 @@ class ChangePassword &lt; Task @@ -87,8 +89,8 @@ class ChangePassword &lt; Task
87 end 89 end
88 end 90 end
89 91
90 - def description  
91 - _('Password change request') 92 + def environment
  93 + self.requestor.environment
92 end 94 end
93 95
94 end 96 end
app/models/comment.rb
1 class Comment < ActiveRecord::Base 1 class Comment < ActiveRecord::Base
2 - 2 +
  3 + track_actions :leave_comment, :after_create, :keep_params => ["article.title", "article.url", "title", "url", "body"], :custom_target => :action_tracker_target
  4 +
3 validates_presence_of :title, :body 5 validates_presence_of :title, :body
4 belongs_to :article, :counter_cache => true 6 belongs_to :article, :counter_cache => true
5 belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id' 7 belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id'
  8 + has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
  9 + belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id'
6 10
7 # unauthenticated authors: 11 # unauthenticated authors:
8 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) 12 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? })
@@ -19,6 +23,10 @@ class Comment &lt; ActiveRecord::Base @@ -19,6 +23,10 @@ class Comment &lt; ActiveRecord::Base
19 23
20 xss_terminate :only => [ :body, :title, :name ], :on => 'validation' 24 xss_terminate :only => [ :body, :title, :name ], :on => 'validation'
21 25
  26 + def action_tracker_target
  27 + self.article.profile
  28 + end
  29 +
22 def author_name 30 def author_name
23 if author 31 if author
24 author.short_name 32 author.short_name
@@ -35,16 +43,20 @@ class Comment &lt; ActiveRecord::Base @@ -35,16 +43,20 @@ class Comment &lt; ActiveRecord::Base
35 author ? author.url : email 43 author ? author.url : email
36 end 44 end
37 45
  46 + def author_url
  47 + author ? author.url : nil
  48 + end
  49 +
38 def url 50 def url
39 article.view_url.merge(:anchor => anchor) 51 article.view_url.merge(:anchor => anchor)
40 end 52 end
41 53
42 def message 54 def message
43 - author_id ? _('(removed user)') : ('<br />' + _('(unauthenticated user)')) 55 + author_id ? _('(removed user)') : _('(unauthenticated user)')
44 end 56 end
45 57
46 def removed_user_image 58 def removed_user_image
47 - '/images/icons-app/user_icon_size-minor.png' 59 + '/images/icons-app/person-minor.png'
48 end 60 end
49 61
50 def anchor 62 def anchor
@@ -62,18 +74,34 @@ class Comment &lt; ActiveRecord::Base @@ -62,18 +74,34 @@ class Comment &lt; ActiveRecord::Base
62 end 74 end
63 75
64 after_create do |comment| 76 after_create do |comment|
65 - if comment.article.notify_comments? 77 + if comment.article.notify_comments? && !comment.article.profile.notification_emails.empty?
66 Comment::Notifier.deliver_mail(comment) 78 Comment::Notifier.deliver_mail(comment)
67 end 79 end
68 end 80 end
69 81
  82 + def replies
  83 + @replies || children
  84 + end
  85 +
  86 + def replies=(comments_list)
  87 + @replies = comments_list
  88 + end
  89 +
  90 + def self.as_thread
  91 + result = {}
  92 + root = []
  93 + all.each do |c|
  94 + c.replies = []
  95 + result[c.id] ||= c
  96 + c.reply_of_id.nil? ? root << c : result[c.reply_of_id].replies << c
  97 + end
  98 + root
  99 + end
  100 +
70 class Notifier < ActionMailer::Base 101 class Notifier < ActionMailer::Base
71 def mail(comment) 102 def mail(comment)
72 profile = comment.article.profile 103 profile = comment.article.profile
73 - email = profile.notification_emails  
74 - return unless email  
75 - recipients email  
76 - 104 + recipients profile.notification_emails
77 from "#{profile.environment.name} <#{profile.environment.contact_email}>" 105 from "#{profile.environment.name} <#{profile.environment.contact_email}>"
78 subject _("[%s] you got a new comment!") % [profile.environment.name] 106 subject _("[%s] you got a new comment!") % [profile.environment.name]
79 body :recipient => profile.nickname || profile.name, 107 body :recipient => profile.nickname || profile.name,
@@ -81,6 +109,8 @@ class Comment &lt; ActiveRecord::Base @@ -81,6 +109,8 @@ class Comment &lt; ActiveRecord::Base
81 :sender_link => comment.author_link, 109 :sender_link => comment.author_link,
82 :article_title => comment.article.name, 110 :article_title => comment.article.name,
83 :comment_url => comment.url, 111 :comment_url => comment.url,
  112 + :comment_title => comment.title,
  113 + :comment_body => comment.body,
84 :environment => profile.environment.name, 114 :environment => profile.environment.name,
85 :url => profile.environment.top_url 115 :url => profile.environment.top_url
86 end 116 end
app/models/communities_block.rb
@@ -8,10 +8,6 @@ class CommunitiesBlock &lt; ProfileListBlock @@ -8,10 +8,6 @@ class CommunitiesBlock &lt; ProfileListBlock
8 n__('{#} community', '{#} communities', profile_count) 8 n__('{#} community', '{#} communities', profile_count)
9 end 9 end
10 10
11 - def profile_image_link_method  
12 - :community_image_link  
13 - end  
14 -  
15 def help 11 def help
16 __('This block displays the communities in which the user is a member.') 12 __('This block displays the communities in which the user is a member.')
17 end 13 end
@@ -25,35 +21,15 @@ class CommunitiesBlock &lt; ProfileListBlock @@ -25,35 +21,15 @@ class CommunitiesBlock &lt; ProfileListBlock
25 end 21 end
26 when Environment 22 when Environment
27 lambda do 23 lambda do
28 - link_to s_('communities|View all'), :controller => 'search', :action => 'assets', :asset => 'communities' 24 + link_to s_('communities|View all'), :controller => 'browse', :action => 'communities'
29 end 25 end
30 else 26 else
31 '' 27 ''
32 end 28 end
33 end 29 end
34 30
35 - def profile_count  
36 - if owner.kind_of?(Environment)  
37 - owner.communities.count(:conditions => { :visible => true })  
38 - else  
39 - owner.communities(:visible => true).count  
40 - end  
41 - end  
42 -  
43 - def profile_finder  
44 - @profile_finder ||= CommunitiesBlock::Finder.new(self)  
45 - end  
46 -  
47 - class Finder < ProfileListBlock::Finder  
48 - def ids  
49 - # FIXME when owner is an environment (i.e. listing communities globally  
50 - # this can become SLOW)  
51 - if block.owner.kind_of?(Environment)  
52 - block.owner.communities.all(:conditions => {:visible => true}, :limit => block.limit, :order => 'random()').map(&:id)  
53 - else  
54 - block.owner.communities(:visible => true).map(&:id)  
55 - end  
56 - end 31 + def profiles
  32 + owner.communities
57 end 33 end
58 34
59 end 35 end
app/models/community.rb
@@ -67,4 +67,12 @@ class Community &lt; Organization @@ -67,4 +67,12 @@ class Community &lt; Organization
67 def blocks_to_expire_cache 67 def blocks_to_expire_cache
68 [MembersBlock] 68 [MembersBlock]
69 end 69 end
  70 +
  71 + def each_member(offset=0)
  72 + while member = self.members.first(:order => :id, :offset => offset)
  73 + yield member
  74 + offset = offset + 1
  75 + end
  76 + end
  77 +
70 end 78 end
app/models/consumption.rb
@@ -1,9 +0,0 @@ @@ -1,9 +0,0 @@
1 -class Consumption < ActiveRecord::Base  
2 - belongs_to :profile  
3 - belongs_to :product_category  
4 -  
5 - validates_uniqueness_of :product_category_id, :scope => :profile_id  
6 -  
7 - xss_terminate :only => [ :aditional_specifications ], :on => 'validation'  
8 -  
9 -end  
app/models/contact_list.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +class ContactList < ActiveRecord::Base
  2 +
  3 + serialize :list, Array
  4 +
  5 + def list
  6 + self[:list] || []
  7 + end
  8 +
  9 + def data
  10 + if self.fetched
  11 + { "fetched" => true, "contact_list" => self.id, "error" => self.error_fetching }
  12 + else
  13 + {}
  14 + end
  15 + end
  16 +
  17 + def register_auth_error
  18 + msg = _('There was an error while authenticating. Did you enter correct login and password?')
  19 + self.update_attributes(:fetched => true, :error_fetching => msg)
  20 + end
  21 +
  22 + def register_error
  23 + msg = _('There was an error while looking for your contact list. Please, try again')
  24 + self.update_attributes(:fetched => true, :error_fetching => msg)
  25 + end
  26 +
  27 +end
app/models/create_community.rb
@@ -6,25 +6,11 @@ class CreateCommunity &lt; Task @@ -6,25 +6,11 @@ class CreateCommunity &lt; Task
6 alias :environment :target 6 alias :environment :target
7 alias :environment= :target= 7 alias :environment= :target=
8 8
9 - serialize :data, Hash  
10 - attr_protected :data  
11 - def data  
12 - self[:data] ||= Hash.new  
13 - end  
14 -  
15 acts_as_having_image 9 acts_as_having_image
16 10
17 DATA_FIELDS = Community.fields + ['name', 'closed'] 11 DATA_FIELDS = Community.fields + ['name', 'closed']
18 -  
19 DATA_FIELDS.each do |field| 12 DATA_FIELDS.each do |field|
20 - # getter  
21 - define_method(field) do  
22 - self.data[field.to_sym]  
23 - end  
24 - # setter  
25 - define_method("#{field}=") do |value|  
26 - self.data[field.to_sym] = value  
27 - end 13 + settings_items field.to_sym
28 end 14 end
29 15
30 def validate 16 def validate
@@ -48,16 +34,30 @@ class CreateCommunity &lt; Task @@ -48,16 +34,30 @@ class CreateCommunity &lt; Task
48 community.add_admin(self.requestor) 34 community.add_admin(self.requestor)
49 end 35 end
50 36
51 - def description  
52 - _('%s wants to create community %s.') % [requestor.name, self.name] 37 + def title
  38 + _("New community")
  39 + end
  40 +
  41 + def icon
  42 + src = image ? image.public_filename(:minor) : '/images/icons-app/community-minor.png'
  43 + {:type => :defined_image, :src => src, :name => name}
  44 + end
  45 +
  46 + def subject
  47 + name
53 end 48 end
54 49
55 - def closing_statement  
56 - data[:closing_statement] 50 + def information
  51 + if description.blank?
  52 + { :message => _('%{requestor} wants to create community %{subject} with no description.') }
  53 + else
  54 + { :message => _('%{requestor} wants to create community %{subject} with this description:<p><em>%{description}</em></p>'),
  55 + :variables => {:description => description} }
  56 + end
57 end 57 end
58 58
59 - def closing_statement= value  
60 - data[:closing_statement] = value 59 + def reject_details
  60 + true
61 end 61 end
62 62
63 # tells if this request was rejected 63 # tells if this request was rejected
@@ -70,8 +70,11 @@ class CreateCommunity &lt; Task @@ -70,8 +70,11 @@ class CreateCommunity &lt; Task
70 self.status == Task::Status::FINISHED 70 self.status == Task::Status::FINISHED
71 end 71 end
72 72
  73 + def target_notification_description
  74 + _('%{requestor} wants to create community %{subject}') % {:requestor => requestor.name, :subject => subject}
  75 + end
  76 +
73 def target_notification_message 77 def target_notification_message
74 - description + "\n\n" +  
75 _("User \"%{user}\" just requested to create community %{community}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :user => self.requestor.name, :community => self.name } 78 _("User \"%{user}\" just requested to create community %{community}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :user => self.requestor.name, :community => self.name }
76 end 79 end
77 80
@@ -82,7 +85,7 @@ class CreateCommunity &lt; Task @@ -82,7 +85,7 @@ class CreateCommunity &lt; Task
82 end 85 end
83 86
84 def task_cancelled_message 87 def task_cancelled_message
85 - _("Your request for registering community %{community} at %{environment} was not approved by the environment administrator. The following explanation was given: \n\n%{explanation}") % { :community => self.name, :environment => self.environment, :explanation => self.closing_statement } 88 + _("Your request for registering community %{community} at %{environment} was not approved by the environment administrator. The following explanation was given: \n\n%{explanation}") % { :community => self.name, :environment => self.environment, :explanation => self.reject_explanation }
86 end 89 end
87 90
88 def task_finished_message 91 def task_finished_message
app/models/create_enterprise.rb
@@ -11,23 +11,9 @@ class CreateEnterprise &lt; Task @@ -11,23 +11,9 @@ class CreateEnterprise &lt; Task
11 N_('Economic activity') 11 N_('Economic activity')
12 N_('Management information') 12 N_('Management information')
13 13
14 - DATA_FIELDS = Enterprise.fields + %w[name identifier region_id reject_explanation]  
15 -  
16 - serialize :data, Hash  
17 - attr_protected :data  
18 - def data  
19 - self[:data] ||= Hash.new  
20 - end  
21 - 14 + DATA_FIELDS = Enterprise.fields + %w[name identifier region_id]
22 DATA_FIELDS.each do |field| 15 DATA_FIELDS.each do |field|
23 - # getter  
24 - define_method(field) do  
25 - self.data[field.to_sym]  
26 - end  
27 - # setter  
28 - define_method("#{field}=") do |value|  
29 - self.data[field.to_sym] = value  
30 - end 16 + settings_items field.to_sym
31 end 17 end
32 18
33 # checks for virtual attributes 19 # checks for virtual attributes
@@ -48,7 +34,6 @@ class CreateEnterprise &lt; Task @@ -48,7 +34,6 @@ class CreateEnterprise &lt; Task
48 34
49 # check for explanation when rejecting 35 # check for explanation when rejecting
50 validates_presence_of :reject_explanation, :if => (lambda { |record| record.status == Task::Status::CANCELLED } ) 36 validates_presence_of :reject_explanation, :if => (lambda { |record| record.status == Task::Status::CANCELLED } )
51 -  
52 xss_terminate :only => [ :acronym, :address, :contact_person, :contact_phone, :economic_activity, :legal_form, :management_information, :name ], :on => 'validation' 37 xss_terminate :only => [ :acronym, :address, :contact_person, :contact_phone, :economic_activity, :legal_form, :management_information, :name ], :on => 'validation'
53 38
54 def validate 39 def validate
@@ -59,7 +44,7 @@ class CreateEnterprise &lt; Task @@ -59,7 +44,7 @@ class CreateEnterprise &lt; Task
59 end 44 end
60 end 45 end
61 46
62 - if self.identifier && Profile.exists?(:identifier => self.identifier) 47 + if self.status != Task::Status::CANCELLED && self.identifier && Profile.exists?(:identifier => self.identifier)
63 self.errors.add(:identifier, '%{fn} is already being as identifier by another enterprise, organization or person.') 48 self.errors.add(:identifier, '%{fn} is already being as identifier by another enterprise, organization or person.')
64 end 49 end
65 end 50 end
@@ -91,7 +76,7 @@ class CreateEnterprise &lt; Task @@ -91,7 +76,7 @@ class CreateEnterprise &lt; Task
91 end 76 end
92 77
93 def environment 78 def environment
94 - region ? region.environment : self.requestor ? self.requestor.environment : Environment.default 79 + requestor.environment
95 end 80 end
96 81
97 def available_regions 82 def available_regions
@@ -153,8 +138,24 @@ class CreateEnterprise &lt; Task @@ -153,8 +138,24 @@ class CreateEnterprise &lt; Task
153 enterprise.add_admin(enterprise.user.person) 138 enterprise.add_admin(enterprise.user.person)
154 end 139 end
155 140
156 - def description  
157 - _('Enterprise registration: "%s"') % self.name 141 + def title
  142 + _("Enterprise registration")
  143 + end
  144 +
  145 + def icon
  146 + {:type => :defined_image, :src => '/images/icons-app/enterprise-minor.png', :name => name}
  147 + end
  148 +
  149 + def subject
  150 + name
  151 + end
  152 +
  153 + def information
  154 + {:message => _('%{requestor} wants to create enterprise %{subject}.')}
  155 + end
  156 +
  157 + def reject_details
  158 + true
158 end 159 end
159 160
160 def task_created_message 161 def task_created_message
@@ -164,18 +165,18 @@ class CreateEnterprise &lt; Task @@ -164,18 +165,18 @@ class CreateEnterprise &lt; Task
164 end 165 end
165 166
166 def task_finished_message 167 def task_finished_message
167 - _('Your request for registering the enterprise "%{enterprise}" was approved. You can access %{environment} now and provide start providing all relevant information your new enterprise.') % { :enterprise => self.name, :environment => self.environment } 168 + __('Your request for registering the enterprise "%{enterprise}" was approved. You can access %{environment} now and provide start providing all relevant information your new enterprise.') % { :enterprise => self.name, :environment => self.environment }
168 end 169 end
169 170
170 def task_cancelled_message 171 def task_cancelled_message
171 - _("Your request for registering the enterprise %{enterprise} at %{environment} was NOT approved by the validator organization. The following explanation was given: \n\n%{explanation}") % { :enterprise => self.name, :environment => self.environment, :explanation => self.reject_explanation } 172 + __("Your request for registering the enterprise %{enterprise} at %{environment} was NOT approved by the validator organization. The following explanation was given: \n\n%{explanation}") % { :enterprise => self.name, :environment => self.environment, :explanation => self.reject_explanation }
172 end 173 end
173 174
174 def target_notification_message 175 def target_notification_message
175 msg = "" 176 msg = ""
176 - msg << _("Enterprise \"%{enterprise}\" just requested to enter %{environment}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :enterprise => self.name, :environment => self.environment } 177 + msg << __("Enterprise \"%{enterprise}\" just requested to enter %{environment}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :enterprise => self.name, :environment => self.environment }
177 msg << "\n" 178 msg << "\n"
178 - msg << _("The data provided by the enterprise was the following:\n") << "\n" 179 + msg << __("The data provided by the enterprise was the following:\n") << "\n"
179 180
180 181
181 msg << (_("Name: %s") % self.name) << "\n" 182 msg << (_("Name: %s") % self.name) << "\n"
@@ -185,16 +186,20 @@ class CreateEnterprise &lt; Task @@ -185,16 +186,20 @@ class CreateEnterprise &lt; Task
185 msg << (_("Foundation Year: %d") % self.foundation_year) << "\n" unless self.foundation_year.blank? 186 msg << (_("Foundation Year: %d") % self.foundation_year) << "\n" unless self.foundation_year.blank?
186 msg << (_("Economic activity: %s") % self.economic_activity) << "\n" 187 msg << (_("Economic activity: %s") % self.economic_activity) << "\n"
187 188
188 - msg << _("Information about enterprise's management:\n") << self.management_information.to_s << "\n" 189 + msg << __("Information about enterprise's management:\n") << self.management_information.to_s << "\n"
189 190
190 msg << (_("Contact phone: %s") % self.contact_phone) << "\n" 191 msg << (_("Contact phone: %s") % self.contact_phone) << "\n"
191 msg << (_("Contact person: %s") % self.contact_person) << "\n" 192 msg << (_("Contact person: %s") % self.contact_person) << "\n"
192 193
193 - msg << _('CreateEnterprise|Identifier') 194 + msg << __('CreateEnterprise|Identifier')
194 195
195 msg 196 msg
196 end 197 end
197 198
  199 + def target_notification_description
  200 + _('%{requestor} wants to create enterprise %{subject}.') % {:requestor => requestor.name, :subject => subject}
  201 + end
  202 +
198 def permission 203 def permission
199 :validate_enterprise 204 :validate_enterprise
200 end 205 end
app/models/disabled_enterprise_message_block.rb
1 class DisabledEnterpriseMessageBlock < Block 1 class DisabledEnterpriseMessageBlock < Block
2 2
3 def self.description 3 def self.description
4 - _('"Disabled enterprise" message') 4 + __('"Disabled enterprise" message')
5 end 5 end
6 6
7 def help 7 def help
8 - _('Shows a message for disabled enterprises.') 8 + __('Shows a message for disabled enterprises.')
9 end 9 end
10 10
11 def default_title 11 def default_title
app/models/email_activation.rb
@@ -11,8 +11,20 @@ class EmailActivation &lt; Task @@ -11,8 +11,20 @@ class EmailActivation &lt; Task
11 end 11 end
12 end 12 end
13 13
14 - def description  
15 - _("'%{user} wants to activate email '%{email}'") % { :user => person.name, :email => person.email_addresses.join(', ') } 14 + def title
  15 + _("Email activation")
  16 + end
  17 +
  18 + def subject
  19 + person.email_addresses.join(', ')
  20 + end
  21 +
  22 + def information
  23 + {:message => _("%{requestor} wants to activate the following email: %{subject}.")}
  24 + end
  25 +
  26 + def icon
  27 + {:type => :profile_image, :profile => requestor, :url => requestor.url}
16 end 28 end
17 29
18 def perform 30 def perform
app/models/enterprise.rb
@@ -4,7 +4,8 @@ class Enterprise &lt; Organization @@ -4,7 +4,8 @@ class Enterprise &lt; Organization
4 4
5 N_('Enterprise') 5 N_('Enterprise')
6 6
7 - has_many :products, :dependent => :destroy 7 + has_many :products, :dependent => :destroy, :order => 'name ASC'
  8 + has_many :inputs, :through => :products
8 9
9 extra_data_for_index :product_categories 10 extra_data_for_index :product_categories
10 11
@@ -51,6 +52,10 @@ class Enterprise &lt; Organization @@ -51,6 +52,10 @@ class Enterprise &lt; Organization
51 environment ? environment.active_enterprise_fields : [] 52 environment ? environment.active_enterprise_fields : []
52 end 53 end
53 54
  55 + def highlighted_products_with_image(options = {})
  56 + Product.find(:all, {:conditions => {:highlighted => true}, :joins => :image}.merge(options))
  57 + end
  58 +
54 def required_fields 59 def required_fields
55 environment ? environment.required_enterprise_fields : [] 60 environment ? environment.required_enterprise_fields : []
56 end 61 end
@@ -117,17 +122,28 @@ class Enterprise &lt; Organization @@ -117,17 +122,28 @@ class Enterprise &lt; Organization
117 end 122 end
118 123
119 def default_set_of_blocks 124 def default_set_of_blocks
  125 + links = [
  126 + {:name => _("Enterprises's profile"), :address => '/profile/{profile}', :icon => 'ok'},
  127 + {:name => _('Blog'), :address => '/{profile}/blog', :icon => 'edit'},
  128 + {:name => _('Products'), :address => '/catalog/{profile}', :icon => 'new'},
  129 + ]
120 blocks = [ 130 blocks = [
121 - [MainBlock],  
122 - [ProfileInfoBlock, MembersBlock],  
123 - [RecentDocumentsBlock] 131 + [MainBlock.new],
  132 + [ProfileImageBlock.new, LinkListBlock.new(:links => links)],
  133 + []
124 ] 134 ]
125 if !environment.enabled?('disable_products_for_enterprises') 135 if !environment.enabled?('disable_products_for_enterprises')
126 - blocks[2].unshift ProductsBlock 136 + blocks[2].unshift ProductsBlock.new
127 end 137 end
128 blocks 138 blocks
129 end 139 end
130 140
  141 + def default_set_of_articles
  142 + [
  143 + Blog.new(:name => _('Blog')),
  144 + ]
  145 + end
  146 +
131 before_create do |enterprise| 147 before_create do |enterprise|
132 if enterprise.environment.enabled?('enterprises_are_disabled_when_created') 148 if enterprise.environment.enabled?('enterprises_are_disabled_when_created')
133 enterprise.enabled = false 149 enterprise.enabled = false
@@ -149,10 +165,4 @@ class Enterprise &lt; Organization @@ -149,10 +165,4 @@ class Enterprise &lt; Organization
149 enable_contact_us 165 enable_contact_us
150 end 166 end
151 167
152 - protected  
153 -  
154 - def default_homepage(attrs)  
155 - EnterpriseHomepage.new(attrs)  
156 - end  
157 -  
158 end 168 end
app/models/enterprise_activation.rb
@@ -2,7 +2,6 @@ class EnterpriseActivation &lt; Task @@ -2,7 +2,6 @@ class EnterpriseActivation &lt; Task
2 2
3 class RequestorRequired < Exception; end 3 class RequestorRequired < Exception; end
4 4
5 - acts_as_having_settings :field => :data  
6 settings_items :enterprise_id, :integer 5 settings_items :enterprise_id, :integer
7 6
8 validates_presence_of :enterprise_id 7 validates_presence_of :enterprise_id
@@ -20,4 +19,20 @@ class EnterpriseActivation &lt; Task @@ -20,4 +19,20 @@ class EnterpriseActivation &lt; Task
20 self.enterprise.enable(requestor) 19 self.enterprise.enable(requestor)
21 end 20 end
22 21
  22 + def title
  23 + _("Enterprise activation")
  24 + end
  25 +
  26 + def linked_subject
  27 + {:text => target.name, :url => target.public_profile_url}
  28 + end
  29 +
  30 + def information
  31 + {:message => _('%{requestor} wants to activate enterprise %{linked_subject}.')}
  32 + end
  33 +
  34 + def icon
  35 + {:type => :profile_image, :profile => requestor, :url => requestor.url}
  36 + end
  37 +
23 end 38 end
app/models/enterprise_homepage.rb
1 class EnterpriseHomepage < Article 1 class EnterpriseHomepage < Article
2 2
3 def self.short_description 3 def self.short_description
4 - _('Enterprise homepage.') 4 + __('Enterprise homepage.')
5 end 5 end
6 6
7 def self.description 7 def self.description
app/models/enterprises_block.rb
@@ -28,29 +28,8 @@ class EnterprisesBlock &lt; ProfileListBlock @@ -28,29 +28,8 @@ class EnterprisesBlock &lt; ProfileListBlock
28 end 28 end
29 end 29 end
30 30
31 - def profile_count  
32 - if owner.kind_of?(Environment)  
33 - owner.enterprises.count(:conditions => { :visible => true })  
34 - else  
35 - owner.enterprises(:visible => true).count  
36 - end  
37 -  
38 - end  
39 -  
40 - def profile_finder  
41 - @profile_finder ||= EnterprisesBlock::Finder.new(self)  
42 - end  
43 -  
44 - class Finder < ProfileListBlock::Finder  
45 - def ids  
46 - # FIXME when owner is an environment (i.e. listing enterprises globally  
47 - # this can become SLOW)  
48 - if block.owner.kind_of?(Environment)  
49 - block.owner.enterprises.all(:conditions => {:visible => true}, :limit => block.limit, :order => 'random()').map(&:id)  
50 - else  
51 - block.owner.enterprises.select(&:visible).map(&:id)  
52 - end  
53 - end 31 + def profiles
  32 + owner.enterprises
54 end 33 end
55 34
56 end 35 end
app/models/environment.rb
@@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
3 # domains. 3 # domains.
4 class Environment < ActiveRecord::Base 4 class Environment < ActiveRecord::Base
5 5
  6 + has_many :users
  7 +
6 self.partial_updates = false 8 self.partial_updates = false
7 9
8 has_many :tasks, :dependent => :destroy, :as => 'target' 10 has_many :tasks, :dependent => :destroy, :as => 'target'
@@ -14,6 +16,7 @@ class Environment &lt; ActiveRecord::Base @@ -14,6 +16,7 @@ class Environment &lt; ActiveRecord::Base
14 'manage_environment_categories' => N_('Manage environment categories'), 16 'manage_environment_categories' => N_('Manage environment categories'),
15 'manage_environment_roles' => N_('Manage environment roles'), 17 'manage_environment_roles' => N_('Manage environment roles'),
16 'manage_environment_validators' => N_('Manage environment validators'), 18 'manage_environment_validators' => N_('Manage environment validators'),
  19 + 'manage_environment_users' => N_('Manage environment users'),
17 } 20 }
18 21
19 module Roles 22 module Roles
@@ -42,9 +45,7 @@ class Environment &lt; ActiveRecord::Base @@ -42,9 +45,7 @@ class Environment &lt; ActiveRecord::Base
42 :name => N_('Member'), 45 :name => N_('Member'),
43 :environment => self, 46 :environment => self,
44 :permissions => [ 47 :permissions => [
45 - 'edit_profile',  
46 - 'post_content',  
47 - 'manage_products' 48 + 'invite_members',
48 ] 49 ]
49 ) 50 )
50 # moderators for enterprises, communities etc 51 # moderators for enterprises, communities etc
@@ -67,7 +68,7 @@ class Environment &lt; ActiveRecord::Base @@ -67,7 +68,7 @@ class Environment &lt; ActiveRecord::Base
67 end 68 end
68 69
69 def admins 70 def admins
70 - self.members_by_role(Environment::Roles.admin(self.id)) 71 + Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', Environment::Roles.admin(self).id])
71 end 72 end
72 73
73 # returns the available features for a Environment, in the form of a 74 # returns the available features for a Environment, in the form of a
@@ -80,7 +81,7 @@ class Environment &lt; ActiveRecord::Base @@ -80,7 +81,7 @@ class Environment &lt; ActiveRecord::Base
80 'disable_asset_communities' => __('Disable search for communities'), 81 'disable_asset_communities' => __('Disable search for communities'),
81 'disable_asset_products' => _('Disable search for products'), 82 'disable_asset_products' => _('Disable search for products'),
82 'disable_asset_events' => _('Disable search for events'), 83 'disable_asset_events' => _('Disable search for events'),
83 - 'disable_products_for_enterprises' => _('Disable products for enterprises'), 84 + 'disable_products_for_enterprises' => __('Disable products for enterprises'),
84 'disable_categories' => _('Disable categories'), 85 'disable_categories' => _('Disable categories'),
85 'disable_cms' => _('Disable CMS'), 86 'disable_cms' => _('Disable CMS'),
86 'disable_header_and_footer' => _('Disable header/footer editing by users'), 87 'disable_header_and_footer' => _('Disable header/footer editing by users'),
@@ -89,14 +90,12 @@ class Environment &lt; ActiveRecord::Base @@ -89,14 +90,12 @@ class Environment &lt; ActiveRecord::Base
89 'disable_select_city_for_contact' => _('Disable state/city select for contact form'), 90 'disable_select_city_for_contact' => _('Disable state/city select for contact form'),
90 'disable_contact_person' => _('Disable contact for people'), 91 'disable_contact_person' => _('Disable contact for people'),
91 'disable_contact_community' => _('Disable contact for groups/communities'), 92 'disable_contact_community' => _('Disable contact for groups/communities'),
92 - 'enterprise_registration' => _('Enterprise registration'),  
93 - 'join_community_popup' => _('Ask users to join a group/community with a popup'), 93 + 'enterprise_registration' => __('Enterprise registration'),
94 94
95 - 'enterprise_activation' => _('Enable activation of enterprises'), 95 + 'enterprise_activation' => __('Enable activation of enterprises'),
96 'wysiwyg_editor_for_environment_home' => _('Use WYSIWYG editor to edit environment home page'), 96 'wysiwyg_editor_for_environment_home' => _('Use WYSIWYG editor to edit environment home page'),
97 'media_panel' => _('Media panel in WYSIWYG editor'), 97 'media_panel' => _('Media panel in WYSIWYG editor'),
98 'select_preferred_domain' => _('Select preferred domains per profile'), 98 'select_preferred_domain' => _('Select preferred domains per profile'),
99 - 'display_wizard_signup' => _('Display wizard signup'),  
100 'use_portal_community' => _('Use the portal as news source for front page'), 99 'use_portal_community' => _('Use the portal as news source for front page'),
101 'user_themes' => _('Allow users to create their own themes'), 100 'user_themes' => _('Allow users to create their own themes'),
102 'search_in_home' => _("Display search form in home page"), 101 'search_in_home' => _("Display search form in home page"),
@@ -107,7 +106,10 @@ class Environment &lt; ActiveRecord::Base @@ -107,7 +106,10 @@ class Environment &lt; ActiveRecord::Base
107 'organizations_are_moderated_by_default' => _("Organizations have moderated publication by default"), 106 'organizations_are_moderated_by_default' => _("Organizations have moderated publication by default"),
108 'enable_organization_url_change' => _("Allow organizations to change their URL"), 107 'enable_organization_url_change' => _("Allow organizations to change their URL"),
109 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"), 108 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"),
110 - 'enterprises_are_disabled_when_created' => _('Enterprises are disabled when created'), 109 + 'enterprises_are_disabled_when_created' => __('Enterprises are disabled when created'),
  110 + 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'),
  111 + 'xmpp_chat' => _('XMPP/Jabber based chat'),
  112 + 'show_zoom_button_on_article_images' => _('Show a zoom link on all article images')
111 } 113 }
112 end 114 end
113 115
@@ -131,7 +133,8 @@ class Environment &lt; ActiveRecord::Base @@ -131,7 +133,8 @@ class Environment &lt; ActiveRecord::Base
131 env.boxes[1].blocks << RecentDocumentsBlock.new 133 env.boxes[1].blocks << RecentDocumentsBlock.new
132 134
133 # "right" area 135 # "right" area
134 - env.boxes[2].blocks << ProfileListBlock.new 136 + env.boxes[2].blocks << CommunitiesBlock.new(:limit => 6)
  137 + env.boxes[2].blocks << PeopleBlock.new(:limit => 6)
135 end 138 end
136 139
137 # One Environment can be reached by many domains 140 # One Environment can be reached by many domains
@@ -147,10 +150,16 @@ class Environment &lt; ActiveRecord::Base @@ -147,10 +150,16 @@ class Environment &lt; ActiveRecord::Base
147 has_many :categories 150 has_many :categories
148 has_many :display_categories, :class_name => 'Category', :conditions => 'display_color is not null and parent_id is null', :order => 'display_color' 151 has_many :display_categories, :class_name => 'Category', :conditions => 'display_color is not null and parent_id is null', :order => 'display_color'
149 152
  153 + has_many :product_categories, :conditions => { :type => 'ProductCategory'}
150 has_many :regions 154 has_many :regions
151 155
152 has_many :roles 156 has_many :roles
153 157
  158 + has_many :qualifiers
  159 + has_many :certifiers
  160 +
  161 + has_many :mailings, :class_name => 'EnvironmentMailing', :foreign_key => :source_id, :as => 'source'
  162 +
154 acts_as_accessible 163 acts_as_accessible
155 164
156 def superior_intances 165 def superior_intances
@@ -198,16 +207,21 @@ class Environment &lt; ActiveRecord::Base @@ -198,16 +207,21 @@ class Environment &lt; ActiveRecord::Base
198 settings_items :location, :type => String 207 settings_items :location, :type => String
199 settings_items :layout_template, :type => String, :default => 'default' 208 settings_items :layout_template, :type => String, :default => 'default'
200 settings_items :homepage, :type => String 209 settings_items :homepage, :type => String
201 - settings_items :description, :type => String  
202 - settings_items :category_types, :type => Array, :default => ['Category'] 210 + settings_items :description, :type => String, :default => '<div style="text-align: center"><a href="http://noosfero.org/"><img src="/images/noosfero-network.png" alt="Noosfero"/></a></div>'
203 settings_items :enable_ssl 211 settings_items :enable_ssl
204 - settings_items :theme, :type => String, :default => 'default'  
205 - settings_items :icon_theme, :type => String, :default => 'default'  
206 settings_items :local_docs, :type => Array, :default => [] 212 settings_items :local_docs, :type => Array, :default => []
207 settings_items :news_amount_by_folder, :type => Integer, :default => 4 213 settings_items :news_amount_by_folder, :type => Integer, :default => 4
208 settings_items :help_message_to_add_enterprise, :type => String, :default => '' 214 settings_items :help_message_to_add_enterprise, :type => String, :default => ''
209 settings_items :tip_message_enterprise_activation_question, :type => String, :default => '' 215 settings_items :tip_message_enterprise_activation_question, :type => String, :default => ''
210 216
  217 + settings_items :currency_unit, :type => String, :default => '$'
  218 + settings_items :currency_separator, :type => String, :default => '.'
  219 + settings_items :currency_delimiter, :type => String, :default => ','
  220 +
  221 + settings_items :trusted_sites_for_iframe, :type => Array, :default => ['itheora.org', 'tv.softwarelivre.org', 'stream.softwarelivre.org']
  222 +
  223 + settings_items :enabled_plugins, :type => Array, :default => []
  224 +
211 def news_amount_by_folder=(amount) 225 def news_amount_by_folder=(amount)
212 settings[:news_amount_by_folder] = amount.to_i 226 settings[:news_amount_by_folder] = amount.to_i
213 end 227 end
@@ -241,6 +255,29 @@ class Environment &lt; ActiveRecord::Base @@ -241,6 +255,29 @@ class Environment &lt; ActiveRecord::Base
241 end 255 end
242 end 256 end
243 257
  258 + def enabled_features
  259 + features = self.class.available_features
  260 + features.delete_if{ |k, v| !self.enabled?(k) }
  261 + end
  262 +
  263 + before_create :enable_default_features
  264 + def enable_default_features
  265 + %w(
  266 + disable_asset_products
  267 + disable_gender_icon
  268 + disable_products_for_enterprises
  269 + disable_select_city_for_contact
  270 + enterprise_registration
  271 + media_panel
  272 + organizations_are_moderated_by_default
  273 + show_balloon_with_profile_links_when_clicked
  274 + use_portal_community
  275 + wysiwyg_editor_for_environment_home
  276 + ).each do |feature|
  277 + enable(feature)
  278 + end
  279 + end
  280 +
244 # returns <tt>true</tt> if this Environment has terms of use to be 281 # returns <tt>true</tt> if this Environment has terms of use to be
245 # accepted by users before registration. 282 # accepted by users before registration.
246 def has_terms_of_use? 283 def has_terms_of_use?
@@ -457,6 +494,10 @@ class Environment &lt; ActiveRecord::Base @@ -457,6 +494,10 @@ class Environment &lt; ActiveRecord::Base
457 494
458 xss_terminate :only => [ :message_for_disabled_enterprise ], :with => 'white_list', :on => 'validation' 495 xss_terminate :only => [ :message_for_disabled_enterprise ], :with => 'white_list', :on => 'validation'
459 496
  497 + validates_presence_of :theme
  498 +
  499 + include WhiteListFilter
  500 + filter_iframes :message_for_disabled_enterprise, :whitelist => lambda { trusted_sites_for_iframe }
460 501
461 # ################################################# 502 # #################################################
462 # Business logic in general 503 # Business logic in general
@@ -525,7 +566,29 @@ class Environment &lt; ActiveRecord::Base @@ -525,7 +566,29 @@ class Environment &lt; ActiveRecord::Base
525 end 566 end
526 567
527 def themes=(values) 568 def themes=(values)
528 - settings[:themes] = values.map(&:id) 569 + settings[:themes] = values
  570 + end
  571 +
  572 + def add_themes(values)
  573 + if settings[:themes].nil?
  574 + self.themes = values
  575 + else
  576 + settings[:themes] += values
  577 + end
  578 + end
  579 +
  580 + before_create do |env|
  581 + env.settings[:themes] ||= %w[
  582 + aluminium
  583 + butter
  584 + chameleon
  585 + chocolate
  586 + noosfero
  587 + orange
  588 + plum
  589 + scarletred
  590 + skyblue
  591 + ]
529 end 592 end
530 593
531 def community_template 594 def community_template
@@ -589,7 +652,7 @@ class Environment &lt; ActiveRecord::Base @@ -589,7 +652,7 @@ class Environment &lt; ActiveRecord::Base
589 end 652 end
590 653
591 def portal_folders 654 def portal_folders
592 - (settings[:portal_folders] || []).map{|fid| portal_community.articles.find(fid) } 655 + (settings[:portal_folders] || []).map{|fid| portal_community.articles.find(:first, :conditions => { :id => fid }) }.compact
593 end 656 end
594 657
595 def portal_folders=(folders) 658 def portal_folders=(folders)
@@ -628,4 +691,17 @@ class Environment &lt; ActiveRecord::Base @@ -628,4 +691,17 @@ class Environment &lt; ActiveRecord::Base
628 end 691 end
629 end 692 end
630 693
  694 + def highlighted_products_with_image(options = {})
  695 + Product.find(:all, {:conditions => {:highlighted => true, :enterprise_id => self.enterprises.find(:all, :select => :id) }, :joins => :image}.merge(options))
  696 + end
  697 +
  698 + settings_items :home_cache_in_minutes, :type => :integer, :default => 5
  699 + settings_items :general_cache_in_minutes, :type => :integer, :default => 15
  700 + settings_items :profile_cache_in_minutes, :type => :integer, :default => 15
  701 +
  702 + def image_galleries
  703 + portal_community ? portal_community.image_galleries : []
  704 + end
  705 +
631 end 706 end
  707 +
app/models/environment_mailing.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +class EnvironmentMailing < Mailing
  2 +
  3 + def recipients(offset=0, limit=100)
  4 + source.people.all(:order => :id, :offset => offset, :limit => limit, :joins => "LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)", :conditions => { "m.person_id" => nil })
  5 + end
  6 +
  7 + def each_recipient
  8 + offset = 0
  9 + limit = 100
  10 + while !(people = recipients(offset, limit)).empty?
  11 + people.each do |person|
  12 + yield person
  13 + end
  14 + offset = offset + limit
  15 + end
  16 + end
  17 +
  18 + def signature_message
  19 + _('Sent by %s.') % source.name
  20 + end
  21 +
  22 + def url
  23 + source.top_url
  24 + end
  25 +end
app/models/environment_statistics_block.rb
@@ -17,11 +17,12 @@ class EnvironmentStatisticsBlock &lt; Block @@ -17,11 +17,12 @@ class EnvironmentStatisticsBlock &lt; Block
17 enterprises = owner.enterprises.visible.count 17 enterprises = owner.enterprises.visible.count
18 communities = owner.communities.visible.count 18 communities = owner.communities.visible.count
19 19
20 - info = [  
21 - n_('One user', '%{num} users', users) % { :num => users },  
22 - n__('One enterprise', '%{num} enterprises', enterprises) % { :num => enterprises },  
23 - n__('One community', '%{num} communities', communities) % { :num => communities },  
24 - ] 20 + info = []
  21 + info << (n_('One user', '%{num} users', users) % { :num => users })
  22 + unless owner.enabled?('disable_asset_enterprises')
  23 + info << (n__('One enterprise', '%{num} enterprises', enterprises) % { :num => enterprises })
  24 + end
  25 + info << (n__('One community', '%{num} communities', communities) % { :num => communities })
25 26
26 block_title(title) + content_tag('ul', info.map {|item| content_tag('li', item) }.join("\n")) 27 block_title(title) + content_tag('ul', info.map {|item| content_tag('li', item) }.join("\n"))
27 end 28 end
app/models/event.rb
1 class Event < Article 1 class Event < Article
2 2
3 - acts_as_having_settings :field => :body  
4 -  
5 - settings_items :description, :type => :string  
6 - settings_items :link, :type => :string  
7 settings_items :address, :type => :string 3 settings_items :address, :type => :string
8 4
  5 + def link=(value)
  6 + self.setting[:link] = maybe_add_http(value)
  7 + end
  8 +
  9 + def link
  10 + maybe_add_http(self.setting[:link])
  11 + end
  12 +
9 xss_terminate :only => [ :link ], :on => 'validation' 13 xss_terminate :only => [ :link ], :on => 'validation'
10 - xss_terminate :only => [ :description, :link, :address ], :with => 'white_list', :on => 'validation' 14 + xss_terminate :only => [ :body, :link, :address ], :with => 'white_list', :on => 'validation'
  15 +
  16 + def initialize(*args)
  17 + super(*args)
  18 + self.start_date ||= Date.today
  19 + end
11 20
12 validates_presence_of :title, :start_date 21 validates_presence_of :title, :start_date
13 22
@@ -21,6 +30,9 @@ class Event &lt; Article @@ -21,6 +30,9 @@ class Event &lt; Article
21 {:conditions => ['start_date = :date AND end_date IS NULL OR (start_date <= :date AND end_date >= :date)', {:date => date}]} 30 {:conditions => ['start_date = :date AND end_date IS NULL OR (start_date <= :date AND end_date >= :date)', {:date => date}]}
22 } 31 }
23 32
  33 + include WhiteListFilter
  34 + filter_iframes :body, :link, :address, :whitelist => lambda { profile && profile.environment && profile.environment.trusted_sites_for_iframe }
  35 +
24 def self.description 36 def self.description
25 _('A calendar event') 37 _('A calendar event')
26 end 38 end
@@ -29,7 +41,7 @@ class Event &lt; Article @@ -29,7 +41,7 @@ class Event &lt; Article
29 _('Event') 41 _('Event')
30 end 42 end
31 43
32 - def icon_name 44 + def self.icon_name(article = nil)
33 'event' 45 'event'
34 end 46 end
35 47
@@ -88,26 +100,27 @@ class Event &lt; Article @@ -88,26 +100,27 @@ class Event &lt; Article
88 } 100 }
89 } 101 }
90 102
91 - if self.description 103 + if self.body
92 html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description') 104 html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description')
93 end 105 end
94 } 106 }
95 107
96 - if self.description  
97 - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.description) 108 + if self.body
  109 + result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.body)
98 end 110 end
99 111
100 result 112 result
101 end 113 end
102 114
103 - def link=(value)  
104 - self.body[:link] = maybe_add_http(value) 115 + def event?
  116 + true
105 end 117 end
106 118
107 - def link  
108 - maybe_add_http(self.body[:link]) 119 + def tiny_mce?
  120 + true
109 end 121 end
110 122
  123 + include Noosfero::TranslatableContent
111 include MaybeAddHttp 124 include MaybeAddHttp
112 125
113 end 126 end
app/models/favorite_enterprises_block.rb
@@ -9,7 +9,7 @@ class FavoriteEnterprisesBlock &lt; ProfileListBlock @@ -9,7 +9,7 @@ class FavoriteEnterprisesBlock &lt; ProfileListBlock
9 end 9 end
10 10
11 def self.description 11 def self.description
12 - __('Favorite enterprises') 12 + __('Favorite Enterprises')
13 end 13 end
14 14
15 def footer 15 def footer
@@ -20,18 +20,8 @@ class FavoriteEnterprisesBlock &lt; ProfileListBlock @@ -20,18 +20,8 @@ class FavoriteEnterprisesBlock &lt; ProfileListBlock
20 end 20 end
21 end 21 end
22 22
23 - def profile_count  
24 - owner.favorite_enterprises.count  
25 - end  
26 -  
27 - def profile_finder  
28 - @profile_finder ||= FavoriteEnterprisesBlock::Finder.new(self)  
29 - end  
30 -  
31 - class Finder < ProfileListBlock::Finder  
32 - def ids  
33 - block.owner.favorite_enterprises.map(&:id)  
34 - end 23 + def profiles
  24 + owner.favorite_enterprises
35 end 25 end
36 26
37 end 27 end
app/models/featured_products_block.rb 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +class FeaturedProductsBlock < Block
  2 +
  3 + settings_items :product_ids, :type => Array, :default => []
  4 + settings_items :groups_of, :type => :integer, :default => 3
  5 + settings_items :speed, :type => :integer, :default => 1000
  6 + settings_items :reflect, :type => :boolean, :default => true
  7 +
  8 + before_save do |block|
  9 + if block.owner.kind_of?(Environment) && block.product_ids.blank?
  10 + seed = block.owner.products.count
  11 + block.product_ids = block.owner.highlighted_products_with_image(:offset => (rand(seed) % (seed - block.groups_of * 3)), :limit => block.groups_of * 3).map(&:id)
  12 + end
  13 + block.groups_of = block.groups_of.to_i
  14 + end
  15 +
  16 + def self.description
  17 + _('Featured Products')
  18 + end
  19 +
  20 + def products
  21 + Product.find(self.product_ids) || []
  22 + end
  23 +
  24 + def products_for_selection
  25 + self.owner.highlighted_products_with_image
  26 + end
  27 +
  28 + def content
  29 + block = self
  30 + lambda do
  31 + render :file => 'blocks/featured_products', :locals => { :block => block }
  32 + end
  33 + end
  34 +
  35 +end
app/models/feed_reader_block.rb
@@ -12,7 +12,7 @@ class FeedReaderBlock &lt; Block @@ -12,7 +12,7 @@ class FeedReaderBlock &lt; Block
12 def address=(new_address) 12 def address=(new_address)
13 old_address = address 13 old_address = address
14 orig_set_address(new_address) 14 orig_set_address(new_address)
15 - self.enabled = (old_address.blank? && !new_address.blank?) || (new_address && new_address != old_address) || false 15 + self.enabled = (new_address && new_address != old_address) || (new_address && self.enabled) || false
16 end 16 end
17 17
18 settings_items :limit, :type => :integer 18 settings_items :limit, :type => :integer
app/models/folder.rb
@@ -2,19 +2,10 @@ class Folder &lt; Article @@ -2,19 +2,10 @@ class Folder &lt; Article
2 2
3 acts_as_having_settings :field => :setting 3 acts_as_having_settings :field => :setting
4 4
5 - settings_items :view_as, :type => :string, :default => 'folder'  
6 -  
7 xss_terminate :only => [ :body ], :with => 'white_list', :on => 'validation' 5 xss_terminate :only => [ :body ], :with => 'white_list', :on => 'validation'
8 6
9 - def self.select_views  
10 - [[_('Folder'), 'folder'], [_('Image gallery'), 'image_gallery']]  
11 - end  
12 -  
13 - def self.views  
14 - select_views.map(&:last)  
15 - end  
16 -  
17 - validates_inclusion_of :view_as, :in => self.views 7 + include WhiteListFilter
  8 + filter_iframes :body, :whitelist => lambda { profile && profile.environment && profile.environment.trusted_sites_for_iframe }
18 9
19 def self.short_description 10 def self.short_description
20 _('Folder') 11 _('Folder')
@@ -24,37 +15,22 @@ class Folder &lt; Article @@ -24,37 +15,22 @@ class Folder &lt; Article
24 _('A folder, inside which you can put other articles.') 15 _('A folder, inside which you can put other articles.')
25 end 16 end
26 17
27 - def icon_name 18 + def self.icon_name(article = nil)
28 'folder' 19 'folder'
29 end 20 end
30 21
31 - 22 + include ActionView::Helpers::TagHelper
32 def to_html(options = {}) 23 def to_html(options = {})
33 - send(view_as)  
34 - end  
35 -  
36 - def folder  
37 folder = self 24 folder = self
38 lambda do 25 lambda do
39 render :file => 'content_viewer/folder', :locals => { :folder => folder } 26 render :file => 'content_viewer/folder', :locals => { :folder => folder }
40 end 27 end
41 end 28 end
42 29
43 - def image_gallery  
44 - article = self  
45 - lambda do  
46 - render :file => 'content_viewer/image_gallery', :locals => {:article => article}  
47 - end  
48 - end  
49 -  
50 def folder? 30 def folder?
51 true 31 true
52 end 32 end
53 33
54 - def display_as_gallery?  
55 - view_as == 'image_gallery'  
56 - end  
57 -  
58 def can_display_hits? 34 def can_display_hits?
59 false 35 false
60 end 36 end
@@ -70,7 +46,5 @@ class Folder &lt; Article @@ -70,7 +46,5 @@ class Folder &lt; Article
70 has_many :images, :class_name => 'Article', 46 has_many :images, :class_name => 'Article',
71 :foreign_key => 'parent_id', 47 :foreign_key => 'parent_id',
72 :order => 'articles.type, articles.name', 48 :order => 'articles.type, articles.name',
73 - :include => :reference_article,  
74 - :conditions => ["articles.type = 'UploadedFile' and articles.content_type in (?) or articles.type = 'Folder' or (articles.type = 'PublishedArticle' and reference_articles_articles.type = 'UploadedFile' and reference_articles_articles.content_type in (?))", UploadedFile.content_types, UploadedFile.content_types]  
75 - 49 + :conditions => ["articles.type = 'UploadedFile' and articles.content_type in (?) or articles.type in ('Folder','Gallery')", UploadedFile.content_types]
76 end 50 end
app/models/forum.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +class Forum < Folder
  2 +
  3 + acts_as_having_posts :order => 'updated_at DESC'
  4 +
  5 + def self.short_description
  6 + _('Forum')
  7 + end
  8 +
  9 + def self.description
  10 + _('An internet forum, also called message board, where discussions can be held.')
  11 + end
  12 +
  13 + include ActionView::Helpers::TagHelper
  14 + def to_html(options = {})
  15 + lambda do
  16 + render :file => 'content_viewer/forum_page'
  17 + end
  18 + end
  19 +
  20 + def forum?
  21 + true
  22 + end
  23 +
  24 + def self.icon_name(article = nil)
  25 + 'forum'
  26 + end
  27 +end
app/models/friends_block.rb
@@ -19,18 +19,8 @@ class FriendsBlock &lt; ProfileListBlock @@ -19,18 +19,8 @@ class FriendsBlock &lt; ProfileListBlock
19 end 19 end
20 end 20 end
21 21
22 - class FriendsBlock::Finder < ProfileListBlock::Finder  
23 - def ids  
24 - self.block.owner.friend_ids  
25 - end  
26 - end  
27 -  
28 - def profile_finder  
29 - @profile_finder ||= FriendsBlock::Finder.new(self)  
30 - end  
31 -  
32 - def profile_count  
33 - owner.friends.visible.count 22 + def profiles
  23 + owner.friends
34 end 24 end
35 25
36 end 26 end