Commit 55f22c3ea607c019bd260c753e50d87ebecfa690

Authored by Paulo Meireles
2 parents 0e8830f9 1aeb0d56

[Mezuro] Merge branch 'master' into mezuro

Showing 195 changed files with 74645 additions and 67678 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 195 files displayed.

@@ -6,45 +6,144 @@ noosfero, that's not a problem). @@ -6,45 +6,144 @@ noosfero, that's not a problem).
6 Developers 6 Developers
7 ========== 7 ==========
8 8
  9 +Alan Freihof Tygel <alantygel@gmail.com>
  10 +Alessandro Palmeira <alessandro.palmeira@gmail.com>
  11 +Alessandro Palmeira + Caio C. Salgado <alessandro.palmeira@gmail.com>
  12 +Alessandro Palmeira + Caio Salgado <alessandro.palmeira@gmail.com>
  13 +Alessandro Palmeira + Caio Salgado <caio.csalgado@gmail.com>
  14 +Alessandro Palmeira + Caio Salgado + Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com>
  15 +Alessandro Palmeira + Carlos Morais <alessandro.palmeira@gmail.com>
  16 +Alessandro Palmeira + Diego Araújo <alessandro.palmeira@gmail.com>
  17 +Alessandro Palmeira + Diego Araújo <diegoamc90@gmail.com>
  18 +Alessandro Palmeira + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
  19 +Alessandro Palmeira + Diego Araújo + Pedro Leal + João M. M. da Silva <diegoamc90@gmail.com>
  20 +Alessandro Palmeira + Diego Araujo + Rafael Manzo <alessandro.palmeira@gmail.com>
  21 +Alessandro Palmeira + Jefferson Fernandes <alessandro.palmeira@gmail.com>
  22 +Alessandro Palmeira + João M. M. da Silva <alessandro.palmeira@gmail.com>
  23 +Alessandro Palmeira + João M. M. da Silva + Renan Teruo <alessandro.palmeira@gmail.com>
  24 +Alessandro Palmeira + João M. M. Silva <alessandro.palmeira@gmail.com>
  25 +Alessandro Palmeira + Paulo Meirelles <alessandro.palmeira@gmail.com>
  26 +Alessandro Palmeira + Rafael Manzo <alessandro.palmeira@gmail.com>
9 Antonio Terceiro + Carlos Morais <terceiro@colivre.coop.br> 27 Antonio Terceiro + Carlos Morais <terceiro@colivre.coop.br>
10 Antonio Terceiro + Paulo Meirelles <terceiro@colivre.coop.br> 28 Antonio Terceiro + Paulo Meirelles <terceiro@colivre.coop.br>
11 Antonio Terceiro <terceiro@colivre.coop.br> 29 Antonio Terceiro <terceiro@colivre.coop.br>
12 Aurelio A. Heckert <aurelio@colivre.coop.br> 30 Aurelio A. Heckert <aurelio@colivre.coop.br>
13 Braulio Bhavamitra <brauliobo@gmail.com> 31 Braulio Bhavamitra <brauliobo@gmail.com>
14 Bráulio Bhavamitra <brauliobo@gmail.com> 32 Bráulio Bhavamitra <brauliobo@gmail.com>
  33 +Caio <caio.csalgado@gmail.com>
  34 +Caio + Diego + Pedro + João <caio.csalgado@gmail.com>
  35 +Caio, Pedro <caio.csalgado@gmail.com>
  36 +Caio Salgado + Alessandro Palmeira <caio.csalgado@gmail.com>
  37 +Caio Salgado <caio.csalgado@gmail.com>
  38 +Caio Salgado + Carlos Morais + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
  39 +Caio Salgado + Diego Araujo <caio.csalgado@gmail.com>
  40 +Caio Salgado + Diego Araújo <caio.csalgado@gmail.com>
  41 +Caio Salgado + Diego Araújo <diegoamc90@gmail.com>
  42 +Caio Salgado + Diego Araújo + Jefferson Fernandes <caio.csalgado@gmail.com>
  43 +Caio Salgado + Diego Araújo + João M. M. da Silva <caio.csalgado@gmail.com>
  44 +Caio Salgado + Diego Araújo + Pedro Leal <caio.csalgado@gmail.com>
  45 +Caio Salgado + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
  46 +Caio Salgado + Diego Araújo + Rafael Manzo <diegoamc90@gmail.com>
  47 +Caio Salgado + Jefferson Fernandes <caio.csalgado@gmail.com>
  48 +Caio Salgado + Jefferson Fernandes <jeffs.fernandes@gmail.com>
  49 +Caio Salgado + Rafael Manzo <caio.csalgado@gmail.com>
  50 +Caio Salgado + Renan Teruo <caio.csalgado@gmail.com>
  51 +Caio Salgado + Renan Teruo <caio.salgado@gmail.com>
  52 +Caio Salgado + Renan Teruo + Jefferson Fernandes <jeffs.fernandes@gmail.com>
  53 +Caio Salgado + Renan Teruo <renanteruoc@gmail.com>
15 Caio SBA <caio@colivre.coop.br> 54 Caio SBA <caio@colivre.coop.br>
16 Carlos Morais <carlos88morais@gmail.com> 55 Carlos Morais <carlos88morais@gmail.com>
17 Carlos Morais + Diego Araújo <diegoamc90@gmail.com> 56 Carlos Morais + Diego Araújo <diegoamc90@gmail.com>
  57 +Carlos Morais + Eduardo Morais <carlos88morais@gmail.com>
18 Carlos Morais + Paulo Meirelles <carlos88morais@gmail.com> 58 Carlos Morais + Paulo Meirelles <carlos88morais@gmail.com>
  59 +Carlos Morais + Pedro Leal <carlos88morais@gmail.com>
19 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> 60 Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>
20 Daniel Cunha <daniel@colivre.coop.br> 61 Daniel Cunha <daniel@colivre.coop.br>
  62 +diegoamc <diegoamc90@gmail.com>
  63 +Diego Araújo + Alessandro Palmeira <diegoamc90@gmail.com>
  64 +Diego Araujo + Caio Salgado <diegoamc90@gmail.com>
21 Diego Araújo <diegoamc90@gmail.com> 65 Diego Araújo <diegoamc90@gmail.com>
  66 +Diego Araújo + Jefferson Fernandes <diegoamc90@gmail.com>
  67 +Diego Araujo + Jefferson Fernandes <jeffs.fernandes@gmail.com>
  68 +Diego Araújo + João Machini <diegoamc90@gmail.com>
  69 +Diego Araújo + João Machini <digoamc90@gmail.com>
22 Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com> 70 Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com>
  71 +Diego Araújo + João M. M. da Silva + João Machini <diegoamc90@gmail.com>
  72 +Diego Araújo + João M. M. da Silva + Pedro Leal <diegoamc90@gmail.com>
  73 +Diego Araújo + Paulo Meirelles <diegoamc90@gmail.com>
  74 +Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
  75 +Diego Araújo + Rafael Manzo <diegoamc90@gmail.com>
  76 +Diego Araújo + Renan Teruo + Alessandro Palmeira <diegoamc90@gmail.com>
  77 +Diego Araújo + Renan Teruo <diegoamc90@gmail.com>
  78 +Diego + Jefferson <diegoamc90@gmail.com>
  79 +Diego Martinez <diegoamc90@gmail.com>
  80 +Diego + Renan <renanteruoc@gmail.com>
23 Fernanda Lopes <nanda.listas+psl@gmail.com> 81 Fernanda Lopes <nanda.listas+psl@gmail.com>
24 Grazieno Pellegrino <grazieno@gmail.com> 82 Grazieno Pellegrino <grazieno@gmail.com>
25 Isaac Canan <isaac@intelletto.com.br> 83 Isaac Canan <isaac@intelletto.com.br>
26 Italo Valcy <italo@dcc.ufba.br> 84 Italo Valcy <italo@dcc.ufba.br>
  85 +Jefferson Fernandes + Diego Araujo + Rafael Manzo <jeffs.fernandes@gmail.com>
  86 +Jefferson Fernandes + Joao M. M. da Silva <jeffs.fernandes@gmail.com>
  87 +Jefferson Fernandes + Joao M. M. Silva <jeffs.fernandes@gmail.com>
27 João da Silva <jaodsilv@linux.ime.usp.br> 88 João da Silva <jaodsilv@linux.ime.usp.br>
  89 +João Marco Maciel da Silva + Rafael Manzo + Renan Teruo <jaodsilv@linux.ime.usp.br>
  90 +João M. M. da Silva + Alessandro Palmeira + Diego Araújo + Caio Salgado <jaodsilv@linux.ime.usp.br>
  91 +João M. M. da Silva + Alessandro Palmeira + Diego Araújo <jaodsilv@linux.ime.usp.br>
  92 +João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>
  93 +João M. M. da Silva + Alessandro Palmeira + João Machini <jaodsilv@linux.ime.usp.br>
  94 +João M. M. da Silva + Caio Salgado + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>
  95 +João M. M. da Silva + Caio Salgado <jaodsilv@linux.ime.usp.br>
28 João M. M. da Silva + Carlos Morais <jaodsilv@linux.ime.usp.br> 96 João M. M. da Silva + Carlos Morais <jaodsilv@linux.ime.usp.br>
29 João M. M. da Silva + Diego Araújo <diegoamc90@gmail.com> 97 João M. M. da Silva + Diego Araújo <diegoamc90@gmail.com>
30 João M. M. da Silva + Diego Araújo <jaodsilv@linux.ime.usp.br> 98 João M. M. da Silva + Diego Araújo <jaodsilv@linux.ime.usp.br>
  99 +João M. M. da Silva + Diego Araújo + Pedro Leal <jaodsilv@linux.ime.usp.br>
  100 +João M. M. da Silva <jaodsilv@linux.ime.usp.br>
  101 +Joao M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>
  102 +João M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>
  103 +João M. M. da Silva + João M. Miranda <jaodsilv@linux.ime.usp.br>
31 João M. M. da Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br> 104 João M. M. da Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br>
  105 +João M. M. da Silva + Pedro Leal <jaodsilv@linux.ime.usp.br>
  106 +João M. M. da Silva + Rafael Manzo + Diego Araújo <jaodsilv@linux.ime.usp.br>
  107 +João M. M. da Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br>
  108 +João M. M. da Silva + Renan Teruo <jaodsilv@linux.ime.usp.br>
  109 +João M. M. Silva + Caio Salgado <jaodsilv@linux.ime.usp.br>
  110 +João M. M. Silva + Diego Araújo <jaodsilv@linux.ime.usp.br>
  111 +Joao M. M. Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>
  112 +João M. M. Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br>
  113 +João M. M. Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br>
  114 +João M. M. Silva + Renan Teruo <jaodsilv@linux.ime.usp.br>
32 Joenio Costa <joenio@colivre.coop.br> 115 Joenio Costa <joenio@colivre.coop.br>
33 Josef Spillner <josef.spillner@tu-dresden.de> 116 Josef Spillner <josef.spillner@tu-dresden.de>
34 Keilla Menezes <keilla@colivre.coop.br> 117 Keilla Menezes <keilla@colivre.coop.br>
35 Larissa Reis <larissa@colivre.coop.br> 118 Larissa Reis <larissa@colivre.coop.br>
36 Larissa Reis <reiss.larissa@gmail.com> 119 Larissa Reis <reiss.larissa@gmail.com>
37 Leandro Nunes dos Santos <leandronunes@gmail.com> 120 Leandro Nunes dos Santos <leandronunes@gmail.com>
  121 +Leandro Nunes dos Santos <leandro.santos@serpro.gov.br>
38 LinguÁgil 2010 <linguagil.bahia@gmail.com> 122 LinguÁgil 2010 <linguagil.bahia@gmail.com>
  123 +Luis David Aguilar Carlos <ludwig9003@gmail.com>
39 Martín Olivera <molivera@solar.org.ar> 124 Martín Olivera <molivera@solar.org.ar>
40 Moises Machado <moises@colivre.coop.br> 125 Moises Machado <moises@colivre.coop.br>
41 Nanda Lopes <nanda.listas+psl@gmail.com> 126 Nanda Lopes <nanda.listas+psl@gmail.com>
  127 +Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org>
42 Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org> 128 Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org>
  129 +Paulo Meirelles + Diego Araújo <paulo@softwarelivre.org>
  130 +Paulo Meirelles + João M. M. da Silva <paulo@softwarelivre.org>
43 Paulo Meirelles <paulo@softwarelivre.org> 131 Paulo Meirelles <paulo@softwarelivre.org>
44 Rafael Gomes <rafaelgomes@techfree.com.br> 132 Rafael Gomes <rafaelgomes@techfree.com.br>
  133 +Rafael Manzo + João M. M. Silva <rr.manzo@gmail.com>
45 Rafael Martins <rmmartins@gmail.com> 134 Rafael Martins <rmmartins@gmail.com>
  135 +Rafael Reggiani Manzo + Caio Salgado + Jefferson Fernandes <rr.manzo@gmail.com>
  136 +Rafael Reggiani Manzo + Diego Araujo <diegoamc90@gmail.com>
  137 +Rafael Reggiani Manzo + Diego Araujo <rr.manzo@gmail.com>
  138 +Rafael Reggiani Manzo <rr.manzo@gmail.com>
46 Raphaël Rousseau <raph@r4f.org> 139 Raphaël Rousseau <raph@r4f.org>
47 Raquel Lira <raquel.lira@gmail.com> 140 Raquel Lira <raquel.lira@gmail.com>
  141 +Renan Teruo + Caio Salgado <renanteruoc@gmail.com>
  142 +Renan Teruoc + Diego Araujo <renanteruoc@gmail.com>
  143 +Renan Teruo + Diego Araujo <renanteruoc@gmail.com>
  144 +Renan Teruo + Diego Araújo <renanteruoc@gmail.com>
  145 +Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com>
  146 +Renan Teruo + Rafael Manzo <renanteruoc@gmail.com>
48 Rodrigo Souto <rodrigo@colivre.coop.br> 147 Rodrigo Souto <rodrigo@colivre.coop.br>
49 Ronny Kursawe <kursawe.ronny@googlemail.com> 148 Ronny Kursawe <kursawe.ronny@googlemail.com>
50 Samuel R. C. Vale <srcvale@holoscopio.com> 149 Samuel R. C. Vale <srcvale@holoscopio.com>
1 = Noosfero instructions for developers 1 = Noosfero instructions for developers
2 2
3 -This document provides useful information to those who want to help with  
4 -Noosfero development. 3 +== A work about your the development platform
5 4
6 -== Requirements for development 5 +These instructions are tested and known to work on Debian stable, which is the
  6 +system that the Noosfero core developers use to work on Noosfero.
7 7
8 -First, install all requirements listed in INSTALL. Note that you do not need to  
9 -follow all the steps described there, you only need to install the packages  
10 -listed in the "Requirements" section. 8 +If you want to use another OS, read "Instructions for other systems" below.
11 9
12 -After installing the requirements listed in INSTALL, you need to install some  
13 -packages be able to run Noosfero tests. On Debian GNU/Linux and Debian-based  
14 -systems, you install them with the following command: 10 +== Instructions for Debian stable
15 11
16 - # apt-get install libtidy-ruby libhpricot-ruby libmocha-ruby imagemagick po4a xvfb libxml2-dev libxslt-dev 12 +Download the source code:
17 13
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. 14 + $ git clone git://gitorious.org/noosfero/noosfero.git
  15 + $ cd noosfero
20 16
21 -* Mocha: http://mocha.rubyforge.org/  
22 -* Tidy: http://tidy.sourceforge.net/  
23 -* Hpricot: http://github.com/whymirror/hpricot  
24 -* Imagemagick: http://wwwimagemagick.org/  
25 -* po4a: http://po4a.alioth.debian.org/  
26 -* xvfb: http://packages.debian.org/lenny/xvfb  
27 -* Libxml2: http://xmlsoft.org/  
28 -* Libxslt: http://xmlsoft.org/xslt 17 +Run the quick start script:
29 18
30 -== Boostraping a development/test environment 19 + $ ./script/quick-start
31 20
32 -You can copy and paste the commands below into a terminal (please review the  
33 -commands and make sure you understand what you are doing): 21 +Now you can execute the development server with:
34 22
35 - # checkout the code from repository  
36 - git clone git://gitorious.org/noosfero/noosfero.git  
37 - # enter the directory  
38 - cd noosfero  
39 - # copy a sample config file  
40 - cp config/database.yml.sqlite3 config/database.yml  
41 - # create tmp directory if it doesn't exist  
42 - mkdir tmp  
43 - # start Solr  
44 - rake solr:start  
45 - # create the development database  
46 - rake db:schema:load  
47 - # run pending migrations  
48 - rake db:migrate  
49 - # compile translations:  
50 - rake makemo  
51 - # create some test data:  
52 - ./script/sample-data  
53 - # install latest requirements for running tests  
54 - RAILS_ENV=cucumber rake gems:install  
55 - RAILS_ENV=test rake gems:install  
56 - # run the automated test suite to make sure your environment is sane:  
57 - rake test 23 + $ ./script/development
58 24
59 -You should now be ready to go. Issue the following command to start the Rails  
60 -development server: 25 +You will be able to access Noosfero at http://localhost:3000/
61 26
62 - ./script/server 27 +If you want to use a different port than 3000, pass `-p <PORT>` to
  28 +./script/development
63 29
64 -The server will be available at http://localhost:3000/ . If you want to use  
65 -another port than 3000, you can use the -p option of ./script/server: 30 +== Instructions for other systems
66 31
67 - ./script/server -p 9999 32 +On other OS, you have 2 options:
68 33
69 -The above command makes the server available at http://localhost:9999/ 34 +1) using a chroot or a VM with Debian stable (easier)
70 35
71 -The sample-data data scripts creates two administrator users with login "ze" and  
72 -password "test" and login "adminuser" and password "admin". 36 +Use a chroot (http://wiki.debian.org/Schroot) or a Virtual Machine (e.g. with
  37 +VirtualBox) with a Debian stable system and follow the instructions above for
  38 +Debian stable.
73 39
74 -Note that some operations, like generating image thumbnails, sending e-mails,  
75 -etc, are done in background in the context of a service independent from the  
76 -Rails application server. To have those tasks performed in a development  
77 -environment, you must run the delayed_job server like this: 40 +2) Installing dependencies on other OS (harder)
78 41
79 - ./script/delayed_job run 42 +If you want to setup a development environment in another OS, you can create a
  43 +file under script/install-dependencies/, called <OS>-<CODENAME>.sh, which
  44 +installed the dependencies for your system. With this script in place,
  45 +./script/quick-start will call it at the point of installing the required
  46 +packages for Noosfero development.
80 47
81 -This will block your terminal. To stop the delayed_job server, hit Control-C. 48 +You can check script/install-dependencies/debian-squeeze.sh to have an idea of
  49 +what kind of stuff that script has to do.
82 50
83 -== Enabling exceptions notification  
84 -  
85 -By default, exception notifications are disabled in development environment. If  
86 -you want to enable it then you need to change some files:  
87 -  
88 -1) Add in config/environments/development.rb:  
89 - config.action_controller.consider_all_requests_local = false  
90 -  
91 -2) Add in app/controller/application.rb:  
92 - local_addresses.clear  
93 -  
94 -3) Add in config/noosfero.yml at development section:  
95 - exception_recipients: [admin@example.com]  
96 -  
97 -== Releasing and building Debian package  
98 -  
99 -See RELEASING file. 51 +If you write such script for your own OS, *please* share it with us at the
  52 +development mailing list so that we can include it in the official repository.
  53 +This way other people using the same OS will have to put less effort to develop
  54 +Noosfero.
1 -= Noosfero installation instructions 1 += Noosfero installation instructions from source for production environments
2 2
3 -Noosfero is written in Ruby with the "Rails framework":http://www.rubyonrails.org,  
4 -so the process of setting it up is pretty similar to other Rails applications. 3 +The instructions below can be used for setting up a Noosfero production
  4 +environment from the Noosfero sources.
5 5
6 -Below we have the instructions for installing Noosfero dependencies and setting  
7 -up a production environment. If you have problems with the setup, please feel  
8 -free to ask questions in the development mailing list. 6 +Before you start installing Noosfero manually, see the information about the
  7 +Noosfero Debian package at http://noosfero.org/Development/DebianPackage. Using
  8 +the Debian packages on a Debian stable system is the recommended method for
  9 +installing production environments.
  10 +
  11 +If you want to setup a development environment instead of a production one,
  12 +stop reading this file right now and read the file HACKING instead.
  13 +
  14 +For a complete installation guide, please see the following web page:
  15 +http://noosfero.org/Development/HowToInstall
  16 +
  17 +If you have problems with the setup, please feel free to ask questions in the
  18 +development mailing list.
9 19
10 == Requirements 20 == Requirements
11 21
  22 +DISCLAIMER: this installation procedure is tested with Debian stable, which is
  23 +currently the only recommended operating system for production usage. It is
  24 +possible that you can install it on other systems, and if you do so, please
  25 +report it on one of the Noosfero mailing lists, and please send a patch
  26 +updating these instructions.
  27 +
  28 +Noosfero is written in Ruby with the "Rails
  29 +framework":http://www.rubyonrails.org, so the process of setting it up is
  30 +pretty similar to other Rails applications.
  31 +
12 You need to install some packages Noosfero depends on. On Debian GNU/Linux or 32 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 33 Debian-based systems, all of these packages are available through the Debian
14 archive. You can install them with the following command: 34 archive. You can install them with the following command:
@@ -38,21 +58,9 @@ If you manage to install Noosfero successfully on other systems than Debian, @@ -38,21 +58,9 @@ If you manage to install Noosfero successfully on other systems than Debian,
38 please feel free to contact the Noosfero development mailing with the 58 please feel free to contact the Noosfero development mailing with the
39 instructions for doing so, and we'll include it here. 59 instructions for doing so, and we'll include it here.
40 60
41 -=== Setting up a production environment  
42 -  
43 -DISCLAIMER: this installation procedure is tested with Debian stable, which is  
44 -currently the only recommended operating system for production usage. It is  
45 -possible that you can install it on other systems, and if you do so, please  
46 -report it on one of the Noosfero mailing lists, and please send a patch  
47 -updating these instructions.  
48 -  
49 As root user 61 As root user
50 ============ 62 ============
51 63
52 -NOTE: these instructions are for seting up a *production* environment. If you  
53 -are going to do Noosfero development, you don't need to do these steps. Stop  
54 -here and see the HACKING file instead.  
55 -  
56 Install memcached. On Debian: 64 Install memcached. On Debian:
57 65
58 # apt-get install memcached 66 # apt-get install memcached
@@ -96,11 +104,11 @@ $ git checkout -b stable origin/stable @@ -96,11 +104,11 @@ $ git checkout -b stable origin/stable
96 downloading tarball 104 downloading tarball
97 ------------------- 105 -------------------
98 106
99 -Note: replace 0.35.0 below from the latest stable version. 107 +Note: replace 0.39.0 below from the latest stable version.
100 108
101 -$ wget http://noosfero.org/pub/Development/NoosferoVersion00x35x00/noosfero-0.35.0.tar.gz  
102 -$ tar -zxvf noosfero-0.35.0.tar.gz  
103 -$ ln -s noosfero-0.35.0 current 109 +$ wget http://noosfero.org/pub/Development/NoosferoVersion00x39x00/noosfero-0.39.0.tar.gz
  110 +$ tar -zxvf noosfero-0.39.0.tar.gz
  111 +$ ln -s noosfero-0.39.0 current
104 $ cd current 112 $ cd current
105 113
106 Copy config/solr.yml.dist to config/solr.yml. You will 114 Copy config/solr.yml.dist to config/solr.yml. You will
@@ -108,7 +116,7 @@ probably not need to customize this configuration, but have a look at it. @@ -108,7 +116,7 @@ probably not need to customize this configuration, but have a look at it.
108 116
109 Create the thin configuration file: 117 Create the thin configuration file:
110 118
111 -$ thin -C config/thin.yml config 119 +$ thin -C config/thin.yml -e production config
112 120
113 Edit config/thin.yml to suit your needs. Make sure your apache 121 Edit config/thin.yml to suit your needs. Make sure your apache
114 configuration matches the thin cluster configuration, specially in respect 122 configuration matches the thin cluster configuration, specially in respect
@@ -147,48 +155,7 @@ Restart postgresql: @@ -147,48 +155,7 @@ Restart postgresql:
147 155
148 Noosfero needs a functional e-mail setup to work: the local mail system should 156 Noosfero needs a functional e-mail setup to work: the local mail system should
149 be able to deliver e-mail to the internet, either directly or through an 157 be able to deliver e-mail to the internet, either directly or through an
150 -external SMTP server.  
151 -  
152 -If you know mail systems well, you just need to make sure thet the local MTA,  
153 -listening on localhost:25, is able to deliver e-mails to the internet. Any mail  
154 -server will do it.  
155 -  
156 -If you are not a mail specialist, we suggest that you use the Postfix mail  
157 -server, since it is easy to configure and very reliable. Just follow the  
158 -instructions below.  
159 -  
160 -To install Postfix:  
161 -  
162 -# apt-get install postfix  
163 -  
164 -During the installation process, you will be asked a few questions. Your answer  
165 -to them will vary in 2 cases:  
166 -  
167 -Case 1: you can send e-mails directly to the internet. This will be the case  
168 -for most commercial private servers. Your answers should be:  
169 -  
170 - General type of mail configuration: Internet site  
171 - System mail name: the name of your domain, e.g. "mysocialnetwork.com"  
172 -  
173 -Case 2: you cannot, or don't want to, send e-mail directly to the internet.  
174 -This happens for example if your server is not allowed to make outbound  
175 -connections on port 25, or if you want to concentrate all your outbound mail  
176 -through a single SMTP server. Your answers in this case should be:  
177 -  
178 - General type of mail configuration: Internet with smarthost  
179 - System mail name: the name of your domain, e.g. "mysocialnetwork.com"  
180 - SMTP relay host: smtp.yourprovider.com  
181 -  
182 -Note that smtp.yourprovider.com must allow your server to deliver e-mails  
183 -through it. You should probably ask your servive provider about this.  
184 -  
185 -There is another possibility: if you are installing on a shared server, and  
186 -don't have permission to configure the local MTA, you can instruct Noosfero to  
187 -send e-mails directly through an external server. Please note that this should  
188 -be your last option, since contacting an external SMTP server directly may slow  
189 -down your Noosfero application server. To configure Noosfero to send e-mails  
190 -through an external SMTP server, follow the instructions on  
191 -http://noosfero.org/Development/SMTPMailSending 158 +external SMTP server. Please check the documentation at the INSTALL.email file.
192 159
193 As noosfero user 160 As noosfero user
194 ================ 161 ================
@@ -220,19 +187,6 @@ $ ./script/dbconsole production @@ -220,19 +187,6 @@ $ ./script/dbconsole production
220 If it connects to your database, then everything is fine. If you got an error 187 If it connects to your database, then everything is fine. If you got an error
221 message, then you have to check your database configuration. 188 message, then you have to check your database configuration.
222 189
223 -Installing gem rack  
224 -===================  
225 -  
226 -This gem is required if you want to run Mezuro plugin.  
227 -  
228 -Install RubyGem Rack 1.0.1.  
229 -Others versions may not be compatible with Noosfero:  
230 -  
231 -# gem install rack -v 1.0.1  
232 -  
233 -As noosfero user  
234 -================  
235 -  
236 Create the database structure: 190 Create the database structure:
237 191
238 $ RAILS_ENV=production rake db:schema:load 192 $ RAILS_ENV=production rake db:schema:load
@@ -245,7 +199,7 @@ Run Solr: @@ -245,7 +199,7 @@ Run Solr:
245 199
246 $ rake solr:start 200 $ rake solr:start
247 201
248 -Now we have to create some initial data. To create your default environment 202 +Now we must create some initial data. To create your default environment
249 (the first one), run the command below: 203 (the first one), run the command below:
250 204
251 $ RAILS_ENV=production ./script/runner 'Environment.create!(:name => "My environment", :is_default => true)' 205 $ RAILS_ENV=production ./script/runner 'Environment.create!(:name => "My environment", :is_default => true)'
@@ -261,10 +215,10 @@ $ RAILS_ENV=production ./script/runner &quot;Environment.default.domains &lt;&lt; Domain.ne @@ -261,10 +215,10 @@ $ RAILS_ENV=production ./script/runner &quot;Environment.default.domains &lt;&lt; Domain.ne
261 215
262 Add at least one user as admin of environment: 216 Add at least one user as admin of environment:
263 217
264 -$ RAILS_ENV=production ./script/runner "User.create(:login => 'adminuser', :email => 'admin@example.com', :password => 'admin', :password_confirmation => 'admin', :environment => Environment.default)" 218 +$ RAILS_ENV=production ./script/runner "User.create(:login => 'adminuser', :email => 'admin@example.com', :password => 'admin', :password_confirmation => 'admin', :environment => Environment.default, :activated_at => Time.new)"
265 219
266 (replace "adminuser", "admin@example.com", "admin" with the login, email 220 (replace "adminuser", "admin@example.com", "admin" with the login, email
267 -and password of your environment admin) 221 +and password of your environment administrator)
268 222
269 To start the Noosfero application servers: 223 To start the Noosfero application servers:
270 224
@@ -274,23 +228,6 @@ At this point you have a functional Noosfero installation running, the only @@ -274,23 +228,6 @@ At this point you have a functional Noosfero installation running, the only
274 thing left is to configure your webserver as a reverse proxy to pass requests 228 thing left is to configure your webserver as a reverse proxy to pass requests
275 to them. 229 to them.
276 230
277 -Enabling exception notifications  
278 -================================  
279 -  
280 -This is an optional step. You will need it only if you want to receive e-mail  
281 -notifications when some exception occurs on Noosfero.  
282 -  
283 -First, install this version of the gem.  
284 -Others versions may not be compatible with Noosfero:  
285 -  
286 -# gem install exception_notification -v 1.0.20090728  
287 -  
288 -You can configure the e-mails that will receive the notifications.  
289 -Change the file config/noosfero.yml as the following example, replacing the  
290 -e-mails by real ones:  
291 -  
292 - production:  
293 - exception_recipients: [admin@example.com, you@example.com]  
294 231
295 ================== 232 ==================
296 Apache instalation 233 Apache instalation
@@ -384,6 +321,26 @@ Now restart your apache server (as root): @@ -384,6 +321,26 @@ Now restart your apache server (as root):
384 321
385 # invoke-rc.d apache2 restart 322 # invoke-rc.d apache2 restart
386 323
  324 +
  325 +Enabling exception notifications
  326 +================================
  327 +
  328 +This is an optional step. You will need it only if you want to receive e-mail
  329 +notifications when some exception occurs on Noosfero.
  330 +
  331 +First, install this version of the gem.
  332 +Others versions may not be compatible with Noosfero:
  333 +
  334 +# gem install exception_notification -v 1.0.20090728
  335 +
  336 +You can configure the e-mails that will receive the notifications.
  337 +Change the file config/noosfero.yml as the following example, replacing the
  338 +e-mails by real ones:
  339 +
  340 + production:
  341 + exception_recipients: [admin@example.com, you@example.com]
  342 +
  343 +
387 ============ 344 ============
388 Maintainance 345 Maintainance
389 ============ 346 ============
INSTALL.email 0 → 100644
@@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
  1 += Noosfero email setup
  2 +
  3 +If you know mail systems well, you just need to make sure that the local MTA,
  4 +listening on localhost:25, is able to deliver e-mails to the internet. Any mail
  5 +server will do it. You can stop reading now.
  6 +
  7 +If you are not an email specialist, then follow the instructions below. We
  8 +suggest that you use the Postfix mail server, since it is easy to configure and
  9 +very reliable. Just follow the instructions below.
  10 +
  11 +To install Postfix:
  12 +
  13 +# apt-get install postfix
  14 +
  15 +During the installation process, you will be asked a few questions. Your answer
  16 +to them will vary in 2 cases:
  17 +
  18 +Case 1: you can send e-mails directly to the internet. This will be the case
  19 +for most commercial private servers. Your answers should be:
  20 +
  21 + General type of mail configuration: Internet site
  22 + System mail name: the name of your domain, e.g. "mysocialnetwork.com"
  23 +
  24 +Case 2: you cannot, or don't want to, send e-mail directly to the internet.
  25 +This happens for example if your server is not allowed to make outbound
  26 +connections on port 25, or if you want to concentrate all your outbound mail
  27 +through a single SMTP server. Your answers in this case should be:
  28 +
  29 + General type of mail configuration: Internet with smarthost
  30 + System mail name: the name of your domain, e.g. "mysocialnetwork.com"
  31 + SMTP relay host: smtp.yourprovider.com
  32 +
  33 +Note that smtp.yourprovider.com must allow your server to deliver e-mails
  34 +through it. You should probably ask your servive provider about this.
  35 +
  36 +There is another possibility: if you are installing on a shared server, and
  37 +don't have permission to configure the local MTA, you can instruct Noosfero to
  38 +send e-mails directly through an external server. Please note that this should
  39 +be your last option, since contacting an external SMTP server directly may slow
  40 +down your Noosfero application server. To configure Noosfero to send e-mails
  41 +through an external SMTP server, follow the instructions on
  42 +http://noosfero.org/Development/SMTPMailSending
  43 +
INSTALL.varnish
@@ -5,75 +5,63 @@ recommended. See http://www.varnish-cache.org/ for more information on Varnish. @@ -5,75 +5,63 @@ recommended. See http://www.varnish-cache.org/ for more information on Varnish.
5 5
6 Varnish can be set up to use with Noosfero with the following steps: 6 Varnish can be set up to use with Noosfero with the following steps:
7 7
8 -1) setup Noosfero with apache according to the INSTALL file. 8 +1) setup Noosfero with apache according to the INSTALL file. If you used the
  9 +Debian package to install noosfero, you don't need to do anything about this.
9 10
10 2) install Varnish 11 2) install Varnish
11 12
12 # apt-get install varnish 13 # apt-get install varnish
13 14
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 Install the RPAF apache module (or skip this step if not using apache): 15 Install the RPAF apache module (or skip this step if not using apache):
19 16
20 # apt-get install libapache2-mod-rpaf 17 # apt-get install libapache2-mod-rpaf
21 18
22 -3) Enable varnish logging:  
23 -  
24 -3a) Edit /etc/default/varnishncsa and uncomment the line that contains:  
25 -  
26 -VARNISHNCSA_ENABLED=1  
27 -  
28 -The varnish log will be written to /var/log/varnish/varnishncsa.log in an  
29 -apache-compatible format. You should change your statistics generation software  
30 -(e.g. awstats) to use that instead of apache logs.  
31 -  
32 -3b) Restart Varnish Logging service  
33 -  
34 - # invoke-rc.d varnishncsa start  
35 -  
36 -4) Change Apache to listen on port 8080 instead of 80 19 +3) Change Apache to listen on port 8080 instead of 80
37 20
38 -4a) Edit /etc/apache2/ports.conf, and: 21 +3a) Edit /etc/apache2/ports.conf, and:
39 22
40 - * change 'Listen 80' to 'Listen 127.0.0.1:8080'  
41 * change 'NameVirtualHost *:80' to 'NameVirtualHost *:8080' 23 * change 'NameVirtualHost *:80' to 'NameVirtualHost *:8080'
  24 + * change 'Listen 80' to 'Listen 127.0.0.1:8080'
42 25
43 -4b) Edit /etc/apache2/sites-enabled/*, and change '<VirtualHost *:80>' to '<VirtualHost *:8080>' 26 +3b) Edit /etc/apache2/sites-enabled/*, and change '<VirtualHost *:80>' to
  27 +'<VirtualHost *:8080>'
44 28
45 -4c) Restart apache 29 +3c) Restart apache
46 30
47 # invoke-rc.d apache2 restart 31 # invoke-rc.d apache2 restart
48 32
49 -5) Change Varnish to listen on port 80 33 +4) Varnish configuration
50 34
51 -5a) Edit /etc/default/varnish and change '-a :6081' to '-a :80' 35 +4a) Edit /etc/default/varnish
52 36
53 -5b) Restart Varnish 37 + * change the line that says "START=no" to say "START=yes"
  38 + * change '-a :6081' to '-a :80'
54 39
55 - # invoke-rc.d varnish restart 40 +4b) Edit /etc/varnish/default.vcl and add the following lines at the end:
56 41
57 -6) Configure varnish to fit noosfero  
58 -(assuming Noosfero is installed in /var/lib/noosfero) 42 + include "/etc/noosfero/varnish-noosfero.vcl";
  43 + include "/etc/noosfero/varnish-accept-language.vcl";
59 44
60 -6a) Configure noosfero to do specific routines to varnish 45 +On manual installations, change "/etc/noosfero/*" to
  46 +"{Rails.root}/etc/noosfero/*"
61 47
62 -Add the following line to your /etc/varnish/default.vcl file: 48 +4c) Restart Varnish
63 49
64 - include "/var/lib/noosfero/etc/noosfero/varnish-noosfero.vcl"; 50 + # invoke-rc.d varnish restart
65 51
66 -6b) Configure varnish to store separate caches for each language 52 +5) Enable varnish logging:
67 53
68 -Add the following line to your /etc/varnish/default.vcl file: 54 +5a) Edit /etc/default/varnishncsa and uncomment the line that contains:
69 55
70 - include "/var/lib/noosfero/etc/noosfero/varnish-accept-language.vcl"; 56 +VARNISHNCSA_ENABLED=1
71 57
72 -7) Restart Varnish 58 +The varnish log will be written to /var/log/varnish/varnishncsa.log in an
  59 +apache-compatible format. You should change your statistics generation software
  60 +(e.g. awstats) to use that instead of apache logs.
73 61
74 - # invoke-rc.d varnish restart 62 +5b) Restart Varnish Logging service
  63 +
  64 + # invoke-rc.d varnishncsa restart
75 65
76 Thanks to Cosimo Streppone for varnish-accept-language. See 66 Thanks to Cosimo Streppone for varnish-accept-language. See
77 http://github.com/cosimo/varnish-accept-language for more information. 67 http://github.com/cosimo/varnish-accept-language for more information.
78 -  
79 - -- Antonio Terceiro <terceiro@colivre.coop.br> Sat, 04 Sep 2010 17:29:27 -0300  
app/controllers/my_profile/cms_controller.rb
@@ -16,7 +16,13 @@ class CmsController &lt; MyProfileController @@ -16,7 +16,13 @@ class CmsController &lt; MyProfileController
16 16
17 before_filter :login_required, :except => [:suggest_an_article] 17 before_filter :login_required, :except => [:suggest_an_article]
18 18
19 - protect_if :except => [:suggest_an_article, :set_home_page, :edit, :destroy, :publish] do |c, user, profile| 19 + protect_if :only => :upload_files do |c, user, profile|
  20 + article_id = c.params[:parent_id]
  21 + (!article_id.blank? && profile.articles.find(article_id).allow_create?(user)) ||
  22 + (user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)))
  23 + end
  24 +
  25 + protect_if :except => [:suggest_an_article, :set_home_page, :edit, :destroy, :publish, :upload_files] do |c, user, profile|
20 user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)) 26 user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile))
21 end 27 end
22 28
@@ -92,7 +98,7 @@ class CmsController &lt; MyProfileController @@ -92,7 +98,7 @@ class CmsController &lt; MyProfileController
92 @article_types = [] 98 @article_types = []
93 available_article_types.each do |type| 99 available_article_types.each do |type|
94 @article_types.push({ 100 @article_types.push({
95 - :name => type.name, 101 + :class => type,
96 :short_description => type.short_description, 102 :short_description => type.short_description,
97 :description => type.description 103 :description => type.description
98 }) 104 })
@@ -154,7 +160,7 @@ class CmsController &lt; MyProfileController @@ -154,7 +160,7 @@ class CmsController &lt; MyProfileController
154 end 160 end
155 if request.post? && params[:uploaded_files] 161 if request.post? && params[:uploaded_files]
156 params[:uploaded_files].each do |file| 162 params[:uploaded_files].each do |file|
157 - @uploaded_files << UploadedFile.create(:uploaded_data => file, :profile => profile, :parent => @parent) unless file == '' 163 + @uploaded_files << UploadedFile.create(:uploaded_data => file, :profile => profile, :parent => @parent, :last_changed_by => user) unless file == ''
158 end 164 end
159 @errors = @uploaded_files.select { |f| f.errors.any? } 165 @errors = @uploaded_files.select { |f| f.errors.any? }
160 if @errors.any? 166 if @errors.any?
app/controllers/my_profile/tasks_controller.rb
@@ -9,7 +9,7 @@ class TasksController &lt; MyProfileController @@ -9,7 +9,7 @@ class TasksController &lt; MyProfileController
9 end 9 end
10 10
11 def processed 11 def processed
12 - @tasks = Task.to(profile).finished.sort_by(&:created_at) 12 + @tasks = Task.to(profile).closed.sort_by(&:created_at)
13 end 13 end
14 14
15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] 15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ]
app/controllers/public/account_controller.rb
@@ -4,6 +4,7 @@ class AccountController &lt; ApplicationController @@ -4,6 +4,7 @@ class AccountController &lt; ApplicationController
4 4
5 before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise] 5 before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise]
6 before_filter :redirect_if_logged_in, :only => [:login, :signup] 6 before_filter :redirect_if_logged_in, :only => [:login, :signup]
  7 + before_filter :protect_from_bots, :only => :signup
7 8
8 # say something nice, you goof! something sweet. 9 # say something nice, you goof! something sweet.
9 def index 10 def index
@@ -55,6 +56,11 @@ class AccountController &lt; ApplicationController @@ -55,6 +56,11 @@ class AccountController &lt; ApplicationController
55 render :action => 'login', :layout => false 56 render :action => 'login', :layout => false
56 end 57 end
57 58
  59 + def signup_time
  60 + key = set_signup_start_time_for_now
  61 + render :text => { :ok=>true, :key=>key }.to_json
  62 + end
  63 +
58 # action to register an user to the application 64 # action to register an user to the application
59 def signup 65 def signup
60 if @plugins.dispatch(:allow_user_registration).include?(false) 66 if @plugins.dispatch(:allow_user_registration).include?(false)
@@ -62,6 +68,7 @@ class AccountController &lt; ApplicationController @@ -62,6 +68,7 @@ class AccountController &lt; ApplicationController
62 session[:notice] = _("This environment doesn't allow user registration.") 68 session[:notice] = _("This environment doesn't allow user registration.")
63 end 69 end
64 70
  71 + @block_bot = !!session[:may_be_a_bot]
65 @invitation_code = params[:invitation_code] 72 @invitation_code = params[:invitation_code]
66 begin 73 begin
67 if params[:user] 74 if params[:user]
@@ -76,19 +83,28 @@ class AccountController &lt; ApplicationController @@ -76,19 +83,28 @@ class AccountController &lt; ApplicationController
76 @person = Person.new(params[:profile_data]) 83 @person = Person.new(params[:profile_data])
77 @person.environment = @user.environment 84 @person.environment = @user.environment
78 if request.post? 85 if request.post?
79 - @user.signup!  
80 - owner_role = Role.find_by_name('owner')  
81 - @user.person.affiliate(@user.person, [owner_role]) if owner_role  
82 - invitation = Task.find_by_code(@invitation_code)  
83 - if invitation  
84 - invitation.update_attributes!({:friend => @user.person})  
85 - invitation.finish  
86 - end  
87 - if @user.activated?  
88 - self.current_user = @user  
89 - redirect_to '/' 86 + if may_be_a_bot
  87 + set_signup_start_time_for_now
  88 + @block_bot = true
  89 + session[:may_be_a_bot] = true
90 else 90 else
91 - @register_pending = true 91 + if session[:may_be_a_bot]
  92 + return false unless verify_recaptcha :model=>@user, :message=>_('Captcha (the human test)')
  93 + end
  94 + @user.signup!
  95 + owner_role = Role.find_by_name('owner')
  96 + @user.person.affiliate(@user.person, [owner_role]) if owner_role
  97 + invitation = Task.find_by_code(@invitation_code)
  98 + if invitation
  99 + invitation.update_attributes!({:friend => @user.person})
  100 + invitation.finish
  101 + end
  102 + if @user.activated?
  103 + self.current_user = @user
  104 + redirect_to '/'
  105 + else
  106 + @register_pending = true
  107 + end
92 end 108 end
93 end 109 end
94 rescue ActiveRecord::RecordInvalid 110 rescue ActiveRecord::RecordInvalid
@@ -97,6 +113,7 @@ class AccountController &lt; ApplicationController @@ -97,6 +113,7 @@ class AccountController &lt; ApplicationController
97 @person.errors.delete(:user_id) 113 @person.errors.delete(:user_id)
98 render :action => 'signup' 114 render :action => 'signup'
99 end 115 end
  116 + clear_signup_start_time
100 end 117 end
101 118
102 # action to perform logout from the application 119 # action to perform logout from the application
@@ -271,7 +288,36 @@ class AccountController &lt; ApplicationController @@ -271,7 +288,36 @@ class AccountController &lt; ApplicationController
271 def no_redirect 288 def no_redirect
272 @cannot_redirect = true 289 @cannot_redirect = true
273 end 290 end
274 - 291 +
  292 + def set_signup_start_time_for_now
  293 + key = 'signup_start_time_' + rand.to_s.split('.')[1]
  294 + Rails.cache.write key, Time.now
  295 + key
  296 + end
  297 +
  298 + def get_signup_start_time
  299 + Rails.cache.read params[:signup_time_key]
  300 + end
  301 +
  302 + def clear_signup_start_time
  303 + Rails.cache.delete params[:signup_time_key] if params[:signup_time_key]
  304 + end
  305 +
  306 + def may_be_a_bot
  307 + # No minimum signup delay, no bot test.
  308 + return false if environment.min_signup_delay == 0
  309 +
  310 + # answering captcha, may be human!
  311 + return false if params[:recaptcha_response_field]
  312 +
  313 + # never set signup_time, hi wget!
  314 + signup_start_time = get_signup_start_time
  315 + return true if signup_start_time.nil?
  316 +
  317 + # so fast, so bot.
  318 + signup_start_time > ( Time.now - environment.min_signup_delay.seconds )
  319 + end
  320 +
275 def check_answer 321 def check_answer
276 unless answer_correct 322 unless answer_correct
277 @enterprise.block 323 @enterprise.block
app/controllers/public/catalog_controller.rb
1 class CatalogController < PublicController 1 class CatalogController < PublicController
2 needs_profile 2 needs_profile
  3 + no_design_blocks
3 4
4 before_filter :check_enterprise_and_environment 5 before_filter :check_enterprise_and_environment
5 6
6 def index 7 def index
7 - @products = @profile.products.paginate(:order => 'name asc', :per_page => 9, :page => params[:page]) 8 + @category = params[:level] ? ProductCategory.find(params[:level]) : nil
  9 + @products = @profile.products.from_category(@category).paginate(:order => 'available desc, highlighted desc, name asc', :per_page => 9, :page => params[:page])
  10 + @categories = ProductCategory.on_level(params[:level])
8 end 11 end
9 12
10 protected 13 protected
app/controllers/public/content_viewer_controller.rb
@@ -25,24 +25,26 @@ class ContentViewerController &lt; ApplicationController @@ -25,24 +25,26 @@ class ContentViewerController &lt; ApplicationController
25 return 25 return
26 end 26 end
27 end 27 end
28 -  
29 - # page not found, give error  
30 - if @page.nil?  
31 - render_not_found(@path)  
32 - return  
33 - end  
34 end 28 end
35 29
36 - if !@page.display_to?(user)  
37 - if profile.display_info_to?(user) || !profile.visible?  
38 - message = _('You are not allowed to view this content. You can contact the owner of this profile to request access then.') 30 + if !@page.nil? && !@page.display_to?(user)
  31 + if !profile.public?
  32 + private_profile_partial_parameters
  33 + render :template => 'profile/_private_profile.rhtml', :status => 403
  34 + else #if !profile.visible?
  35 + message = _('You are not allowed to view this content.')
  36 + message += ' ' + _('You can contact the owner of this profile to request access then.')
39 render_access_denied(message) 37 render_access_denied(message)
40 - elsif !profile.public?  
41 - redirect_to :controller => 'profile', :action => 'index', :profile => profile.identifier  
42 end 38 end
43 return 39 return
44 end 40 end
45 41
  42 + # page not found, give error
  43 + if @page.nil?
  44 + render_not_found(@path)
  45 + return
  46 + end
  47 +
46 if request.xhr? && params[:toolbar] 48 if request.xhr? && params[:toolbar]
47 render :partial => 'article_toolbar' 49 render :partial => 'article_toolbar'
48 return 50 return
app/controllers/public/profile_controller.rb
@@ -368,18 +368,13 @@ class ProfileController &lt; PublicController @@ -368,18 +368,13 @@ class ProfileController &lt; PublicController
368 end 368 end
369 369
370 def private_profile 370 def private_profile
371 - if profile.person?  
372 - @action = :add_friend  
373 - @message = _("The content here is available to %s's friends only.") % profile.short_name  
374 - else  
375 - @action = :join  
376 - @message = _('The contents in this community is available to members only.')  
377 - end  
378 - @no_design_blocks = true 371 + private_profile_partial_parameters
379 end 372 end
380 373
381 def invisible_profile 374 def invisible_profile
382 - render_access_denied(_("This profile is inaccessible. You don't have the permission to view the content here."), _("Oops ... you cannot go ahead here")) 375 + unless profile.is_template?
  376 + render_access_denied(_("This profile is inaccessible. You don't have the permission to view the content here."), _("Oops ... you cannot go ahead here"))
  377 + end
383 end 378 end
384 379
385 def per_page 380 def per_page
app/helpers/application_helper.rb
@@ -1284,7 +1284,7 @@ module ApplicationHelper @@ -1284,7 +1284,7 @@ module ApplicationHelper
1284 (user.already_reported?(profile) ? 1284 (user.already_reported?(profile) ?
1285 content_tag('a', text, :class => klass + ' disabled comment-footer comment-footer-link', :title => already_reported_message) : 1285 content_tag('a', text, :class => klass + ' disabled comment-footer comment-footer-link', :title => already_reported_message) :
1286 link_to(text, url, :class => klass + ' comment-footer comment-footer-link', :title => report_profile_message) 1286 link_to(text, url, :class => klass + ' comment-footer comment-footer-link', :title => report_profile_message)
1287 - ) + content_tag('span', ' | ', :class => 'comment-footer comment-footer-hide') 1287 + ) + content_tag('span', ' ', :class => 'comment-footer comment-footer-hide')
1288 end 1288 end
1289 end 1289 end
1290 1290
@@ -1337,11 +1337,12 @@ module ApplicationHelper @@ -1337,11 +1337,12 @@ module ApplicationHelper
1337 counter = 0 1337 counter = 0
1338 radios = klass.templates.map do |template| 1338 radios = klass.templates.map do |template|
1339 counter += 1 1339 counter += 1
1340 - content_tag('li', labelled_radio_button(template.name, "#{field_name}[template_id]", template.id, counter==1)) 1340 + content_tag('li', labelled_radio_button(link_to(template.name, template.url, :target => '_blank'), "#{field_name}[template_id]", template.id, counter==1))
1341 end.join("\n") 1341 end.join("\n")
1342 1342
1343 - content_tag('div', content_tag('span', _('Template:')) +  
1344 - content_tag('ul', radios, :style => 'list-style: none; padding-left: 0; margin-top: 0.5em;'), 1343 + content_tag('div', content_tag('label', _('Profile organization'), :for => 'template-options', :class => 'formlabel') +
  1344 + content_tag('p', _('Your profile will be created according to the selected template. Click on the options to view them.'), :style => 'margin: 5px 15px;padding: 0px 10px;') +
  1345 + content_tag('ul', radios, :style => 'list-style: none; padding-left: 20px; margin-top: 0.5em;'),
1345 :id => 'template-options', 1346 :id => 'template-options',
1346 :style => 'margin-top: 1em' 1347 :style => 'margin-top: 1em'
1347 ) 1348 )
@@ -1410,4 +1411,16 @@ module ApplicationHelper @@ -1410,4 +1411,16 @@ module ApplicationHelper
1410 options[:class] = "comment-footer comment-footer-link comment-footer-hide" 1411 options[:class] = "comment-footer comment-footer-link comment-footer-hide"
1411 expirable_content_reference content, action, text, url, options 1412 expirable_content_reference content, action, text, url, options
1412 end 1413 end
  1414 +
  1415 + def private_profile_partial_parameters
  1416 + if profile.person?
  1417 + @action = :add_friend
  1418 + @message = _("The content here is available to %s's friends only.") % profile.short_name
  1419 + else
  1420 + @action = :join
  1421 + @message = _('The contents in this community is available to members only.')
  1422 + end
  1423 + @no_design_blocks = true
  1424 + end
  1425 +
1413 end 1426 end
app/helpers/catalog_helper.rb
@@ -3,4 +3,28 @@ module CatalogHelper @@ -3,4 +3,28 @@ module CatalogHelper
3 include DisplayHelper 3 include DisplayHelper
4 include ManageProductsHelper 4 include ManageProductsHelper
5 5
  6 + def breadcrumb(category)
  7 + start = link_to(_('Start'), {:action => 'index'})
  8 + ancestors = category.ancestors.map { |c| link_to(c.name, {:action => 'index', :level => c.id}) }.reverse
  9 + current_level = content_tag('strong', category.name)
  10 + all_items = [start] + ancestors + [current_level]
  11 + content_tag('div', all_items.join(' &rarr; '), :id => 'breadcrumb')
  12 + end
  13 +
  14 + def category_link(category, sub = false)
  15 + count = profile.products.from_category(category).count
  16 + name = truncate(category.name, :length => 22 - count.to_s.size)
  17 + link_name = sub ? name : content_tag('strong', name)
  18 + link = link_to(link_name, {:action => 'index', :level => category.id}, :title => category.name)
  19 + content_tag('li', "#{link} (#{count})") if count > 0
  20 + end
  21 +
  22 + def category_sub_links(category)
  23 + sub_categories = []
  24 + category.children.each do |sub_category|
  25 + sub_categories << category_link(sub_category, true)
  26 + end
  27 + content_tag('ul', sub_categories) if sub_categories.size > 1
  28 + end
  29 +
6 end 30 end
app/helpers/content_viewer_helper.rb
@@ -26,7 +26,7 @@ module ContentViewerHelper @@ -26,7 +26,7 @@ module ContentViewerHelper
26 end 26 end
27 title << content_tag('span', 27 title << content_tag('span',
28 content_tag('span', show_date(article.published_at), :class => 'date') + 28 content_tag('span', show_date(article.published_at), :class => 'date') +
29 - content_tag('span', [_(", by %s") % link_to(article.author_name, article.author.url)], :class => 'author') + 29 + content_tag('span', [_(", by %s") % link_to(article.author_name, article.author_url)], :class => 'author') +
30 content_tag('span', comments, :class => 'comments'), 30 content_tag('span', comments, :class => 'comments'),
31 :class => 'created-at' 31 :class => 'created-at'
32 ) 32 )
app/helpers/display_helper.rb
@@ -8,6 +8,14 @@ module DisplayHelper @@ -8,6 +8,14 @@ module DisplayHelper
8 opts 8 opts
9 end 9 end
10 10
  11 + def themed_path(file)
  12 + if File.exists?(File.join(Rails.root, 'public', theme_path, file))
  13 + File.join(theme_path, file)
  14 + else
  15 + file
  16 + end
  17 + end
  18 +
11 def image_link_to_product(product, opts={}) 19 def image_link_to_product(product, opts={})
12 return _('No product') unless product 20 return _('No product') unless product
13 target = product_path(product) 21 target = product_path(product)
app/helpers/folder_helper.rb
@@ -52,8 +52,8 @@ module FolderHelper @@ -52,8 +52,8 @@ module FolderHelper
52 end 52 end
53 end 53 end
54 54
55 - def icon_for_new_article(type)  
56 - "icon-new icon-new%s" % type.constantize.icon_name 55 + def icon_for_new_article(klass)
  56 + "icon-new icon-new%s" % klass.icon_name
57 end 57 end
58 58
59 def custom_options_for_article(article) 59 def custom_options_for_article(article)
app/helpers/forms_helper.rb
@@ -142,6 +142,38 @@ module FormsHelper @@ -142,6 +142,38 @@ module FormsHelper
142 content_tag('table',rows.join("\n")) 142 content_tag('table',rows.join("\n"))
143 end 143 end
144 144
  145 + def select_folder(label_text, field_id, collection, default_value=nil, html_options = {}, js_options = {})
  146 + root = profile ? profile.identifier : _("root")
  147 + labelled_form_field(
  148 + label_text,
  149 + select_tag(
  150 + field_id,
  151 + options_for_select(
  152 + [[root, '']] +
  153 + collection.collect {|f| [ root + '/' + f.full_name, f.id ] },
  154 + default_value
  155 + ),
  156 + html_options.merge(js_options)
  157 + )
  158 + )
  159 + end
  160 +
  161 + def select_profile_folder(label_text, field_id, profile, default_value='', html_options = {}, js_options = {})
  162 + result = labelled_form_field(
  163 + label_text,
  164 + select_tag(
  165 + field_id,
  166 + options_for_select(
  167 + [[profile.identifier, '']] +
  168 + profile.folders.collect {|f| [ profile.identifier + '/' + f.full_name, f.id ] },
  169 + default_value
  170 + ),
  171 + html_options.merge(js_options)
  172 + )
  173 + )
  174 + return result
  175 + end
  176 +
145 def date_field(name, value, format = '%Y-%m-%d', datepicker_options = {}, html_options = {}) 177 def date_field(name, value, format = '%Y-%m-%d', datepicker_options = {}, html_options = {})
146 datepicker_options[:disabled] ||= false 178 datepicker_options[:disabled] ||= false
147 datepicker_options[:alt_field] ||= '' 179 datepicker_options[:alt_field] ||= ''
app/helpers/language_helper.rb
@@ -13,19 +13,20 @@ module LanguageHelper @@ -13,19 +13,20 @@ module LanguageHelper
13 13
14 alias :calendar_date_select_language :tinymce_language 14 alias :calendar_date_select_language :tinymce_language
15 15
16 - def language_chooser(environment, options = {})  
17 - return if environment.locales.size < 2 16 + def language_chooser(environment=nil, options = {})
  17 + locales = environment.nil? ? Noosfero.locales : environment.locales
  18 + return if locales.size < 2
18 current = language 19 current = language
19 separator = options[:separator] || ' &mdash; ' 20 separator = options[:separator] || ' &mdash; '
20 21
21 if options[:element] == 'dropdown' 22 if options[:element] == 'dropdown'
22 select_tag('lang', 23 select_tag('lang',
23 - options_for_select(environment.locales.map{|code,name| [name, code]}, current), 24 + options_for_select(locales.map{|code,name| [name, code]}, current),
24 :onchange => "document.location.href= #{url_for(params.merge(:lang => 'LANGUAGE')).inspect}.replace(/LANGUAGE/, this.value) ;", 25 :onchange => "document.location.href= #{url_for(params.merge(:lang => 'LANGUAGE')).inspect}.replace(/LANGUAGE/, this.value) ;",
25 :help => _('The language you choose here is the language used for options, buttons, etc. It does not affect the language of the content created by other users.') 26 :help => _('The language you choose here is the language used for options, buttons, etc. It does not affect the language of the content created by other users.')
26 ) 27 )
27 else 28 else
28 - languages = environment.locales.map do |code,name| 29 + languages = locales.map do |code,name|
29 if code == current 30 if code == current
30 content_tag('strong', name) 31 content_tag('strong', name)
31 else 32 else
app/models/approve_article.rb
@@ -48,7 +48,7 @@ class ApproveArticle &lt; Task @@ -48,7 +48,7 @@ class ApproveArticle &lt; Task
48 end 48 end
49 49
50 def perform 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) 51 + article.copy!(:name => name, :abstract => abstract, :body => body, :profile => target, :reference_article => article, :parent => article_parent, :highlighted => highlighted, :source => article.source, :last_changed_by_id => article.author_id)
52 end 52 end
53 53
54 def title 54 def title
app/models/article.rb
@@ -13,10 +13,18 @@ class Article &lt; ActiveRecord::Base @@ -13,10 +13,18 @@ class Article &lt; ActiveRecord::Base
13 # xss_terminate plugin can't sanitize array fields 13 # xss_terminate plugin can't sanitize array fields
14 before_save :sanitize_tag_list 14 before_save :sanitize_tag_list
15 15
  16 + before_create do |article|
  17 + if article.last_changed_by_id
  18 + article.author_name = Person.find(article.last_changed_by_id).name
  19 + end
  20 + end
  21 +
16 belongs_to :profile 22 belongs_to :profile
17 validates_presence_of :profile_id, :name 23 validates_presence_of :profile_id, :name
18 validates_presence_of :slug, :path, :if => lambda { |article| !article.name.blank? } 24 validates_presence_of :slug, :path, :if => lambda { |article| !article.name.blank? }
19 25
  26 + validates_length_of :name, :maximum => 150
  27 +
20 validates_uniqueness_of :slug, :scope => ['profile_id', 'parent_id'], :message => N_('The title (article name) is already being used by another article, please use another title.'), :if => lambda { |article| !article.slug.blank? } 28 validates_uniqueness_of :slug, :scope => ['profile_id', 'parent_id'], :message => N_('The title (article name) is already being used by another article, please use another title.'), :if => lambda { |article| !article.slug.blank? }
21 29
22 belongs_to :last_changed_by, :class_name => 'Person', :foreign_key => 'last_changed_by_id' 30 belongs_to :last_changed_by, :class_name => 'Person', :foreign_key => 'last_changed_by_id'
@@ -289,7 +297,7 @@ class Article &lt; ActiveRecord::Base @@ -289,7 +297,7 @@ class Article &lt; ActiveRecord::Base
289 if last_comment 297 if last_comment
290 {:date => last_comment.created_at, :author_name => last_comment.author_name, :author_url => last_comment.author_url} 298 {:date => last_comment.created_at, :author_name => last_comment.author_name, :author_url => last_comment.author_url}
291 else 299 else
292 - {:date => updated_at, :author_name => author.name, :author_url => author.url} 300 + {:date => updated_at, :author_name => author_name, :author_url => author_url}
293 end 301 end
294 end 302 end
295 303
@@ -441,7 +449,7 @@ class Article &lt; ActiveRecord::Base @@ -441,7 +449,7 @@ class Article &lt; ActiveRecord::Base
441 end 449 end
442 450
443 def allow_post_content?(user = nil) 451 def allow_post_content?(user = nil)
444 - user && (user.has_permission?('post_content', profile) || allow_publish_content?(user) && (user == self.creator)) 452 + user && (user.has_permission?('post_content', profile) || allow_publish_content?(user) && (user == author))
445 end 453 end
446 454
447 def allow_publish_content?(user = nil) 455 def allow_publish_content?(user = nil)
@@ -496,7 +504,6 @@ class Article &lt; ActiveRecord::Base @@ -496,7 +504,6 @@ class Article &lt; ActiveRecord::Base
496 :slug, 504 :slug,
497 :updated_at, 505 :updated_at,
498 :created_at, 506 :created_at,
499 - :last_changed_by_id,  
500 :version, 507 :version,
501 :lock_version, 508 :lock_version,
502 :type, 509 :type,
@@ -539,15 +546,24 @@ class Article &lt; ActiveRecord::Base @@ -539,15 +546,24 @@ class Article &lt; ActiveRecord::Base
539 end 546 end
540 547
541 def author 548 def author
542 - if reference_article  
543 - reference_article.author 549 + if versions.empty?
  550 + last_changed_by
544 else 551 else
545 - last_changed_by || profile 552 + author_id = versions.first.last_changed_by_id
  553 + Person.exists?(author_id) ? Person.find(author_id) : nil
546 end 554 end
547 end 555 end
548 556
549 def author_name 557 def author_name
550 - setting[:author_name].blank? ? author.name : setting[:author_name] 558 + author ? author.name : (setting[:author_name] || _('Unknown'))
  559 + end
  560 +
  561 + def author_url
  562 + author ? author.url : nil
  563 + end
  564 +
  565 + def author_id
  566 + author ? author.id : nil
551 end 567 end
552 568
553 alias :active_record_cache_key :cache_key 569 alias :active_record_cache_key :cache_key
@@ -572,11 +588,6 @@ class Article &lt; ActiveRecord::Base @@ -572,11 +588,6 @@ class Article &lt; ActiveRecord::Base
572 truncate sanitize_html(self.lead), :length => 170, :omission => '...' 588 truncate sanitize_html(self.lead), :length => 170, :omission => '...'
573 end 589 end
574 590
575 - def creator  
576 - creator_id = versions[0][:last_changed_by_id]  
577 - creator_id && Profile.find(creator_id)  
578 - end  
579 -  
580 def notifiable? 591 def notifiable?
581 false 592 false
582 end 593 end
app/models/category.rb
@@ -13,6 +13,16 @@ class Category &lt; ActiveRecord::Base @@ -13,6 +13,16 @@ class Category &lt; ActiveRecord::Base
13 {:conditions => ['parent_id is null and environment_id = ?', environment.id ]} 13 {:conditions => ['parent_id is null and environment_id = ?', environment.id ]}
14 } 14 }
15 15
  16 + named_scope :on_level, lambda { |parent| {:conditions => {:parent_id => parent}} }
  17 +
  18 + named_scope :sub_categories, lambda { |category|
  19 + {:conditions => ['categories.path LIKE ? AND categories.id != ?', "%#{category.slug}%", category.id]}
  20 + }
  21 +
  22 + named_scope :sub_tree, lambda { |category|
  23 + {:conditions => ['categories.path LIKE ?', "%#{category.slug}%"]}
  24 + }
  25 +
16 acts_as_filesystem 26 acts_as_filesystem
17 27
18 has_many :article_categorizations, :dependent => :destroy 28 has_many :article_categorizations, :dependent => :destroy
app/models/comment.rb
@@ -74,6 +74,10 @@ class Comment &lt; ActiveRecord::Base @@ -74,6 +74,10 @@ class Comment &lt; ActiveRecord::Base
74 self.find(:all, :order => 'created_at desc, id desc', :limit => limit) 74 self.find(:all, :order => 'created_at desc, id desc', :limit => limit)
75 end 75 end
76 76
  77 + def notification_emails
  78 + self.article.profile.notification_emails - [self.author_email || self.email]
  79 + end
  80 +
77 after_save :notify_article 81 after_save :notify_article
78 after_destroy :notify_article 82 after_destroy :notify_article
79 def notify_article 83 def notify_article
@@ -114,7 +118,7 @@ class Comment &lt; ActiveRecord::Base @@ -114,7 +118,7 @@ class Comment &lt; ActiveRecord::Base
114 118
115 def notify_by_mail 119 def notify_by_mail
116 if source.kind_of?(Article) && article.notify_comments? 120 if source.kind_of?(Article) && article.notify_comments?
117 - if !article.profile.notification_emails.empty? 121 + if !notification_emails.empty?
118 Comment::Notifier.deliver_mail(self) 122 Comment::Notifier.deliver_mail(self)
119 end 123 end
120 emails = article.followers - [author_email] 124 emails = article.followers - [author_email]
@@ -174,7 +178,7 @@ class Comment &lt; ActiveRecord::Base @@ -174,7 +178,7 @@ class Comment &lt; ActiveRecord::Base
174 class Notifier < ActionMailer::Base 178 class Notifier < ActionMailer::Base
175 def mail(comment) 179 def mail(comment)
176 profile = comment.article.profile 180 profile = comment.article.profile
177 - recipients profile.notification_emails 181 + recipients comment.notification_emails
178 from "#{profile.environment.name} <#{profile.environment.contact_email}>" 182 from "#{profile.environment.name} <#{profile.environment.contact_email}>"
179 subject _("[%s] you got a new comment!") % [profile.environment.name] 183 subject _("[%s] you got a new comment!") % [profile.environment.name]
180 body :recipient => profile.nickname || profile.name, 184 body :recipient => profile.nickname || profile.name,
@@ -224,6 +228,7 @@ class Comment &lt; ActiveRecord::Base @@ -224,6 +228,7 @@ class Comment &lt; ActiveRecord::Base
224 def spam! 228 def spam!
225 self.spam = true 229 self.spam = true
226 self.save! 230 self.save!
  231 + SpammerLogger.log(ip_address, self)
227 Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam)) 232 Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam))
228 self 233 self
229 end 234 end
app/models/enterprise_homepage.rb
@@ -5,7 +5,7 @@ class EnterpriseHomepage &lt; Article @@ -5,7 +5,7 @@ class EnterpriseHomepage &lt; Article
5 end 5 end
6 6
7 def self.short_description 7 def self.short_description
8 - __('Enterprise homepage.') 8 + __('Enterprise homepage')
9 end 9 end
10 10
11 def self.description 11 def self.description
app/models/environment.rb
@@ -233,8 +233,10 @@ class Environment &lt; ActiveRecord::Base @@ -233,8 +233,10 @@ class Environment &lt; ActiveRecord::Base
233 settings[:message_for_member_invitation] || InviteMember.mail_template 233 settings[:message_for_member_invitation] || InviteMember.mail_template
234 end 234 end
235 235
  236 + settings_items :min_signup_delay, :type => Integer, :default => 3 #seconds
236 settings_items :activation_blocked_text, :type => String 237 settings_items :activation_blocked_text, :type => String
237 - settings_items :message_for_disabled_enterprise, :type => String 238 + settings_items :message_for_disabled_enterprise, :type => String,
  239 + :default => _('This enterprise needs to be enabled.')
238 settings_items :location, :type => String 240 settings_items :location, :type => String
239 settings_items :layout_template, :type => String, :default => 'default' 241 settings_items :layout_template, :type => String, :default => 'default'
240 settings_items :homepage, :type => String 242 settings_items :homepage, :type => String
app/models/event.rb
@@ -38,7 +38,7 @@ class Event &lt; Article @@ -38,7 +38,7 @@ class Event &lt; Article
38 filter_iframes :body, :link, :address, :whitelist => lambda { profile && profile.environment && profile.environment.trusted_sites_for_iframe } 38 filter_iframes :body, :link, :address, :whitelist => lambda { profile && profile.environment && profile.environment.trusted_sites_for_iframe }
39 39
40 def self.description 40 def self.description
41 - _('A calendar event') 41 + _('A calendar event.')
42 end 42 end
43 43
44 def self.short_description 44 def self.short_description
app/models/organization.rb
@@ -78,6 +78,8 @@ class Organization &lt; Profile @@ -78,6 +78,8 @@ class Organization &lt; Profile
78 country 78 country
79 tag_list 79 tag_list
80 template_id 80 template_id
  81 + district
  82 + address_reference
81 ] 83 ]
82 84
83 def self.fields 85 def self.fields
@@ -96,8 +98,8 @@ class Organization &lt; Profile @@ -96,8 +98,8 @@ class Organization &lt; Profile
96 [] 98 []
97 end 99 end
98 100
99 - N_('Display name'); N_('Description'); N_('Contact person'); N_('Contact email'); N_('Acronym'); N_('Foundation year'); N_('Legal form'); N_('Economic activity'); N_('Management information'); N_('Tag list')  
100 - settings_items :display_name, :description, :contact_person, :contact_email, :acronym, :foundation_year, :legal_form, :economic_activity, :management_information 101 + N_('Display name'); N_('Description'); N_('Contact person'); N_('Contact email'); N_('Acronym'); N_('Foundation year'); N_('Legal form'); N_('Economic activity'); N_('Management information'); N_('Tag list'); N_('District'); N_('Address reference')
  102 + settings_items :display_name, :description, :contact_person, :contact_email, :acronym, :foundation_year, :legal_form, :economic_activity, :management_information, :district, :address_reference
101 103
102 validates_format_of :foundation_year, :with => Noosfero::Constants::INTEGER_FORMAT 104 validates_format_of :foundation_year, :with => Noosfero::Constants::INTEGER_FORMAT
103 validates_format_of :contact_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda { |org| !org.contact_email.blank? }) 105 validates_format_of :contact_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda { |org| !org.contact_email.blank? })
app/models/person.rb
@@ -67,6 +67,9 @@ class Person &lt; Profile @@ -67,6 +67,9 @@ class Person &lt; Profile
67 :order => 'total DESC', 67 :order => 'total DESC',
68 :conditions => ['action_tracker.created_at >= ? OR action_tracker.id IS NULL', ActionTracker::Record::RECENT_DELAY.days.ago] 68 :conditions => ['action_tracker.created_at >= ? OR action_tracker.id IS NULL', ActionTracker::Record::RECENT_DELAY.days.ago]
69 69
  70 + named_scope :abusers, :joins => :abuse_complaints, :conditions => ['tasks.status = 3'], :select => 'DISTINCT profiles.*'
  71 + named_scope :non_abusers, :joins => "LEFT JOIN tasks ON profiles.id = tasks.requestor_id AND tasks.type='AbuseComplaint'", :conditions => ["tasks.status != 3 OR tasks.id is NULL"], :select => "DISTINCT profiles.*"
  72 +
70 after_destroy do |person| 73 after_destroy do |person|
71 Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy } 74 Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy }
72 end 75 end
@@ -144,6 +147,9 @@ class Person &lt; Profile @@ -144,6 +147,9 @@ class Person &lt; Profile
144 contact_phone 147 contact_phone
145 contact_information 148 contact_information
146 description 149 description
  150 + image
  151 + district
  152 + address_reference
147 ] 153 ]
148 154
149 validates_multiparameter_assignments 155 validates_multiparameter_assignments
@@ -198,8 +204,8 @@ class Person &lt; Profile @@ -198,8 +204,8 @@ class Person &lt; Profile
198 N_('Education'); N_('Custom education'); N_('Custom area of study'); 204 N_('Education'); N_('Custom education'); N_('Custom area of study');
199 settings_items :formation, :custom_formation, :custom_area_of_study 205 settings_items :formation, :custom_formation, :custom_area_of_study
200 206
201 - N_('Contact information'); N_('City'); N_('State'); N_('Country'); N_('Sex'); N_('Zip code')  
202 - settings_items :photo, :contact_information, :sex, :city, :state, :country, :zip_code 207 + N_('Contact information'); N_('City'); N_('State'); N_('Country'); N_('Sex'); N_('Zip code'); N_('District'); N_('Address reference')
  208 + settings_items :photo, :contact_information, :sex, :city, :state, :country, :zip_code, :district, :address_reference
203 209
204 extend SetProfileRegionFromCityState::ClassMethods 210 extend SetProfileRegionFromCityState::ClassMethods
205 set_profile_region_from_city_state 211 set_profile_region_from_city_state
@@ -440,6 +446,10 @@ class Person &lt; Profile @@ -440,6 +446,10 @@ class Person &lt; Profile
440 abuse_report.save! 446 abuse_report.save!
441 end 447 end
442 448
  449 + def abuser?
  450 + AbuseComplaint.finished.where(:requestor_id => self).count > 0
  451 + end
  452 +
443 def control_panel_settings_button 453 def control_panel_settings_button
444 {:title => _('Edit Profile'), :icon => 'edit-profile'} 454 {:title => _('Edit Profile'), :icon => 'edit-profile'}
445 end 455 end
app/models/product.rb
@@ -23,6 +23,10 @@ class Product &lt; ActiveRecord::Base @@ -23,6 +23,10 @@ class Product &lt; ActiveRecord::Base
23 23
24 named_scope :more_recent, :order => "created_at DESC" 24 named_scope :more_recent, :order => "created_at DESC"
25 25
  26 + named_scope :from_category, lambda { |category|
  27 + {:joins => :product_category, :conditions => ['categories.path LIKE ?', "%#{category.slug}%"]} if category
  28 + }
  29 +
26 after_update :save_image 30 after_update :save_image
27 31
28 def lat 32 def lat
app/models/profile.rb
@@ -141,6 +141,10 @@ class Profile &lt; ActiveRecord::Base @@ -141,6 +141,10 @@ class Profile &lt; ActiveRecord::Base
141 141
142 acts_as_having_settings :field => :data 142 acts_as_having_settings :field => :data
143 143
  144 + def settings
  145 + data
  146 + end
  147 +
144 settings_items :redirect_l10n, :type => :boolean, :default => false 148 settings_items :redirect_l10n, :type => :boolean, :default => false
145 settings_items :public_content, :type => :boolean, :default => true 149 settings_items :public_content, :type => :boolean, :default => true
146 settings_items :description 150 settings_items :description
@@ -229,7 +233,7 @@ class Profile &lt; ActiveRecord::Base @@ -229,7 +233,7 @@ class Profile &lt; ActiveRecord::Base
229 if myregion 233 if myregion
230 myregion.hierarchy.reverse.first(2).map(&:name).join(separator) 234 myregion.hierarchy.reverse.first(2).map(&:name).join(separator)
231 else 235 else
232 - %w[address city state country_name zip_code ].map {|item| (self.respond_to?(item) && !self.send(item).blank?) ? self.send(item) : nil }.compact.join(separator) 236 + %w[address district city state country_name zip_code ].map {|item| (self.respond_to?(item) && !self.send(item).blank?) ? self.send(item) : nil }.compact.join(separator)
233 end 237 end
234 end 238 end
235 239
@@ -463,6 +467,10 @@ class Profile &lt; ActiveRecord::Base @@ -463,6 +467,10 @@ class Profile &lt; ActiveRecord::Base
463 { :profile => identifier, :controller => 'profile_editor', :action => 'index' } 467 { :profile => identifier, :controller => 'profile_editor', :action => 'index' }
464 end 468 end
465 469
  470 + def tasks_url
  471 + { :profile => identifier, :controller => 'tasks', :action => 'index', :host => default_hostname }
  472 + end
  473 +
466 def leave_url(reload = false) 474 def leave_url(reload = false)
467 { :profile => identifier, :controller => 'profile', :action => 'leave', :reload => reload } 475 { :profile => identifier, :controller => 'profile', :action => 'leave', :reload => reload }
468 end 476 end
@@ -694,7 +702,7 @@ private :generate_url, :url_options @@ -694,7 +702,7 @@ private :generate_url, :url_options
694 def custom_footer_expanded 702 def custom_footer_expanded
695 footer = custom_footer 703 footer = custom_footer
696 if footer 704 if footer
697 - %w[contact_person contact_email contact_phone location address economic_activity city state country zip_code].each do |att| 705 + %w[contact_person contact_email contact_phone location address district address_reference economic_activity city state country zip_code].each do |att|
698 if self.respond_to?(att) && footer.match(/\{[^{]*#{att}\}/) 706 if self.respond_to?(att) && footer.match(/\{[^{]*#{att}\}/)
699 if !self.send(att).nil? && !self.send(att).blank? 707 if !self.send(att).nil? && !self.send(att).blank?
700 footer = footer.gsub(/\{([^{]*)#{att}\}/, '\1' + self.send(att)) 708 footer = footer.gsub(/\{([^{]*)#{att}\}/, '\1' + self.send(att))
app/models/raw_html_article.rb
@@ -5,11 +5,11 @@ class RawHTMLArticle &lt; TextArticle @@ -5,11 +5,11 @@ class RawHTMLArticle &lt; TextArticle
5 end 5 end
6 6
7 def self.short_description 7 def self.short_description
8 - _('Raw HTML text article.') 8 + _('Raw HTML text article')
9 end 9 end
10 10
11 def self.description 11 def self.description
12 - _('Allows HTML without filter (only for admins)') 12 + _('Allows HTML without filter (only for admins).')
13 end 13 end
14 14
15 xss_terminate :only => [ ] 15 xss_terminate :only => [ ]
app/models/spammer_logger.rb 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +class SpammerLogger < Logger
  2 + @logpath = File.join(Rails.root, 'log', "#{ENV['RAILS_ENV']}_spammers.log")
  3 + @logger = new(@logpath)
  4 +
  5 + def self.log(spammer_ip, object=nil)
  6 + if object
  7 + if object.kind_of?(Comment)
  8 + @logger << "[#{Time.now.strftime("%F %T %z")}] Comment-id: #{object.id} IP: #{spammer_ip}\n"
  9 + end
  10 + else
  11 + @logger << "[#{Time.now.strftime("%F %T %z")}] IP: #{spammer_ip}\n"
  12 + end
  13 + end
  14 +
  15 + def self.clean_log
  16 + File.delete(@logpath) if File.exists?(@logpath)
  17 + end
  18 +
  19 + def self.reload_log
  20 + clean_log
  21 + @logger = new(@logpath)
  22 + end
  23 +
  24 +end
app/models/task.rb
@@ -267,7 +267,10 @@ class Task &lt; ActiveRecord::Base @@ -267,7 +267,10 @@ class Task &lt; ActiveRecord::Base
267 end 267 end
268 268
269 named_scope :pending, :conditions => { :status => Task::Status::ACTIVE } 269 named_scope :pending, :conditions => { :status => Task::Status::ACTIVE }
270 - named_scope :finished, :conditions => { :status => [Task::Status::CANCELLED, Task::Status::FINISHED] } 270 + named_scope :hidden, :conditions => { :status => Task::Status::HIDDEN }
  271 + named_scope :finished, :conditions => { :status => Task::Status::FINISHED }
  272 + named_scope :canceled, :conditions => { :status => Task::Status::CANCELLED }
  273 + named_scope :closed, :conditions => { :status => [Task::Status::CANCELLED, Task::Status::FINISHED] }
271 named_scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } 274 named_scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] }
272 named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } 275 named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} }
273 named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } 276 named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} }
app/models/task_mailer.rb
@@ -14,7 +14,7 @@ class TaskMailer &lt; ActionMailer::Base @@ -14,7 +14,7 @@ class TaskMailer &lt; ActionMailer::Base
14 14
15 recipients task.target.notification_emails 15 recipients task.target.notification_emails
16 16
17 - url_for_tasks_list = task.target.kind_of?(Environment) ? '' : url_for(task.target.url.merge(:controller => 'tasks', :action => 'index')) 17 + url_for_tasks_list = task.target.kind_of?(Environment) ? '' : url_for(task.target.tasks_url)
18 18
19 from self.class.generate_from(task) 19 from self.class.generate_from(task)
20 subject '[%s] %s' % [task.environment.name, task.target_notification_description] 20 subject '[%s] %s' % [task.environment.name, task.target_notification_description]
app/models/tiny_mce_article.rb
1 class TinyMceArticle < TextArticle 1 class TinyMceArticle < TextArticle
2 2
3 def self.short_description 3 def self.short_description
4 - _('Text article with visual editor.') 4 + _('Text article with visual editor')
5 end 5 end
6 6
7 def self.description 7 def self.description
app/models/user.rb
@@ -31,7 +31,7 @@ class User &lt; ActiveRecord::Base @@ -31,7 +31,7 @@ class User &lt; ActiveRecord::Base
31 after_create do |user| 31 after_create do |user|
32 user.person ||= Person.new 32 user.person ||= Person.new
33 user.person.attributes = user.person_data.merge(:identifier => user.login, :user => user, :environment_id => user.environment_id) 33 user.person.attributes = user.person_data.merge(:identifier => user.login, :user => user, :environment_id => user.environment_id)
34 - user.person.name ||= user.login 34 + user.person.name ||= user.name
35 user.person.visible = false unless user.activated? 35 user.person.visible = false unless user.activated?
36 user.person.save! 36 user.person.save!
37 if user.environment.enabled?('skip_new_user_email_confirmation') 37 if user.environment.enabled?('skip_new_user_email_confirmation')
app/views/account/_signup_form.rhtml
1 -<%= error_messages_for :user, :person %> 1 +<% if @block_bot %>
  2 + <div class="atention" style="font-size: 150%;">
  3 + <strong><%=_('Are you a robot?')%></strong> <br />
  4 + <%=_('Please, prove that you are human by filling the captcha.')%>
  5 + </div>
  6 +<% end %>
  7 +
  8 +<% @profile_data = @person %>
  9 +
  10 +<%= error_messages_for :user, :person, :header_message => _('The account could not be created') %>
  11 +
  12 +<% labelled_form_for :user, @user, :html => { :multipart => true, :id => 'signup-form', :honeypot => true } do |f| %>
2 13
3 -<% labelled_form_for :user, @user, :html => { :multipart => true, :id => 'signup-form' } do |f| %> 14 +<input type="hidden" id="signup_time_key" name="signup_time_key" />
  15 +<script type="text/javascript">
  16 + jQuery.ajax({
  17 + type: "POST",
  18 + url: "<%= url_for :controller=>'account', :action=>'signup_time' %>",
  19 + dataType: 'json',
  20 + success: function(data) {
  21 + if (data.ok) jQuery('#signup_time_key').val(data.key);
  22 + }
  23 + });
  24 +</script>
4 25
5 <%= hidden_field_tag :invitation_code, @invitation_code %> 26 <%= hidden_field_tag :invitation_code, @invitation_code %>
6 27
7 <div id='signup-form-header'> 28 <div id='signup-form-header'>
8 29
9 - <span id="signup-domain"><%= environment.default_hostname %>/</span>  
10 - <div id='signup-login'>  
11 - <div id='signup-login-field'>  
12 - <%= required f.text_field(:login, :onchange => 'this.value = convToValidLogin(this.value);', :rel => s_('signup|Login')) %>  
13 - <div id='url-check'><p>&nbsp;</p></div> 30 + <div id='signup-formfield-group'>
  31 + <%= label(:user, :login, _('Username'), {:class => 'formlabel'}) %>
  32 + <span id="signup-domain"><%= environment.default_hostname %>/</span>
  33 + <div id='signup-login'>
  34 + <div id='signup-login-field' class='formfield'>
  35 + <%= required text_field(:user, :login, :id => 'user_login', :onchange => 'this.value = convToValidLogin(this.value);') %>
  36 + <div id='url-check'><p>&nbsp;</p></div>
  37 + </div>
  38 + <%= content_tag(:small, _('Choose your login name carefully! It will be your network access and you will not be able to change it later.'), :id => 'signup-balloon') %>
  39 + <br style="clear: both;" />
14 </div> 40 </div>
15 - <%= content_tag(:small, _('Choose your login name carefully! It will be your network access and you will not be able to change it later.'), :id => 'signup-balloon') %>  
16 - <br style="clear: both;" />  
17 </div> 41 </div>
18 <%= observe_field 'user_login', 42 <%= observe_field 'user_login',
19 :url => { :action => 'check_url' }, 43 :url => { :action => 'check_url' },
@@ -26,20 +50,19 @@ @@ -26,20 +50,19 @@
26 50
27 <div id='signup-password'> 51 <div id='signup-password'>
28 <%= required f.password_field(:password, :id => 'user_pw') %> 52 <%= required f.password_field(:password, :id => 'user_pw') %>
29 - <%= f.text_field(:password_clear, :value => _('password')) %>  
30 <%= content_tag(:small,_('Choose a password that you can remember easily. It must have at least 4 characters.'), :id => 'password-balloon') %> 53 <%= content_tag(:small,_('Choose a password that you can remember easily. It must have at least 4 characters.'), :id => 'password-balloon') %>
31 <div id='fake-check'><p>&nbsp;</p></div> 54 <div id='fake-check'><p>&nbsp;</p></div>
32 </div> 55 </div>
33 56
34 <div id='signup-password-confirmation'> 57 <div id='signup-password-confirmation'>
35 <%= required f.password_field(:password_confirmation) %> 58 <%= required f.password_field(:password_confirmation) %>
36 - <%= f.text_field(:password_confirmation_clear, :value => _('password confirmation')) %> 59 + <%= content_tag(:small,_('We need to be sure that you filled in your password correctly. Confirm you password.'), :id => 'password-confirmation-balloon') %>
37 <div id='password-check'><p>&nbsp;</p></div> 60 <div id='password-check'><p>&nbsp;</p></div>
38 </div> 61 </div>
39 62
40 <div id='signup-email'> 63 <div id='signup-email'>
41 - <%= required f.text_field(:email, :rel => _('e-Mail')) %>  
42 - <%= content_tag(:small,_('This e-mail address will be used to contact you.')) %> 64 + <%= required f.text_field(:email) %>
  65 + <%= content_tag(:small,_('This e-mail address will be used to contact you.'), :id => 'email-balloon') %>
43 <div id='email-check'><p>&nbsp;</p></div> 66 <div id='email-check'><p>&nbsp;</p></div>
44 </div> 67 </div>
45 <%= observe_field "user_email", 68 <%= observe_field "user_email",
@@ -62,21 +85,23 @@ @@ -62,21 +85,23 @@
62 }" 85 }"
63 %> 86 %>
64 87
65 - <%= label :profile_data, :name %>  
66 - <%= required text_field(:profile_data, :name, :rel => _('Full name')) %> 88 + <div id='signup-name'>
  89 + <%= labelled_form_field(_('Full name'), text_field(:profile_data, :name)) %>
  90 + <%= content_tag(:small,_('Tell us your name, it will be used to identify yourself.'), :id => 'name-balloon') %>
  91 + </div>
67 92
68 </div> 93 </div>
69 94
70 <div id="signup-form-profile"> 95 <div id="signup-form-profile">
71 96
72 - <%= template_options(Person, 'profile_data') %>  
73 -  
74 <% labelled_fields_for :profile_data, @person do |f| %> 97 <% labelled_fields_for :profile_data, @person do |f| %>
75 <%= render :partial => 'profile_editor/person_form', :locals => {:f => f} %> 98 <%= render :partial => 'profile_editor/person_form', :locals => {:f => f} %>
76 <% end %> 99 <% end %>
77 100
78 <%= @plugins.dispatch(:signup_extra_contents).collect { |content| instance_eval(&content) }.join("") %> 101 <%= @plugins.dispatch(:signup_extra_contents).collect { |content| instance_eval(&content) }.join("") %>
79 102
  103 + <%= template_options(Person, 'profile_data') %>
  104 +
80 <% unless @terms_of_use.blank? %> 105 <% unless @terms_of_use.blank? %>
81 <div id='terms-of-use-box' class='formfieldline'> 106 <div id='terms-of-use-box' class='formfieldline'>
82 <%= labelled_check_box(_('I accept the %s') % link_to(_('terms of use'), {:controller => 'home', :action => 'terms'}, :target => '_blank'), 'user[terms_accepted]') %> 107 <%= labelled_check_box(_('I accept the %s') % link_to(_('terms of use'), {:controller => 'home', :action => 'terms'}, :target => '_blank'), 'user[terms_accepted]') %>
@@ -91,6 +116,8 @@ @@ -91,6 +116,8 @@
91 <% end %> 116 <% end %>
92 </div> 117 </div>
93 118
  119 +<%= recaptcha_tags :ajax => true, :display => {:theme => 'clean'} if @block_bot %>
  120 +
94 <p style="text-align: center"> 121 <p style="text-align: center">
95 <%= submit_button('save', _('Create my account')) %> 122 <%= submit_button('save', _('Create my account')) %>
96 </p> 123 </p>
@@ -99,70 +126,59 @@ @@ -99,70 +126,59 @@
99 126
100 <script type="text/javascript"> 127 <script type="text/javascript">
101 jQuery(function($) { 128 jQuery(function($) {
  129 +
  130 + $('#signup-form #user_login').css('width', 335 - $('#signup-domain').outerWidth());
  131 +
102 $('#signup-form input[type=text], #signup-form textarea').each(function() { 132 $('#signup-form input[type=text], #signup-form textarea').each(function() {
103 - if ($(this).attr('rel')) var default_value = $(this).attr('rel').toLowerCase();  
104 - if ($(this).val() == '') $(this).val(default_value);  
105 - $(this).bind('focus', function() {  
106 - if ($(this).val() == default_value) $(this).val('');  
107 - });  
108 $(this).bind('blur', function() { 133 $(this).bind('blur', function() {
109 if ($(this).val() == '') { 134 if ($(this).val() == '') {
110 - $(this).val(default_value);  
111 $(this).removeClass('filled-in'); 135 $(this).removeClass('filled-in');
112 } 136 }
113 else $(this).addClass('filled-in'); 137 else $(this).addClass('filled-in');
114 }); 138 });
115 }); 139 });
116 140
117 - $('#signup-form').bind('submit', function() {  
118 - $('#signup-form input[type=text], #signup-form textarea').each(function() {  
119 - if ($(this).attr('rel')) var default_value = $(this).attr('rel').toLowerCase();  
120 - if ($(this).val() == default_value) $(this).val('');  
121 - });  
122 - return true;  
123 - });  
124 -  
125 - $('#user_password_clear, #user_password_confirmation_clear').show();  
126 - $('#user_password_clear, #user_password_confirmation_clear').unbind();  
127 - $('#user_pw, #user_password_confirmation').hide();  
128 - $('#user_password_clear').focus(function() {  
129 - $(this).hide();  
130 - $('#user_pw').show();  
131 - $('#user_pw').focus();  
132 - });  
133 $('#user_pw').focus(function() { 141 $('#user_pw').focus(function() {
134 $('#password-balloon').fadeIn('slow'); 142 $('#password-balloon').fadeIn('slow');
135 }); 143 });
136 - $('#user_pw').blur(function() {  
137 - if ($(this).val() == '') {  
138 - $('#user_password_clear').show();  
139 - $(this).hide();  
140 - }  
141 - });  
142 - $('#user_password_confirmation_clear').focus(function() {  
143 - $(this).hide();  
144 - $('#user_password_confirmation').show();  
145 - $('#user_password_confirmation').focus(); 144 + $('#user_password_confirmation').focus(function() {
  145 + $('#password-confirmation-balloon').fadeIn('slow');
146 }); 146 });
147 $('#user_password_confirmation, #user_pw').blur(function() { 147 $('#user_password_confirmation, #user_pw').blur(function() {
148 - if ($('#user_password_confirmation').val() == '') {  
149 - $('#user_password_confirmation_clear').show();  
150 - $('#user_password_confirmation').hide();  
151 - } else if ($('#user_password_confirmation').val() == $('#user_pw').val()) {  
152 - $('#user_password_confirmation').addClass('validated').removeClass('invalid');  
153 - $('#user_pw').removeClass('invalid_input').addClass('valid_input');  
154 - $('#password-check').html("<p>&nbsp;</p>");  
155 - } else if ($('#user_password_confirmation').val() != $('#user_pw').val()) {  
156 - $('#user_password_confirmation').removeClass('validated').addClass('invalid');  
157 - $('#user_pw').addClass('invalid_input').removeClass('valid_input');  
158 - $('#password-check').html("<p><span class='invalid'><%= _('Passwords don\'t match') %></span></p>"); 148 + if ($('#user_password_confirmation').val() != '') {
  149 + if ($('#user_password_confirmation').val() == $('#user_pw').val()) {
  150 + $('#user_password_confirmation').addClass('validated').removeClass('invalid');
  151 + $('#user_pw').removeClass('invalid_input').addClass('valid_input');
  152 + $('#password-check').html("<p>&nbsp;</p>");
  153 + } else if ($('#user_password_confirmation').val() != $('#user_pw').val()) {
  154 + $('#user_password_confirmation').removeClass('validated').addClass('invalid');
  155 + $('#user_pw').addClass('invalid_input').removeClass('valid_input');
  156 + $('#password-check').html("<p><span class='invalid'><%= _('Passwords don\'t match') %></span></p>");
  157 + }
159 } 158 }
160 $('#password-balloon').fadeOut('slow'); 159 $('#password-balloon').fadeOut('slow');
  160 + $('#password-confirmation-balloon').fadeOut('slow');
161 }); 161 });
162 $('#user_login').focus(function() { 162 $('#user_login').focus(function() {
163 $('#signup-balloon').fadeIn('slow'); 163 $('#signup-balloon').fadeIn('slow');
164 }); 164 });
165 $('#user_login').blur(function() { $('#signup-balloon').fadeOut('slow'); }); 165 $('#user_login').blur(function() { $('#signup-balloon').fadeOut('slow'); });
166 $('#signup-form').validate({ rules: { 'user[email]': { email: true } }, messages: { 'user[email]' : '' } }); 166 $('#signup-form').validate({ rules: { 'user[email]': { email: true } }, messages: { 'user[email]' : '' } });
  167 + $('#user_email').focus(function() {
  168 + $('#email-balloon').fadeIn('slow');
  169 + });
  170 + $('#user_email').blur(function() {
  171 + $('#email-balloon').fadeOut('slow');
  172 + });
  173 + $('#profile_data_name').focus(function() {
  174 + $('#name-balloon').fadeIn('slow');
  175 + });
  176 + $('#profile_data_name').blur(function() {
  177 + $('#name-balloon').fadeOut('slow');
  178 + if ($(this).val() == '') {
  179 + $(this).removeClass('validated');
  180 + }
  181 + else $(this).addClass('validated');
  182 + });
167 }); 183 });
168 </script> 184 </script>
app/views/catalog/index.rhtml
1 <% extra_content = [] %> 1 <% extra_content = [] %>
2 <% extra_content_list = [] %> 2 <% extra_content_list = [] %>
3 3
4 -<ul id="product-list">  
5 - <li><h1><%= _('Products/Services') %></h1></li> 4 +<h1><%= _('Products/Services') %></h1>
6 5
  6 +<%= breadcrumb(@category) if params[:level] %>
  7 +
  8 +<div class='l-sidebar-left-bar'>
  9 + <ul>
  10 + <% if @categories.size > 0 %>
  11 + <% @categories.each do |category| %>
  12 + <%= category_link(category) %>
  13 + <%= category_sub_links(category) %>
  14 + <% end %>
  15 + <% else %>
  16 + <%= content_tag('li', _('There are no sub-categories for %s') % @category.name, :style => 'color: #555753; padding-bottom: 0.5em;') %>
  17 + <% end %>
  18 + </ul>
  19 +</div>
  20 +
  21 +<ul id="product-list" class="l-sidebar-left-content">
7 <% @products.each do |product| %> 22 <% @products.each do |product| %>
8 <% extra_content = @plugins.dispatch(:catalog_item_extras, product).collect { |content| instance_eval(&content) } %> 23 <% extra_content = @plugins.dispatch(:catalog_item_extras, product).collect { |content| instance_eval(&content) } %>
9 <% extra_content_list = @plugins.dispatch(:catalog_list_item_extras, product).collect { |content| instance_eval(&content) } %> 24 <% extra_content_list = @plugins.dispatch(:catalog_list_item_extras, product).collect { |content| instance_eval(&content) } %>
10 25
11 - <li class="product <%= "not-available" unless product.available %>"> 26 + <% status = [] %>
  27 + <% status << 'not-available' if !product.available %>
  28 + <% status << 'highlighted' if product.highlighted %>
  29 +
  30 + <li id="product-<%= product.id %>" class="product <%= status.join(' ') %>">
12 <ul> 31 <ul>
13 <li class="product-image-link"> 32 <li class="product-image-link">
  33 + <% if product.highlighted? %>
  34 + <%= link_to image_tag(themed_path('/images/star.png'), :class => 'star', :alt => _('Highlighted product')), product_path(product) %>
  35 + <% end %>
14 <% if product.image %> 36 <% if product.image %>
15 <div class="zoomable-image"> 37 <div class="zoomable-image">
16 <%= link_to_product product, :class => 'product-big', :style => "background-image: url(#{product.default_image(:big)})" %> 38 <%= link_to_product product, :class => 'product-big', :style => "background-image: url(#{product.default_image(:big)})" %>
app/views/cms/_blog.rhtml
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 4
5 <%= render :file => 'shared/tiny_mce' %> 5 <%= render :file => 'shared/tiny_mce' %>
6 6
7 -<%= required f.text_field(:name, :size => '64', :onchange => "updateUrlField(this, 'article_slug')") %> 7 +<%= required f.text_field(:name, :size => '64', :maxlength => 150, :onchange => "updateUrlField(this, 'article_slug')") %>
8 8
9 <%= render :partial => 'general_fields' %> 9 <%= render :partial => 'general_fields' %>
10 10
app/views/cms/_event.rhtml
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 <%# TODO add Textile help here %> 3 <%# TODO add Textile help here %>
4 <%= render :file => 'shared/tiny_mce' %> 4 <%= render :file => 'shared/tiny_mce' %>
5 5
6 -<%= required f.text_field('name', :size => '64') %> 6 +<%= required f.text_field('name', :size => '64', :maxlength => 150) %>
7 7
8 <%= render :partial => 'general_fields' %> 8 <%= render :partial => 'general_fields' %>
9 <%= render :partial => 'translatable' %> 9 <%= render :partial => 'translatable' %>
app/views/cms/_folder.rhtml
1 <%= required_fields_message %> 1 <%= required_fields_message %>
2 2
3 -<%= required f.text_field('name', :size => '64') %> 3 +<%= required f.text_field('name', :size => '64', :maxlength => 150) %>
4 <%= render :partial => 'general_fields' %> 4 <%= render :partial => 'general_fields' %>
5 5
6 <%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 3, :cols => 64)) %> 6 <%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 3, :cols => 64)) %>
app/views/cms/_forum.rhtml
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 4
5 <%= render :file => 'shared/tiny_mce' %> 5 <%= render :file => 'shared/tiny_mce' %>
6 6
7 -<%= required f.text_field(:name, :size => '64', :onchange => "updateUrlField(this, 'article_slug')") %> 7 +<%= required f.text_field(:name, :size => '64', :maxlength => 150, :onchange => "updateUrlField(this, 'article_slug')") %>
8 8
9 <%= render :partial => 'general_fields' %> 9 <%= render :partial => 'general_fields' %>
10 10
app/views/cms/_gallery.rhtml
1 <%= required_fields_message %> 1 <%= required_fields_message %>
2 2
3 -<%= required f.text_field('name', :size => '64') %> 3 +<%= required f.text_field('name', :size => '64', :maxlength => 150) %>
4 4
5 <%= render :partial => 'general_fields' %> 5 <%= render :partial => 'general_fields' %>
6 6
app/views/cms/_published_article.rhtml
1 -<%= f.text_field 'name', :size => '64' %> 1 +<%= f.text_field 'name', :size => '64', :maxlength => 150 %>
2 <%= render :partial => 'general_fields' %> 2 <%= render :partial => 'general_fields' %>
3 3
4 <p><%= _('This is a republication of "%s", by %s.') % [link_to(h(@article.reference_article.name), @article.reference_article.url), @article.reference_article.profile.name] %></p> 4 <p><%= _('This is a republication of "%s", by %s.') % [link_to(h(@article.reference_article.name), @article.reference_article.url), @article.reference_article.profile.name] %></p>
app/views/cms/_raw_html_article.rhtml
1 <%= required_fields_message %> 1 <%= required_fields_message %>
2 2
3 -<%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64')) %> 3 +<%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64', :maxlength => 150)) %>
4 4
5 <%= render :partial => 'general_fields' %> 5 <%= render :partial => 'general_fields' %>
6 <%= render :partial => 'translatable' %> 6 <%= render :partial => 'translatable' %>
app/views/cms/_textile_article.rhtml
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 2
3 <%# TODO add Textile help here %> 3 <%# TODO add Textile help here %>
4 4
5 -<%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '72')) %> 5 +<%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '72', :maxlength => 150)) %>
6 6
7 <%= render :partial => 'general_fields' %> 7 <%= render :partial => 'general_fields' %>
8 <%= render :partial => 'translatable' %> 8 <%= render :partial => 'translatable' %>
app/views/cms/_tiny_mce_article.rhtml
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 <%= render :file => 'shared/tiny_mce' %> 3 <%= render :file => 'shared/tiny_mce' %>
4 4
5 <div> 5 <div>
6 - <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64')) %> 6 + <%= required labelled_form_field(_('Title'), text_field(:article, 'name', :size => '64', :maxlength => 150)) %>
7 7
8 <%= render :partial => 'general_fields' %> 8 <%= render :partial => 'general_fields' %>
9 <%= render :partial => 'translatable' %> 9 <%= render :partial => 'translatable' %>
app/views/cms/select_article_type.rhtml
@@ -2,9 +2,9 @@ @@ -2,9 +2,9 @@
2 2
3 <ul id="article_types"> 3 <ul id="article_types">
4 <% for type in @article_types %> 4 <% for type in @article_types %>
5 - <% action = type[:name] == 'UploadedFile' ? {:action => 'upload_files'} : {:action => 'new', :type => type[:name]} %> 5 + <% action = type[:class].name == 'UploadedFile' ? {:action => 'upload_files'} : {:action => 'new', :type => type[:class].name} %>
6 <% content_tag('a', :href => url_for(action.merge(:parent_id => @parent_id, :back_to => @back_to))) do %> 6 <% content_tag('a', :href => url_for(action.merge(:parent_id => @parent_id, :back_to => @back_to))) do %>
7 - <li class="<%= icon_for_new_article(type[:name]) %>" onmouseover="javascript: jQuery(this).addClass('mouseover')" onmouseout="jQuery(this).removeClass('mouseover')"> 7 + <li class="<%= icon_for_new_article(type[:class]) %>" onmouseover="javascript: jQuery(this).addClass('mouseover')" onmouseout="jQuery(this).removeClass('mouseover')">
8 <strong><%= type[:short_description] %></strong> 8 <strong><%= type[:short_description] %></strong>
9 <div class='description'><%= type[:description] %></div> 9 <div class='description'><%= type[:description] %></div>
10 </li> 10 </li>
app/views/content_viewer/_article_toolbar.rhtml
@@ -34,11 +34,11 @@ @@ -34,11 +34,11 @@
34 <%= expirable_button @page, :locale, content, url %> 34 <%= expirable_button @page, :locale, content, url %>
35 <% end %> 35 <% end %>
36 36
37 - <%= colorbox_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) %> 37 + <%= colorbox_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) unless remove_content_button(:new) %>
38 <% end %> 38 <% end %>
39 39
40 <% if @page.accept_uploads? && @page.allow_create?(user) %> 40 <% if @page.accept_uploads? && @page.allow_create?(user) %>
41 - <%= button('upload-file', _('Upload files'), profile.admin_url.merge(:controller => 'cms', :action => 'upload_files', :parent_id => (@page.folder? ? @page : @page.parent))) %> 41 + <%= button('upload-file', _('Upload files'), profile.admin_url.merge(:controller => 'cms', :action => 'upload_files', :parent_id => (@page.folder? ? @page : @page.parent))) unless remove_content_button(:upload)%>
42 <% end %> 42 <% end %>
43 43
44 <% if !@page.allow_create?(user) && profile.community? && (@page.blog? || @page.parent && @page.parent.blog?) && !remove_content_button(:suggest) %> 44 <% if !@page.allow_create?(user) && profile.community? && (@page.blog? || @page.parent && @page.parent.blog?) && !remove_content_button(:suggest) %>
app/views/profile/_create_article.rhtml
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 <div class='profile-activity-lead'> 9 <div class='profile-activity-lead'>
10 <div class='article-name'><%= link_to(activity.params['name'], activity.params['url']) %></div> 10 <div class='article-name'><%= link_to(activity.params['name'], activity.params['url']) %></div>
11 <span title='<%= activity.target.class.short_description %>' class='profile-activity-icon icon-new icon-new<%= activity.target.class.icon_name %>'></span> 11 <span title='<%= activity.target.class.short_description %>' class='profile-activity-icon icon-new icon-new<%= activity.target.class.icon_name %>'></span>
12 - <%= image_tag(activity.params['first_image']) unless activity.params['first_image'].blank? %><%= strip_tags(truncate(activity.params['lead'], :length => 1000, :ommision => '...')).gsub(/(\xA0|\xC2|\s)+/, ' ').gsub(/^\s+/, '') %> <small><%= link_to(_('See more'), activity.params['url']) unless activity.get_lead.blank? %></small> 12 + <%= image_tag(activity.params['first_image']) unless activity.params['first_image'].blank? %><%= strip_tags(truncate(activity.params['lead'], :length => 1000, :ommision => '...')).gsub(/(\xC2\xA0|\s)+/, ' ').gsub(/^\s+/, '') %> <small><%= link_to(_('See more'), activity.params['url']) unless activity.get_lead.blank? %></small>
13 </div> 13 </div>
14 <%= content_tag(:p, link_to(_('See complete forum'), activity.get_url), :class => 'see-forum') if activity.target.is_a?(Forum) %> 14 <%= content_tag(:p, link_to(_('See complete forum'), activity.get_url), :class => 'see-forum') if activity.target.is_a?(Forum) %>
15 <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p> 15 <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p>
app/views/profile/communities.rhtml
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 <% cache_timeout(profile.communities_cache_key(params), 4.hour) do %> 5 <% cache_timeout(profile.communities_cache_key(params), 4.hour) do %>
6 <ul class='profile-list'> 6 <ul class='profile-list'>
7 <% @communities.each do |community| %> 7 <% @communities.each do |community| %>
8 - <li><%= profile_image_link(community)%></li> 8 + <%= profile_image_link(community)%>
9 <% end %> 9 <% end %>
10 </ul> 10 </ul>
11 11
app/views/profile_editor/_person_form.rhtml
@@ -21,6 +21,8 @@ @@ -21,6 +21,8 @@
21 <%= optional_field(@person, 'city', f.text_field(:city, :rel => _('City'))) %> 21 <%= optional_field(@person, 'city', f.text_field(:city, :rel => _('City'))) %>
22 <%= optional_field(@person, 'zip_code', labelled_form_field(_('ZIP code'), text_field(:profile_data, :zip_code, :rel => _('ZIP code')))) %> 22 <%= optional_field(@person, 'zip_code', labelled_form_field(_('ZIP code'), text_field(:profile_data, :zip_code, :rel => _('ZIP code')))) %>
23 <%= optional_field(@person, 'address', labelled_form_field(_('Address (street and number)'), text_field(:profile_data, :address, :rel => _('Address')))) %> 23 <%= optional_field(@person, 'address', labelled_form_field(_('Address (street and number)'), text_field(:profile_data, :address, :rel => _('Address')))) %>
  24 +<%= optional_field(@person, 'address_reference', labelled_form_field(_('Address reference'), text_field(:profile_data, :address_reference, :rel => _('Address reference')))) %>
  25 +<%= optional_field(@person, 'district', labelled_form_field(_('District'), text_field(:profile_data, :district, :rel => _('District')))) %>
24 26
25 <% optional_field(@person, 'schooling') do %> 27 <% optional_field(@person, 'schooling') do %>
26 <div class="formfieldline"> 28 <div class="formfieldline">
app/views/search/_product.rhtml
1 -<% extra_content = @plugins.dispatch(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %> 1 +<% extra_content = @plugins.dispatch(:asset_product_extras, product).collect { |content| instance_eval(&content) } %>
2 <% extra_properties = @plugins.dispatch(:asset_product_properties, product)%> 2 <% extra_properties = @plugins.dispatch(:asset_product_properties, product)%>
3 3
4 -<li class="search-product-item"> 4 +<li class="search-product-item <%= 'highlighted' if product.highlighted? %>">
5 5
6 <div class="search-product-item-first-column"> 6 <div class="search-product-item-first-column">
7 <%= render :partial => 'search/image', :object => product %> 7 <%= render :partial => 'search/image', :object => product %>
app/views/shared/_organization_custom_fields.rhtml
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 <%= optional_field(profile, 'economic_activity', f.text_field(:economic_activity)) %> 10 <%= optional_field(profile, 'economic_activity', f.text_field(:economic_activity)) %>
11 <%= optional_field(profile, 'management_information', f.text_area(:management_information, :rows => 5)) %> 11 <%= optional_field(profile, 'management_information', f.text_area(:management_information, :rows => 5)) %>
12 <%= optional_field(profile, 'address', labelled_form_field(_('Address (street and number)'), text_field(object_name, :address))) %> 12 <%= optional_field(profile, 'address', labelled_form_field(_('Address (street and number)'), text_field(object_name, :address))) %>
  13 +<%= optional_field(profile, 'address_reference', labelled_form_field(_('Address reference'), text_field(object_name, :address_reference))) %>
  14 +<%= optional_field(profile, 'district', labelled_form_field(_('District'), text_field(object_name, :district))) %>
13 <%= optional_field(profile, 'zip_code', labelled_form_field(_('ZIP code'), text_field(object_name, :zip_code))) %> 15 <%= optional_field(profile, 'zip_code', labelled_form_field(_('ZIP code'), text_field(object_name, :zip_code))) %>
14 <%= optional_field(profile, 'city', f.text_field(:city)) %> 16 <%= optional_field(profile, 'city', f.text_field(:city)) %>
15 <%= optional_field(profile, 'state', f.text_field(:state)) %> 17 <%= optional_field(profile, 'state', f.text_field(:state)) %>
config/initializers/plugins.rb
@@ -3,4 +3,5 @@ require &#39;noosfero/plugin/hot_spot&#39; @@ -3,4 +3,5 @@ require &#39;noosfero/plugin/hot_spot&#39;
3 require 'noosfero/plugin/manager' 3 require 'noosfero/plugin/manager'
4 require 'noosfero/plugin/active_record' 4 require 'noosfero/plugin/active_record'
5 require 'noosfero/plugin/mailer_base' 5 require 'noosfero/plugin/mailer_base'
  6 +require 'noosfero/plugin/settings'
6 Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS 7 Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS
db/migrate/20130111232201_aggressive_indexing_strategy3.rb 0 → 100644
@@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
  1 +class AggressiveIndexingStrategy3 < ActiveRecord::Migration
  2 + def self.up
  3 + add_index :articles, :slug
  4 + add_index :articles, :parent_id
  5 + add_index :articles, :profile_id
  6 + add_index :articles, :name
  7 +
  8 + add_index :article_versions, :article_id
  9 +
  10 + add_index :comments, [:source_id, :spam]
  11 +
  12 + add_index :profiles, :identifier
  13 +
  14 + add_index :friendships, :person_id
  15 + add_index :friendships, :friend_id
  16 + add_index :friendships, [:person_id, :friend_id], :uniq => true
  17 +
  18 + add_index :external_feeds, :blog_id
  19 + end
  20 +
  21 + def self.down
  22 + remove_index :articles, :slug
  23 + remove_index :articles, :parent_id
  24 + remove_index :articles, :profile_id
  25 + remove_index :articles, :name
  26 +
  27 + remove_index :article_versions, :article_id
  28 +
  29 + remove_index :comments, [:source_id, :spam]
  30 +
  31 + remove_index :profiles, :identifier
  32 +
  33 + remove_index :friendships, :person_id
  34 + remove_index :friendships, :friend_id
  35 + remove_index :friendships, [:person_id, :friend_id]
  36 +
  37 + remove_index :external_feeds, :blog_id
  38 + end
  39 +end
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 # 9 #
10 # It's strongly recommended to check this file into your version control system. 10 # It's strongly recommended to check this file into your version control system.
11 11
12 -ActiveRecord::Schema.define(:version => 20121008185303) do 12 +ActiveRecord::Schema.define(:version => 20130111232201) do
13 13
14 create_table "abuse_reports", :force => true do |t| 14 create_table "abuse_reports", :force => true do |t|
15 t.integer "reporter_id" 15 t.integer "reporter_id"
@@ -88,6 +88,8 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do @@ -88,6 +88,8 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do
88 t.integer "license_id" 88 t.integer "license_id"
89 end 89 end
90 90
  91 + add_index "article_versions", ["article_id"], :name => "index_article_versions_on_article_id"
  92 +
91 create_table "articles", :force => true do |t| 93 create_table "articles", :force => true do |t|
92 t.string "name" 94 t.string "name"
93 t.string "slug" 95 t.string "slug"
@@ -129,6 +131,10 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do @@ -129,6 +131,10 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do
129 t.integer "license_id" 131 t.integer "license_id"
130 end 132 end
131 133
  134 + add_index "articles", ["name"], :name => "index_articles_on_name"
  135 + add_index "articles", ["parent_id"], :name => "index_articles_on_parent_id"
  136 + add_index "articles", ["profile_id"], :name => "index_articles_on_profile_id"
  137 + add_index "articles", ["slug"], :name => "index_articles_on_slug"
132 add_index "articles", ["translation_of_id"], :name => "index_articles_on_translation_of_id" 138 add_index "articles", ["translation_of_id"], :name => "index_articles_on_translation_of_id"
133 139
134 create_table "articles_categories", :id => false, :force => true do |t| 140 create_table "articles_categories", :id => false, :force => true do |t|
@@ -217,6 +223,8 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do @@ -217,6 +223,8 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do
217 t.string "referrer" 223 t.string "referrer"
218 end 224 end
219 225
  226 + add_index "comments", ["source_id", "spam"], :name => "index_comments_on_source_id_and_spam"
  227 +
220 create_table "contact_lists", :force => true do |t| 228 create_table "contact_lists", :force => true do |t|
221 t.text "list" 229 t.text "list"
222 t.string "error_fetching" 230 t.string "error_fetching"
@@ -280,6 +288,7 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do @@ -280,6 +288,7 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do
280 t.integer "update_errors", :default => 0 288 t.integer "update_errors", :default => 0
281 end 289 end
282 290
  291 + add_index "external_feeds", ["blog_id"], :name => "index_external_feeds_on_blog_id"
283 add_index "external_feeds", ["enabled"], :name => "index_external_feeds_on_enabled" 292 add_index "external_feeds", ["enabled"], :name => "index_external_feeds_on_enabled"
284 add_index "external_feeds", ["fetched_at"], :name => "index_external_feeds_on_fetched_at" 293 add_index "external_feeds", ["fetched_at"], :name => "index_external_feeds_on_fetched_at"
285 294
@@ -295,6 +304,10 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do @@ -295,6 +304,10 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do
295 t.string "group" 304 t.string "group"
296 end 305 end
297 306
  307 + add_index "friendships", ["friend_id"], :name => "index_friendships_on_friend_id"
  308 + add_index "friendships", ["person_id", "friend_id"], :name => "index_friendships_on_person_id_and_friend_id"
  309 + add_index "friendships", ["person_id"], :name => "index_friendships_on_person_id"
  310 +
298 create_table "images", :force => true do |t| 311 create_table "images", :force => true do |t|
299 t.integer "parent_id" 312 t.integer "parent_id"
300 t.string "content_type" 313 t.string "content_type"
@@ -446,6 +459,7 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do @@ -446,6 +459,7 @@ ActiveRecord::Schema.define(:version =&gt; 20121008185303) do
446 end 459 end
447 460
448 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id" 461 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id"
  462 + add_index "profiles", ["identifier"], :name => "index_profiles_on_identifier"
449 add_index "profiles", ["region_id"], :name => "index_profiles_on_region_id" 463 add_index "profiles", ["region_id"], :name => "index_profiles_on_region_id"
450 464
451 create_table "qualifier_certifiers", :force => true do |t| 465 create_table "qualifier_certifiers", :force => true do |t|
debian/changelog
  1 +noosfero (0.41.0) unstable; urgency=low
  2 +
  3 + * Features version with anti spam-bot measures
  4 +
  5 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Mon, 28 Jan 2013 10:20:21 +0000
  6 +
  7 +noosfero (0.40.0) unstable; urgency=low
  8 +
  9 + * Features version release
  10 +
  11 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Sat, 19 Jan 2013 21:58:06 +0000
  12 +
  13 +noosfero (0.39.3) unstable; urgency=low
  14 +
  15 + * Bugfixes release
  16 +
  17 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Sat, 19 Jan 2013 19:27:04 +0000
  18 +
  19 +noosfero (0.39.2) unstable; urgency=low
  20 +
  21 + * Bugfixes release
  22 +
  23 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Sat, 12 Jan 2013 10:13:46 +0000
  24 +
  25 +noosfero (0.39.1) unstable; urgency=low
  26 +
  27 + * Bugfixes release
  28 +
  29 + -- Daniela Soares Feitosa <daniela@colivre.coop.br> Thu, 20 Dec 2012 15:47:55 -0200
  30 +
  31 +noosfero (0.39.0) unstable; urgency=low
  32 +
  33 + * Features version release
  34 +
  35 + -- Daniela Soares Feitosa <daniela@colivre.coop.br> Fri, 07 Dec 2012 09:53:42 -0200
  36 +
1 noosfero (0.39.0~1) UNRELEASED; urgency=low 37 noosfero (0.39.0~1) UNRELEASED; urgency=low
2 38
3 * Pre-release to test the antispam mechanism. 39 * Pre-release to test the antispam mechanism.
4 40
5 -- Antonio Terceiro <terceiro@debian.org> Thu, 30 Aug 2012 14:55:10 -0300 41 -- Antonio Terceiro <terceiro@debian.org> Thu, 30 Aug 2012 14:55:10 -0300
6 42
  43 +noosfero (0.38.3) unstable; urgency=low
  44 +
  45 + * Bugfixes release
  46 +
  47 + -- Daniela Soares Feitosa <daniela@colivre.coop.br> Wed, 07 Nov 2012 20:25:51 -0200
  48 +
7 noosfero (0.38.2) unstable; urgency=low 49 noosfero (0.38.2) unstable; urgency=low
8 50
9 * Bugfixes release 51 * Bugfixes release
debian/noosfero.install
@@ -32,6 +32,8 @@ debian/solr.yml etc/noosfero @@ -32,6 +32,8 @@ debian/solr.yml etc/noosfero
32 debian/thin.yml etc/noosfero 32 debian/thin.yml etc/noosfero
33 etc/logrotate.d/noosfero etc/logrotate.d 33 etc/logrotate.d/noosfero etc/logrotate.d
34 debian/noosfero.yml etc/noosfero 34 debian/noosfero.yml etc/noosfero
  35 +etc/noosfero/varnish-accept-language.vcl etc/noosfero
  36 +etc/noosfero/varnish-noosfero.vcl etc/noosfero
35 37
36 locale usr/share/noosfero 38 locale usr/share/noosfero
37 doc/noosfero usr/share/noosfero/doc 39 doc/noosfero usr/share/noosfero/doc
etc/noosfero/varnish-noosfero.vcl
1 sub vcl_recv { 1 sub vcl_recv {
2 if (req.request == "GET" || req.request == "HEAD") { 2 if (req.request == "GET" || req.request == "HEAD") {
3 if (req.http.Cookie) { 3 if (req.http.Cookie) {
4 - # We only care about the "_noosfero_session.*" cookie, used for  
5 - # authentication.  
6 - if (req.http.Cookie !~ "_noosfero_session.*" ) { 4 + # We only care about the "_noosfero_.*" cookies, used by Noosfero
  5 + if (req.http.Cookie !~ "_noosfero_.*" ) {
7 # strip all cookies 6 # strip all cookies
8 unset req.http.Cookie; 7 unset req.http.Cookie;
9 } 8 }
features/signup.feature
@@ -12,7 +12,7 @@ Feature: signup @@ -12,7 +12,7 @@ Feature: signup
12 | Username | josesilva | 12 | Username | josesilva |
13 | Password | secret | 13 | Password | secret |
14 | Password confirmation | secret | 14 | Password confirmation | secret |
15 - | Name | José da Silva | 15 + | Full name | José da Silva |
16 And I press "Create my account" 16 And I press "Create my account"
17 Then I should not be logged in 17 Then I should not be logged in
18 And I should receive an e-mail on josesilva@example.com 18 And I should receive an e-mail on josesilva@example.com
gitignore.example
@@ -19,9 +19,10 @@ public/image_uploads @@ -19,9 +19,10 @@ public/image_uploads
19 public/thumbnails 19 public/thumbnails
20 public/user_themes 20 public/user_themes
21 public/designs/themes/default 21 public/designs/themes/default
  22 +public/designs/themes/*
22 public/designs/icons/default 23 public/designs/icons/default
23 -public/javascripts/cache*.js  
24 -public/stylesheets/cache*.css 24 +public/javascripts/cache*
  25 +public/stylesheets/cache*
25 public/plugins 26 public/plugins
26 db/development.db 27 db/development.db
27 db/production.db 28 db/production.db
lib/noosfero.rb
@@ -2,7 +2,7 @@ require &#39;fast_gettext&#39; @@ -2,7 +2,7 @@ require &#39;fast_gettext&#39;
2 2
3 module Noosfero 3 module Noosfero
4 PROJECT = 'noosfero' 4 PROJECT = 'noosfero'
5 - VERSION = '0.39.0~1' 5 + VERSION = '0.41.0'
6 6
7 def self.pattern_for_controllers_in_directory(dir) 7 def self.pattern_for_controllers_in_directory(dir)
8 disjunction = controllers_in_directory(dir).join('|') 8 disjunction = controllers_in_directory(dir).join('|')
lib/noosfero/plugin.rb
@@ -130,7 +130,7 @@ class Noosfero::Plugin @@ -130,7 +130,7 @@ class Noosfero::Plugin
130 end 130 end
131 131
132 # -> Adds plugin-specific content types to CMS 132 # -> Adds plugin-specific content types to CMS
133 - # returns = { content type class } 133 + # returns = [ContentClass1, ContentClass2, ...]
134 def content_types 134 def content_types
135 nil 135 nil
136 end 136 end
@@ -161,7 +161,7 @@ class Noosfero::Plugin @@ -161,7 +161,7 @@ class Noosfero::Plugin
161 161
162 # -> Adds content to products on asset list 162 # -> Adds content to products on asset list
163 # returns = lambda block that creates html code 163 # returns = lambda block that creates html code
164 - def asset_product_extras(product, enterprise) 164 + def asset_product_extras(product)
165 nil 165 nil
166 end 166 end
167 167
@@ -384,7 +384,9 @@ class Noosfero::Plugin @@ -384,7 +384,9 @@ class Noosfero::Plugin
384 private 384 private
385 385
386 def content_actions 386 def content_actions
387 - %w[edit delete spread locale suggest home] 387 + #FIXME 'new' and 'upload' only works for content_remove. It should work for
  388 + #content_expire too.
  389 + %w[edit delete spread locale suggest home new upload]
388 end 390 end
389 391
390 end 392 end
lib/noosfero/plugin/settings.rb 0 → 100644
@@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
  1 +class Noosfero::Plugin::Settings
  2 +
  3 + def initialize(base, plugin, attributes = nil)
  4 + @base = base
  5 + @plugin = plugin
  6 + attributes ||= {}
  7 + attributes.each do |k,v|
  8 + self.send("#{k}=", v)
  9 + end
  10 + end
  11 +
  12 + def settings
  13 + @base.settings["#{@plugin.public_name}_plugin".to_sym] ||= {}
  14 + end
  15 +
  16 + def method_missing(method, *args, &block)
  17 + if method.to_s =~ /^(.+)=$/
  18 + set_setting($1, args.first)
  19 + elsif method.to_s =~ /^(.+)$/
  20 + get_setting($1)
  21 + end
  22 + end
  23 +
  24 + def get_setting(name)
  25 + if settings[name.to_sym].nil?
  26 + if @plugin.respond_to?("#{name}_default_setting")
  27 + @plugin.send("#{name}_default_setting")
  28 + else
  29 + nil
  30 + end
  31 + else
  32 + settings[name.to_sym]
  33 + end
  34 + end
  35 +
  36 + def set_setting(name, value)
  37 + settings[name.to_sym] = value
  38 + end
  39 +
  40 + def save!
  41 + @base.save!
  42 + end
  43 +
  44 +end
  45 +
plugins/anti_spam/README 0 → 100644
@@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
  1 +README - AntiSpam (AntiSpam Plugin)
  2 +=======================================
  3 +
  4 +Plugin that checks comments against a spam checking service compatible
  5 +with the Akismet API.
  6 +
  7 +
  8 +Enable Plugin
  9 +-------------
  10 +
  11 +Also, you need to enable AntiSpam Plugin at your Noosfero:
  12 +
  13 +cd <your_noosfero_dir>
  14 +./script/noosfero-plugins enable anti_spam
  15 +
  16 +
  17 +Activate Plugin
  18 +-------------
  19 +
  20 +As a Noosfero administrator user, go to administrator panel:
  21 +
  22 +- Click on "Plugins" option
  23 +- Click on "AntiSpam Plugin" check-box
  24 +
  25 +Configure Plugin
  26 +----------------
  27 +
  28 +As a Noosfero administrator user, go to administrator panel:
  29 +
  30 +- Click on "Configuration" below the "AntiSpam Plugin"
  31 +- Fill in the "API key" field with the key generated after signing up to
  32 + akismet: https://akismet.com/signup/
  33 +- Save your changes
plugins/anti_spam/controllers/anti_spam_plugin_admin_controller.rb
@@ -2,7 +2,7 @@ class AntiSpamPluginAdminController &lt; AdminController @@ -2,7 +2,7 @@ class AntiSpamPluginAdminController &lt; AdminController
2 append_view_path File.join(File.dirname(__FILE__) + '/../views') 2 append_view_path File.join(File.dirname(__FILE__) + '/../views')
3 3
4 def index 4 def index
5 - @settings = AntiSpamPlugin::Settings.new(environment, params[:settings]) 5 + @settings = Noosfero::Plugin::Settings.new(environment, AntiSpamPlugin, params[:settings])
6 if request.post? 6 if request.post?
7 @settings.save! 7 @settings.save!
8 redirect_to :action => 'index' 8 redirect_to :action => 'index'
plugins/anti_spam/lib/anti_spam_plugin.rb
@@ -8,6 +8,10 @@ class AntiSpamPlugin &lt; Noosfero::Plugin @@ -8,6 +8,10 @@ class AntiSpamPlugin &lt; Noosfero::Plugin
8 _("Checks comments against a spam checking service compatible with the Akismet API") 8 _("Checks comments against a spam checking service compatible with the Akismet API")
9 end 9 end
10 10
  11 + def self.host_default_setting
  12 + 'api.antispam.typepad.com'
  13 + end
  14 +
11 def check_comment_for_spam(comment) 15 def check_comment_for_spam(comment)
12 if rakismet_call(comment, :spam?) 16 if rakismet_call(comment, :spam?)
13 comment.spam = true 17 comment.spam = true
@@ -26,7 +30,7 @@ class AntiSpamPlugin &lt; Noosfero::Plugin @@ -26,7 +30,7 @@ class AntiSpamPlugin &lt; Noosfero::Plugin
26 protected 30 protected
27 31
28 def rakismet_call(comment, op) 32 def rakismet_call(comment, op)
29 - settings = AntiSpamPlugin::Settings.new(comment.environment) 33 + settings = Noosfero::Plugin::Settings.new(comment.environment, self.class)
30 34
31 Rakismet.host = settings.host 35 Rakismet.host = settings.host
32 Rakismet.key = settings.api_key 36 Rakismet.key = settings.api_key
plugins/anti_spam/lib/anti_spam_plugin/settings.rb
@@ -1,35 +0,0 @@ @@ -1,35 +0,0 @@
1 -class AntiSpamPlugin::Settings  
2 -  
3 - def initialize(environment, attributes = nil)  
4 - @environment = environment  
5 - attributes ||= {}  
6 - attributes.each do |k,v|  
7 - self.send("#{k}=", v)  
8 - end  
9 - end  
10 -  
11 - def settings  
12 - @environment.settings[:anti_spam_plugin] ||= {}  
13 - end  
14 -  
15 - def host  
16 - settings[:host] ||= 'api.antispam.typepad.com'  
17 - end  
18 -  
19 - def host=(value)  
20 - settings[:host] = value  
21 - end  
22 -  
23 - def api_key  
24 - settings[:api_key]  
25 - end  
26 -  
27 - def api_key=(value)  
28 - settings[:api_key] = value  
29 - end  
30 -  
31 - def save!  
32 - @environment.save!  
33 - end  
34 -  
35 -end  
plugins/anti_spam/lib/anti_spam_plugin/spaminator.rb
@@ -1,115 +0,0 @@ @@ -1,115 +0,0 @@
1 -require 'benchmark'  
2 -  
3 -class AntiSpamPlugin::Spaminator  
4 -  
5 - class << self  
6 - def run(environment)  
7 - instance = new(environment)  
8 - instance.run  
9 - end  
10 -  
11 - def benchmark(environment)  
12 - puts Benchmark.measure { run(environment) }  
13 - end  
14 - end  
15 -  
16 -  
17 - def initialize(environment)  
18 - @environment = environment  
19 - end  
20 -  
21 - def run  
22 - start_time = Time.now  
23 -  
24 - process_all_comments  
25 - process_all_people  
26 - process_people_without_network  
27 -  
28 - finish(start_time)  
29 - end  
30 -  
31 - protected  
32 -  
33 - def finish(start_time)  
34 - @environment.settings[:spaminator_last_run] = start_time  
35 - @environment.save!  
36 - end  
37 -  
38 - def conditions(table)  
39 - last_run = @environment.settings[:spaminator_last_run]  
40 - if last_run  
41 - ["profiles.environment_id = ? AND #{table}.created_at > ?", @environment.id, last_run]  
42 - else  
43 - [ "profiles.environment_id = ?", @environment.id]  
44 - end  
45 - end  
46 -  
47 - def process_all_comments  
48 - puts 'Processing comments ...'  
49 - i = 0  
50 - comments = Comment.joins("JOIN articles ON (comments.source_id = articles.id AND comments.source_type = 'Article') JOIN profiles ON (profiles.id = articles.profile_id)").where(conditions(:comments))  
51 - total = comments.count  
52 - comments.find_each do |comment|  
53 - puts "Comment #{i += 1}/#{total} (#{100*i/total}%)"  
54 - process_comment(comment)  
55 - end  
56 - end  
57 -  
58 - def process_all_people  
59 - puts 'Processing people ...'  
60 - i = 0  
61 - people = Person.where(conditions(:profiles))  
62 - total = people.count  
63 - people.find_each do |person|  
64 - puts "Person #{i += 1}/#{total} (#{100*i/total}%)"  
65 - process_person(person)  
66 - end  
67 - end  
68 -  
69 - def process_comment(comment)  
70 - comment.check_for_spam  
71 -  
72 - # TODO several comments with the same content:  
73 - # → disable author  
74 - # → mark all of them as spam  
75 -  
76 - # TODO check comments that contains URL's  
77 - end  
78 -  
79 - def process_person(person)  
80 - # person is author of more than 2 comments marked as spam  
81 - # → burn  
82 - #  
83 - number_of_spam_comments = Comment.spam.where(author_id => person.id).count  
84 - if number_of_spam_comments > 2  
85 - mark_as_spammer(person)  
86 - end  
87 - end  
88 -  
89 - def process_people_without_network  
90 - # people who signed up more than one month ago, have no friends and <= 1  
91 - # communities  
92 - #  
93 - # → burn  
94 - # → mark their comments as spam  
95 - #  
96 - Person.where(:environment_id => @environment.id).where(['created_at < ?', Time.now - 1.month]).find_each do |person|  
97 - # TODO progress indicator - see process_all_people above  
98 - number_of_friends = person.friends.count  
99 - number_of_communities = person.communities.count  
100 - if number_of_friends == 0 && number_of_communities <= 1  
101 - mark_as_spammer(person)  
102 - Comment.where(:author_id => person.id).find_each do |comment|  
103 - comment.spam!  
104 - end  
105 - end  
106 - end  
107 - end  
108 -  
109 - def mark_as_spammer(person)  
110 - # FIXME create an AbuseComplaint and finish instead of calling  
111 - # Person#disable directly  
112 - person.disable  
113 - end  
114 -  
115 -end  
plugins/anti_spam/test/unit/anti_spam_plugin/settings_test.rb
@@ -1,29 +0,0 @@ @@ -1,29 +0,0 @@
1 -require 'test_helper'  
2 -  
3 -class AntiSpamSettingsTest < ActiveSupport::TestCase  
4 -  
5 - def setup  
6 - @environment = Environment.new  
7 - @settings = AntiSpamPlugin::Settings.new(@environment)  
8 - end  
9 -  
10 - should 'store setttings in environment' do  
11 - @settings.host = 'foo.com'  
12 - @settings.api_key = '1234567890'  
13 - assert_equal 'foo.com', @environment.settings[:anti_spam_plugin][:host]  
14 - assert_equal '1234567890', @environment.settings[:anti_spam_plugin][:api_key]  
15 - assert_equal 'foo.com', @settings.host  
16 - assert_equal '1234567890', @settings.api_key  
17 - end  
18 -  
19 - should 'save environment on save' do  
20 - @environment.expects(:save!)  
21 - @settings.save!  
22 - end  
23 -  
24 - should 'use TypePad AntiSpam by default' do  
25 - assert_equal 'api.antispam.typepad.com', @settings.host  
26 - end  
27 -  
28 -  
29 -end  
plugins/anti_spam/test/unit/anti_spam_plugin/spaminator_test.rb
@@ -1,53 +0,0 @@ @@ -1,53 +0,0 @@
1 -require 'test_helper'  
2 -  
3 -class AntiSpamPluginSpaminatorTest < ActiveSupport::TestCase  
4 -  
5 - def setup  
6 - @environment = Environment.new  
7 - @environment.id = 99  
8 - @spaminator = AntiSpamPlugin::Spaminator.new(@environment)  
9 - @spaminator.stubs(:puts)  
10 - @now = Time.now  
11 - Time.stubs(:now).returns(@now)  
12 - end  
13 -  
14 - should 'search everything in the first run' do  
15 - assert_equal(['profiles.environment_id = ?',99], @spaminator.send(:conditions, nil))  
16 - end  
17 -  
18 - should 'search using recorded last date' do  
19 - @environment.settings[:spaminator_last_run] = @now  
20 - assert_equal(['profiles.environment_id = ? AND table.created_at > ?', 99, @now], @spaminator.send(:conditions, 'table'))  
21 - end  
22 -  
23 - should 'record time of last run in environment' do  
24 - @spaminator.expects(:process_all_comments)  
25 - @spaminator.expects(:process_all_people)  
26 - @environment.stubs(:save!)  
27 - @spaminator.run  
28 - assert_equal @now, @environment.settings[:spaminator_last_run]  
29 - end  
30 -  
31 - should 'find all comments' do  
32 - @spaminator.stubs(:process_comment)  
33 - @spaminator.send :process_all_comments  
34 - end  
35 -  
36 - should 'find all people' do  
37 - @spaminator.stubs(:process_person)  
38 - @spaminator.send :process_all_people  
39 - end  
40 -  
41 - should 'find all comments newer than a date' do  
42 - @environment.settings[:spaminator_last_run] = Time.now - 1.month  
43 - @spaminator.stubs(:process_comment)  
44 - @spaminator.send :process_all_comments  
45 - end  
46 -  
47 - should 'find all people newer than a date' do  
48 - @environment.settings[:spaminator_last_run] = Time.now - 1.month  
49 - @spaminator.stubs(:process_person)  
50 - @spaminator.send :process_all_people  
51 - end  
52 -  
53 -end  
plugins/anti_spam/test/unit/anti_spam_plugin_test.rb
@@ -7,7 +7,7 @@ class AntiSpamPluginTest &lt; ActiveSupport::TestCase @@ -7,7 +7,7 @@ class AntiSpamPluginTest &lt; ActiveSupport::TestCase
7 article = fast_create(TextileArticle, :profile_id => profile.id) 7 article = fast_create(TextileArticle, :profile_id => profile.id)
8 @comment = fast_create(Comment, :source_id => article.id, :source_type => 'Article') 8 @comment = fast_create(Comment, :source_id => article.id, :source_type => 'Article')
9 9
10 - @settings = AntiSpamPlugin::Settings.new(@comment.environment) 10 + @settings = Noosfero::Plugin::Settings.new(@comment.environment, AntiSpamPlugin)
11 @settings.api_key = 'b8b80ddb8084062d0c9119c945ce3bc3' 11 @settings.api_key = 'b8b80ddb8084062d0c9119c945ce3bc3'
12 @settings.save! 12 @settings.save!
13 13
plugins/bsc/test/functional/bsc_plugin_admin_controller_test.rb
1 require File.dirname(__FILE__) + '/../../../../test/test_helper' 1 require File.dirname(__FILE__) + '/../../../../test/test_helper'
2 require File.dirname(__FILE__) + '/../../controllers/bsc_plugin_admin_controller' 2 require File.dirname(__FILE__) + '/../../controllers/bsc_plugin_admin_controller'
3 -require File.dirname(__FILE__) + '/../../../../app/models/uploaded_file'  
4 3
5 # Re-raise errors caught by the controller. 4 # Re-raise errors caught by the controller.
6 class BscPluginAdminController; def rescue_action(e) raise e end; end 5 class BscPluginAdminController; def rescue_action(e) raise e end; end
plugins/bsc/test/functional/bsc_plugin_myprofile_controller_test.rb
1 require File.dirname(__FILE__) + '/../../../../test/test_helper' 1 require File.dirname(__FILE__) + '/../../../../test/test_helper'
2 require File.dirname(__FILE__) + '/../../controllers/bsc_plugin_myprofile_controller' 2 require File.dirname(__FILE__) + '/../../controllers/bsc_plugin_myprofile_controller'
3 -require File.dirname(__FILE__) + '/../../../../app/models/uploaded_file'  
4 3
5 # Re-raise errors caught by the controller. 4 # Re-raise errors caught by the controller.
6 class BscPluginMyprofileController; def rescue_action(e) raise e end; end 5 class BscPluginMyprofileController; def rescue_action(e) raise e end; end
plugins/bsc/test/unit/bsc_plugin/associate_enterprise_test.rb
1 require File.dirname(__FILE__) + '/../../../../../test/test_helper' 1 require File.dirname(__FILE__) + '/../../../../../test/test_helper'
2 -require File.dirname(__FILE__) + '/../../../../../app/models/uploaded_file'  
3 2
4 class BscPlugin::AssociateEnterpriseTest < ActiveSupport::TestCase 3 class BscPlugin::AssociateEnterpriseTest < ActiveSupport::TestCase
5 VALID_CNPJ = '94.132.024/0001-48' 4 VALID_CNPJ = '94.132.024/0001-48'
plugins/bsc/test/unit/bsc_plugin/contract_test.rb
1 require File.dirname(__FILE__) + '/../../../../../test/test_helper' 1 require File.dirname(__FILE__) + '/../../../../../test/test_helper'
2 -#require File.dirname(__FILE__) + '/../../../../../app/models/uploaded_file'  
3 -#require File.dirname(__FILE__) + '/../../../lib/ext/enterprise'  
4 2
5 class BscPlugin::ContractTest < ActiveSupport::TestCase 3 class BscPlugin::ContractTest < ActiveSupport::TestCase
6 def setup 4 def setup
plugins/bsc/test/unit/ext/product_test.rb
1 require File.dirname(__FILE__) + '/../../../../../test/test_helper' 1 require File.dirname(__FILE__) + '/../../../../../test/test_helper'
2 -require File.dirname(__FILE__) + '/../../../../../app/models/uploaded_file'  
3 2
4 class ProductTest < ActiveSupport::TestCase 3 class ProductTest < ActiveSupport::TestCase
5 VALID_CNPJ = '94.132.024/0001-48' 4 VALID_CNPJ = '94.132.024/0001-48'
plugins/mezuro/dependencies.rb
  1 +require 'rubygems'
1 require 'savon' 2 require 'savon'
2 require 'googlecharts' 3 require 'googlecharts'
plugins/shopping_cart/controllers/shopping_cart_plugin_controller.rb 0 → 100644
@@ -0,0 +1,320 @@ @@ -0,0 +1,320 @@
  1 +require 'base64'
  2 +
  3 +class ShoppingCartPluginController < PublicController
  4 +
  5 + include ShoppingCartPlugin::CartHelper
  6 + helper ShoppingCartPlugin::CartHelper
  7 +
  8 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  9 + before_filter :login_required, :only => []
  10 +
  11 + before_filter :login_required, :only => []
  12 +
  13 + def get
  14 + config =
  15 + if cart.nil?
  16 + { 'enterprise_id' => nil, 'hasProducts' => false }
  17 + else
  18 + { 'enterprise_id' => cart[:enterprise_id], 'hasProducts' => (cart[:items].keys.size > 0) }
  19 + end
  20 + render :text => config.to_json
  21 + end
  22 +
  23 + def add
  24 + product = find_product(params[:id])
  25 + if product && enterprise = validate_same_enterprise(product)
  26 + self.cart = { :enterprise_id => enterprise.id, :items => {} } if self.cart.nil?
  27 + self.cart[:items][product.id] = 0 if self.cart[:items][product.id].nil?
  28 + self.cart[:items][product.id] += 1
  29 + render :text => {
  30 + :ok => true,
  31 + :error => {:code => 0},
  32 + :products => [{
  33 + :id => product.id,
  34 + :name => product.name,
  35 + :price => get_price(product, enterprise.environment),
  36 + :description => product.description,
  37 + :picture => product.default_image(:minor),
  38 + :quantity => self.cart[:items][product.id]
  39 + }]
  40 + }.to_json
  41 + end
  42 + end
  43 +
  44 + def remove
  45 + id = params[:id].to_i
  46 + if validate_cart_presence && validate_cart_has_product(id)
  47 + self.cart[:items].delete(id)
  48 + self.cart = nil if self.cart[:items].empty?
  49 + render :text => {
  50 + :ok => true,
  51 + :error => {:code => 0},
  52 + :product_id => id
  53 + }.to_json
  54 + end
  55 + end
  56 +
  57 + def list
  58 + if validate_cart_presence
  59 + products = self.cart[:items].collect do |id, quantity|
  60 + product = Product.find(id)
  61 + { :id => product.id,
  62 + :name => product.name,
  63 + :price => get_price(product, product.enterprise.environment),
  64 + :description => product.description,
  65 + :picture => product.default_image(:minor),
  66 + :quantity => quantity
  67 + }
  68 + end
  69 + render :text => {
  70 + :ok => true,
  71 + :error => {:code => 0},
  72 + :products => products
  73 + }.to_json
  74 + end
  75 + end
  76 +
  77 + def update_quantity
  78 + quantity = params[:quantity].to_i
  79 + id = params[:id].to_i
  80 + if validate_cart_presence && validate_cart_has_product(id) && validate_item_quantity(quantity)
  81 + product = Product.find(id)
  82 + self.cart[:items][product.id] = quantity
  83 + render :text => {
  84 + :ok => true,
  85 + :error => {:code => 0},
  86 + :product_id => id,
  87 + :quantity => quantity
  88 + }.to_json
  89 + end
  90 + end
  91 +
  92 + def clean
  93 + self.cart = nil
  94 + render :text => {
  95 + :ok => true,
  96 + :error => {:code => 0}
  97 + }.to_json
  98 + end
  99 +
  100 + def buy
  101 + @cart = cart
  102 + @enterprise = environment.enterprises.find(cart[:enterprise_id])
  103 + @settings = Noosfero::Plugin::Settings.new(@enterprise, ShoppingCartPlugin)
  104 + render :layout => false
  105 + end
  106 +
  107 + def send_request
  108 + register_order(params[:customer], self.cart[:items])
  109 + begin
  110 + enterprise = environment.enterprises.find(cart[:enterprise_id])
  111 + ShoppingCartPlugin::Mailer.deliver_customer_notification(params[:customer], enterprise, self.cart[:items], params[:delivery_option])
  112 + ShoppingCartPlugin::Mailer.deliver_supplier_notification(params[:customer], enterprise, self.cart[:items], params[:delivery_option])
  113 + self.cart = nil
  114 + render :text => {
  115 + :ok => true,
  116 + :message => _('Request sent successfully. Check your email.'),
  117 + :error => {:code => 0}
  118 + }.to_json
  119 + rescue ActiveRecord::ActiveRecordError
  120 + render :text => {
  121 + :ok => false,
  122 + :error => {
  123 + :code => 6,
  124 + :message => exception.message
  125 + }
  126 + }.to_json
  127 + end
  128 + end
  129 +
  130 + def visibility
  131 + render :text => self.cart.has_key?(:visibility) ? self.cart[:visibility].to_json : true.to_json
  132 + end
  133 +
  134 + def show
  135 + begin
  136 + self.cart[:visibility] = true
  137 + render :text => {
  138 + :ok => true,
  139 + :message => _('Basket displayed.'),
  140 + :error => {:code => 0}
  141 + }.to_json
  142 + rescue Exception => exception
  143 + render :text => {
  144 + :ok => false,
  145 + :error => {
  146 + :code => 7,
  147 + :message => exception.message
  148 + }
  149 + }.to_json
  150 + end
  151 + end
  152 +
  153 + def hide
  154 + begin
  155 + self.cart[:visibility] = false
  156 + render :text => {
  157 + :ok => true,
  158 + :message => _('Basket hidden.'),
  159 + :error => {:code => 0}
  160 + }.to_json
  161 + rescue Exception => exception
  162 + render :text => {
  163 + :ok => false,
  164 + :error => {
  165 + :code => 8,
  166 + :message => exception.message
  167 + }
  168 + }.to_json
  169 + end
  170 + end
  171 +
  172 + def update_delivery_option
  173 + enterprise = environment.enterprises.find(cart[:enterprise_id])
  174 + settings = Noosfero::Plugin::Settings.new(enterprise, ShoppingCartPlugin)
  175 + delivery_price = settings.delivery_options[params[:delivery_option]]
  176 + delivery = Product.new(:name => params[:delivery_option], :price => delivery_price)
  177 + delivery.save(false)
  178 + items = self.cart[:items].clone
  179 + items[delivery.id] = 1
  180 + total_price = get_total_on_currency(items, environment)
  181 + delivery.destroy
  182 + render :text => {
  183 + :ok => true,
  184 + :delivery_price => float_to_currency_cart(delivery_price, environment),
  185 + :total_price => total_price,
  186 + :message => _('Delivery option updated.'),
  187 + :error => {:code => 0}
  188 + }.to_json
  189 + end
  190 +
  191 + private
  192 +
  193 + def validate_same_enterprise(product)
  194 + if self.cart && self.cart[:enterprise_id] && product.enterprise_id != self.cart[:enterprise_id]
  195 + render :text => {
  196 + :ok => false,
  197 + :error => {
  198 + :code => 1,
  199 + :message => _("Can't join items from different enterprises.")
  200 + }
  201 + }.to_json
  202 + return nil
  203 + end
  204 + product.enterprise
  205 + end
  206 +
  207 + def validate_cart_presence
  208 + if self.cart.nil?
  209 + render :text => {
  210 + :ok => false,
  211 + :error => {
  212 + :code => 2,
  213 + :message => _("There is no basket.")
  214 + }
  215 + }.to_json
  216 + return false
  217 + end
  218 + true
  219 + end
  220 +
  221 + def find_product(id)
  222 + begin
  223 + product = Product.find(id)
  224 + rescue ActiveRecord::RecordNotFound
  225 + render :text => {
  226 + :ok => false,
  227 + :error => {
  228 + :code => 3,
  229 + :message => _("This enterprise doesn't have this product.")
  230 + }
  231 + }.to_json
  232 + return nil
  233 + end
  234 + product
  235 + end
  236 +
  237 + def validate_cart_has_product(id)
  238 + if !self.cart[:items].has_key?(id)
  239 + render :text => {
  240 + :ok => false,
  241 + :error => {
  242 + :code => 4,
  243 + :message => _("The basket doesn't have this product.")
  244 + }
  245 + }.to_json
  246 + return false
  247 + end
  248 + true
  249 + end
  250 +
  251 + def validate_item_quantity(quantity)
  252 + if quantity.to_i < 1
  253 + render :text => {
  254 + :ok => false,
  255 + :error => {
  256 + :code => 5,
  257 + :message => _("Invalid quantity.")
  258 + }
  259 + }.to_json
  260 + return false
  261 + end
  262 + true
  263 + end
  264 +
  265 + def register_order(custumer, items)
  266 + new_items = {}
  267 + items.each do |id, quantity|
  268 + product = Product.find(id)
  269 + price = product.price || 0
  270 + new_items[id] = {:quantity => quantity, :price => price, :name => product.name}
  271 + end
  272 + ShoppingCartPlugin::PurchaseOrder.create!(
  273 + :seller => Enterprise.find(cart[:enterprise_id]),
  274 + :customer => user,
  275 + :status => ShoppingCartPlugin::PurchaseOrder::Status::OPENED,
  276 + :products_list => new_items,
  277 + :customer_delivery_option => params[:delivery_option],
  278 + :customer_payment => params[:customer][:payment],
  279 + :customer_change => params[:customer][:change],
  280 + :customer_name => params[:customer][:name],
  281 + :customer_email => params[:customer][:email],
  282 + :customer_contact_phone => params[:customer][:contact_phone],
  283 + :customer_address => params[:customer][:address],
  284 + :customer_district => params[:customer][:district],
  285 + :customer_city => params[:customer][:city],
  286 + :customer_zip_code => params[:customer][:zip_code]
  287 + )
  288 + end
  289 +
  290 + protected
  291 +
  292 + def cart
  293 + @cart ||=
  294 + begin
  295 + cookies[cookie_key] && YAML.load(Base64.decode64(cookies[cookie_key])) || nil
  296 + end
  297 + @cart
  298 + end
  299 +
  300 + def cart=(data)
  301 + @cart = data
  302 + end
  303 +
  304 + after_filter :save_cookie
  305 + def save_cookie
  306 + if @cart.nil?
  307 + cookies.delete(cookie_key, :path => '/plugin/shopping_cart')
  308 + else
  309 + cookies[cookie_key] = {
  310 + :value => Base64.encode64(@cart.to_yaml),
  311 + :path => "/plugin/shopping_cart"
  312 + }
  313 + end
  314 + end
  315 +
  316 + def cookie_key
  317 + :_noosfero_plugin_shopping_cart
  318 + end
  319 +
  320 +end
plugins/shopping_cart/controllers/shopping_cart_plugin_myprofile_controller.rb
@@ -4,9 +4,12 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController @@ -4,9 +4,12 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController
4 append_view_path File.join(File.dirname(__FILE__) + '/../views') 4 append_view_path File.join(File.dirname(__FILE__) + '/../views')
5 5
6 def edit 6 def edit
  7 + params[:settings] = treat_cart_options(params[:settings])
  8 +
  9 + @settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin, params[:settings])
7 if request.post? 10 if request.post?
8 begin 11 begin
9 - profile.update_attributes!(params[:profile_attr]) 12 + @settings.save!
10 session[:notice] = _('Option updated successfully.') 13 session[:notice] = _('Option updated successfully.')
11 rescue Exception => exception 14 rescue Exception => exception
12 session[:notice] = _('Option wasn\'t updated successfully.') 15 session[:notice] = _('Option wasn\'t updated successfully.')
@@ -46,4 +49,25 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController @@ -46,4 +49,25 @@ class ShoppingCartPluginMyprofileController &lt; MyProfileController
46 order.save! 49 order.save!
47 redirect_to :action => 'reports', :from => params[:context_from], :to => params[:context_to], :filter_status => params[:context_status] 50 redirect_to :action => 'reports', :from => params[:context_from], :to => params[:context_to], :filter_status => params[:context_status]
48 end 51 end
  52 +
  53 + private
  54 +
  55 + def treat_cart_options(settings)
  56 + return if settings.blank?
  57 + settings[:enabled] = settings[:enabled] == '1'
  58 + settings[:delivery] = settings[:delivery] == '1'
  59 + settings[:free_delivery_price] = settings[:free_delivery_price].blank? ? nil : settings[:free_delivery_price].to_d
  60 + settings[:delivery_options] = treat_delivery_options(settings[:delivery_options])
  61 + settings
  62 + end
  63 +
  64 + def treat_delivery_options(params)
  65 + result = {}
  66 + params[:options].size.times do |counter|
  67 + if params[:options][counter].present? && params[:prices][counter].present?
  68 + result[params[:options][counter]] = params[:prices][counter]
  69 + end
  70 + end
  71 + result
  72 + end
49 end 73 end
plugins/shopping_cart/controllers/shopping_cart_plugin_profile_controller.rb
@@ -1,248 +0,0 @@ @@ -1,248 +0,0 @@
1 -include ShoppingCartPlugin::CartHelper  
2 -  
3 -class ShoppingCartPluginProfileController < ProfileController  
4 - append_view_path File.join(File.dirname(__FILE__) + '/../views')  
5 - before_filter :login_required, :only => []  
6 -  
7 - before_filter :login_required, :only => []  
8 -  
9 - def add  
10 - session[:cart] = { :enterprise_id => profile.id, :items => {} } if session[:cart].nil?  
11 - if validate_same_enterprise && product = validate_enterprise_has_product(params[:id])  
12 - session[:cart][:items][product.id] = 0 if session[:cart][:items][product.id].nil?  
13 - session[:cart][:items][product.id] += 1  
14 - render :text => {  
15 - :ok => true,  
16 - :error => {:code => 0},  
17 - :products => [{  
18 - :id => product.id,  
19 - :name => product.name,  
20 - :price => get_price(product, profile.environment),  
21 - :description => product.description,  
22 - :picture => product.default_image(:minor),  
23 - :quantity => session[:cart][:items][product.id]  
24 - }]  
25 - }.to_json  
26 - end  
27 - end  
28 -  
29 - def remove  
30 - id = params[:id].to_i  
31 - if validate_cart_presence && validate_cart_has_product(id)  
32 - session[:cart][:items].delete(id)  
33 - session[:cart] = nil if session[:cart][:items].empty?  
34 - render :text => {  
35 - :ok => true,  
36 - :error => {:code => 0},  
37 - :product_id => id  
38 - }.to_json  
39 - end  
40 - end  
41 -  
42 - def list  
43 - if validate_cart_presence  
44 - products = session[:cart][:items].collect do |id, quantity|  
45 - product = Product.find(id)  
46 - { :id => product.id,  
47 - :name => product.name,  
48 - :price => get_price(product, profile.environment),  
49 - :description => product.description,  
50 - :picture => product.default_image(:minor),  
51 - :quantity => quantity  
52 - }  
53 - end  
54 - render :text => {  
55 - :ok => true,  
56 - :error => {:code => 0},  
57 - :enterprise => Enterprise.find(session[:cart][:enterprise_id]).identifier,  
58 - :products => products  
59 - }.to_json  
60 - end  
61 - end  
62 -  
63 - def update_quantity  
64 - quantity = params[:quantity].to_i  
65 - id = params[:id].to_i  
66 - if validate_cart_presence && validate_cart_has_product(id) && validate_item_quantity(quantity)  
67 - product = Product.find(id)  
68 - session[:cart][:items][product.id] = quantity  
69 - render :text => {  
70 - :ok => true,  
71 - :error => {:code => 0},  
72 - :product_id => id,  
73 - :quantity => quantity  
74 - }.to_json  
75 - end  
76 - end  
77 -  
78 - def clean  
79 - session[:cart] = nil  
80 - render :text => {  
81 - :ok => true,  
82 - :error => {:code => 0}  
83 - }.to_json  
84 - end  
85 -  
86 - def buy  
87 - @environment = profile.environment  
88 - render :layout => false  
89 - end  
90 -  
91 - def send_request  
92 - register_order(params[:customer], session[:cart][:items])  
93 - begin  
94 - ShoppingCartPlugin::Mailer.deliver_customer_notification(params[:customer], profile, session[:cart][:items])  
95 - ShoppingCartPlugin::Mailer.deliver_supplier_notification(params[:customer], profile, session[:cart][:items])  
96 - render :text => {  
97 - :ok => true,  
98 - :message => _('Request sent successfully. Check your email.'),  
99 - :error => {:code => 0}  
100 - }.to_json  
101 - rescue Exception => exception  
102 - render :text => {  
103 - :ok => false,  
104 - :error => {  
105 - :code => 6,  
106 - :message => exception.message  
107 - }  
108 - }.to_json  
109 - end  
110 - end  
111 -  
112 - def visibility  
113 - render :text => session[:cart].has_key?(:visibility) ? session[:cart][:visibility].to_json : true.to_json  
114 - end  
115 -  
116 - def show  
117 - begin  
118 - session[:cart][:visibility] = true  
119 - render :text => {  
120 - :ok => true,  
121 - :message => _('Basket displayed.'),  
122 - :error => {:code => 0}  
123 - }.to_json  
124 - rescue Exception => exception  
125 - render :text => {  
126 - :ok => false,  
127 - :error => {  
128 - :code => 7,  
129 - :message => exception.message  
130 - }  
131 - }.to_json  
132 - end  
133 - end  
134 -  
135 - def hide  
136 - begin  
137 - session[:cart][:visibility] = false  
138 - render :text => {  
139 - :ok => true,  
140 - :message => _('Basket hidden.'),  
141 - :error => {:code => 0}  
142 - }.to_json  
143 - rescue Exception => exception  
144 - render :text => {  
145 - :ok => false,  
146 - :error => {  
147 - :code => 8,  
148 - :message => exception.message  
149 - }  
150 - }.to_json  
151 - end  
152 - end  
153 -  
154 - private  
155 -  
156 - def validate_same_enterprise  
157 - if profile.id != session[:cart][:enterprise_id]  
158 - render :text => {  
159 - :ok => false,  
160 - :error => {  
161 - :code => 1,  
162 - :message => _("Can't join items from different enterprises.")  
163 - }  
164 - }.to_json  
165 - return false  
166 - end  
167 - true  
168 - end  
169 -  
170 - def validate_cart_presence  
171 - if session[:cart].nil?  
172 - render :text => {  
173 - :ok => false,  
174 - :error => {  
175 - :code => 2,  
176 - :message => _("There is no basket.")  
177 - }  
178 - }.to_json  
179 - return false  
180 - end  
181 - true  
182 - end  
183 -  
184 - def validate_enterprise_has_product(id)  
185 - begin  
186 - product = profile.products.find(id)  
187 - rescue  
188 - render :text => {  
189 - :ok => false,  
190 - :error => {  
191 - :code => 3,  
192 - :message => _("This enterprise doesn't have this product.")  
193 - }  
194 - }.to_json  
195 - return nil  
196 - end  
197 - product  
198 - end  
199 -  
200 - def validate_cart_has_product(id)  
201 - if !session[:cart][:items].has_key?(id)  
202 - render :text => {  
203 - :ok => false,  
204 - :error => {  
205 - :code => 4,  
206 - :message => _("The basket doesn't have this product.")  
207 - }  
208 - }.to_json  
209 - return false  
210 - end  
211 - true  
212 - end  
213 -  
214 - def validate_item_quantity(quantity)  
215 - if quantity.to_i < 1  
216 - render :text => {  
217 - :ok => false,  
218 - :error => {  
219 - :code => 5,  
220 - :message => _("Invalid quantity.")  
221 - }  
222 - }.to_json  
223 - return false  
224 - end  
225 - true  
226 - end  
227 -  
228 - def register_order(custumer, items)  
229 - new_items = {}  
230 - items.each do |id, quantity|  
231 - product = Product.find(id)  
232 - price = product.price || 0  
233 - new_items[id] = {:quantity => quantity, :price => price, :name => product.name}  
234 - end  
235 - ShoppingCartPlugin::PurchaseOrder.create!(  
236 - :seller => profile,  
237 - :customer => user,  
238 - :status => ShoppingCartPlugin::PurchaseOrder::Status::OPENED,  
239 - :products_list => new_items,  
240 - :customer_name => params[:customer][:name],  
241 - :customer_email => params[:customer][:email],  
242 - :customer_contact_phone => params[:customer][:contact_phone],  
243 - :customer_address => params[:customer][:address],  
244 - :customer_city => params[:customer][:city],  
245 - :customer_zip_code => params[:customer][:zip_code]  
246 - )  
247 - end  
248 -end  
plugins/shopping_cart/db/migrate/20121022190819_move_fields_included_on_profiles_table_to_settings.rb 0 → 100644
@@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
  1 +class MoveFieldsIncludedOnProfilesTableToSettings < ActiveRecord::Migration
  2 + def self.up
  3 + Profile.find_each do |profile|
  4 + settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin)
  5 + settings.enabled = profile.shopping_cart
  6 + settings.delivery = profile.shopping_cart_delivery
  7 + settings.delivery_price = profile.shopping_cart_delivery_price
  8 + settings.save!
  9 + end
  10 +
  11 + remove_column :profiles, :shopping_cart
  12 + remove_column :profiles, :shopping_cart_delivery
  13 + remove_column :profiles, :shopping_cart_delivery_price
  14 + end
  15 +
  16 + def self.down
  17 + say "This migration can not be reverted!"
  18 + end
  19 +end
plugins/shopping_cart/lib/shopping_cart_plugin.rb
@@ -3,20 +3,40 @@ require_dependency &#39;shopping_cart_plugin/ext/person&#39; @@ -3,20 +3,40 @@ require_dependency &#39;shopping_cart_plugin/ext/person&#39;
3 3
4 class ShoppingCartPlugin < Noosfero::Plugin 4 class ShoppingCartPlugin < Noosfero::Plugin
5 5
6 - def self.plugin_name 6 + class << self
  7 + def plugin_name
7 "Shopping Basket" 8 "Shopping Basket"
8 - end 9 + end
  10 +
  11 + def plugin_description
  12 + _("A shopping basket feature for enterprises")
  13 + end
  14 +
  15 + def enabled_default_setting
  16 + true
  17 + end
9 18
10 - def self.plugin_description  
11 - _("A shopping basket feature for enterprises") 19 + def delivery_default_setting
  20 + false
  21 + end
  22 +
  23 + def delivery_price_default_setting
  24 + 0
  25 + end
  26 +
  27 + def delivery_options_default_setting
  28 + {}
  29 + end
12 end 30 end
13 31
14 - def add_to_cart_button(item, enterprise = context.profile)  
15 - if enterprise.shopping_cart && item.available 32 + def add_to_cart_button(item)
  33 + enterprise = item.enterprise
  34 + settings = Noosfero::Plugin::Settings.new(enterprise, ShoppingCartPlugin)
  35 + if settings.enabled && item.available
16 lambda { 36 lambda {
17 link_to(_('Add to basket'), "add:#{item.name}", 37 link_to(_('Add to basket'), "add:#{item.name}",
18 :class => 'cart-add-item', 38 :class => 'cart-add-item',
19 - :onclick => "Cart.addItem('#{enterprise.identifier}', #{item.id}, this); return false" 39 + :onclick => "Cart.addItem(#{item.id}, this); return false"
20 ) 40 )
21 } 41 }
22 end 42 end
@@ -35,15 +55,16 @@ class ShoppingCartPlugin &lt; Noosfero::Plugin @@ -35,15 +55,16 @@ class ShoppingCartPlugin &lt; Noosfero::Plugin
35 end 55 end
36 56
37 def body_beginning 57 def body_beginning
38 - expanded_template('cart.html.erb',{:cart => context.session[:cart]}) 58 + expanded_template('cart.html.erb')
39 end 59 end
40 60
41 def control_panel_buttons 61 def control_panel_buttons
  62 + settings = Noosfero::Plugin::Settings.new(context.profile, ShoppingCartPlugin)
42 buttons = [] 63 buttons = []
43 if context.profile.enterprise? 64 if context.profile.enterprise?
44 buttons << { :title => _('Shopping basket'), :icon => 'shopping-cart-icon', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'edit'} } 65 buttons << { :title => _('Shopping basket'), :icon => 'shopping-cart-icon', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'edit'} }
45 end 66 end
46 - if context.profile.enterprise? && context.profile.shopping_cart 67 + if context.profile.enterprise? && settings.enabled
47 buttons << { :title => _('Purchase reports'), :icon => 'shopping-cart-purchase-report', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'reports'} } 68 buttons << { :title => _('Purchase reports'), :icon => 'shopping-cart-purchase-report', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'reports'} }
48 end 69 end
49 70
plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb
1 module ShoppingCartPlugin::CartHelper 1 module ShoppingCartPlugin::CartHelper
2 2
3 include ActionView::Helpers::NumberHelper 3 include ActionView::Helpers::NumberHelper
  4 + include ActionView::Helpers::TagHelper
4 5
5 def sell_price(product) 6 def sell_price(product)
6 return 0 if product.price.nil? 7 return 0 if product.price.nil?
7 product.discount ? product.price_with_discount : product.price 8 product.discount ? product.price_with_discount : product.price
8 end 9 end
9 10
10 - def get_price(product, environment)  
11 - float_to_currency_cart(sell_price(product), environment) 11 + def get_price(product, environment, quantity=1)
  12 + float_to_currency_cart(price_with_quantity(product,quantity), environment)
12 end 13 end
13 14
14 - def get_total(items, environment)  
15 - float_to_currency_cart(items.map { |id, quantity| sell_price(Product.find(id)) * quantity}.sum, environment) 15 + def price_with_quantity(product, quantity=1)
  16 + quantity = 1 if !quantity.kind_of?(Numeric)
  17 + sell_price(product)*quantity
16 end 18 end
17 19
18 - def items_table(items, profile, by_mail = false) 20 + def get_total(items)
  21 + items.map { |id, quantity| price_with_quantity(Product.find(id),quantity)}.sum
  22 + end
  23 +
  24 + def get_total_on_currency(items, environment)
  25 + float_to_currency_cart(get_total(items), environment)
  26 + end
  27 +
  28 + def items_table(items, profile, delivery_option = nil, by_mail = false)
19 environment = profile.environment 29 environment = profile.environment
  30 + settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin)
20 items = items.to_a 31 items = items.to_a
21 - if profile.shopping_cart_delivery  
22 - delivery = Product.create!(:name => _('Delivery'), :price => profile.shopping_cart_delivery_price, :product_category => ProductCategory.last)  
23 - items << [delivery.id, 1]  
24 - end  
25 32
26 quantity_opts = { :class => 'cart-table-quantity' } 33 quantity_opts = { :class => 'cart-table-quantity' }
27 quantity_opts.merge!({:align => 'center'}) if by_mail 34 quantity_opts.merge!({:align => 'center'}) if by_mail
28 price_opts = {:class => 'cart-table-price'} 35 price_opts = {:class => 'cart-table-price'}
29 price_opts.merge!({:align => 'right'}) if by_mail 36 price_opts.merge!({:align => 'right'}) if by_mail
  37 + items.sort! {|a, b| Product.find(a.first).name <=> Product.find(b.first).name}
  38 +
  39 + if settings.delivery
  40 + if settings.free_delivery_price && get_total(items) >= settings.free_delivery_price
  41 + delivery = Product.new(:name => _('Free delivery'), :price => 0)
  42 + else
  43 + delivery = Product.new(:name => delivery_option || _('Delivery'), :price => settings.delivery_options[delivery_option])
  44 + end
  45 + delivery.save(false)
  46 + items << [delivery.id, '']
  47 + end
30 48
31 table = '<table id="cart-items-table" cellpadding="2" cellspacing="0" 49 table = '<table id="cart-items-table" cellpadding="2" cellspacing="0"
32 border="'+(by_mail ? '1' : '0')+'" 50 border="'+(by_mail ? '1' : '0')+'"
33 style="'+(by_mail ? 'border-collapse:collapse' : '')+'">' + 51 style="'+(by_mail ? 'border-collapse:collapse' : '')+'">' +
34 content_tag('tr', 52 content_tag('tr',
35 - content_tag('th', _('Item name')) +  
36 - content_tag('th', by_mail ? '&nbsp;#&nbsp;' : '#') +  
37 - content_tag('th', _('Price')) 53 + content_tag('th', _('Item name')) +
  54 + content_tag('th', by_mail ? '&nbsp;#&nbsp;' : '#') +
  55 + content_tag('th', _('Price'))
38 ) + 56 ) +
39 items.map do |id, quantity| 57 items.map do |id, quantity|
40 product = Product.find(id) 58 product = Product.find(id)
  59 + name_opts = {}
  60 + is_delivery = quantity.kind_of?(String)
  61 + if is_delivery
  62 + price_opts.merge!({:id => 'delivery-price'})
  63 + name_opts.merge!({:id => 'delivery-name'})
  64 + end
41 content_tag('tr', 65 content_tag('tr',
42 - content_tag('td', product.name) +  
43 - content_tag('td', quantity, quantity_opts ) +  
44 - content_tag('td', get_price(product, environment), price_opts )  
45 - ) 66 + content_tag('td', product.name, name_opts) +
  67 + content_tag('td', quantity, quantity_opts ) +
  68 + content_tag('td', get_price(product, environment, quantity), price_opts)
  69 + )
46 end.join("\n") 70 end.join("\n")
47 71
48 - total = get_total(items, environment)  
49 - delivery.destroy if profile.shopping_cart_delivery 72 + total = get_total_on_currency(items, environment)
  73 + delivery.destroy if settings.delivery
50 74
51 table + 75 table +
52 content_tag('th', _('Total:'), :colspan => 2, :class => 'cart-table-total-label') + 76 content_tag('th', _('Total:'), :colspan => 2, :class => 'cart-table-total-label') +
@@ -55,6 +79,14 @@ module ShoppingCartPlugin::CartHelper @@ -55,6 +79,14 @@ module ShoppingCartPlugin::CartHelper
55 end 79 end
56 80
57 def float_to_currency_cart(value, environment) 81 def float_to_currency_cart(value, environment)
58 - number_to_currency(value, :unit => environment.currency_unit, :separator => environment.currency_separator, :delimiter => environment.currency_delimiter, :precision => 2, :format => "%u %n") 82 + number_to_currency(value, :unit => environment.currency_unit, :separator => environment.currency_separator, :delimiter => environment.currency_delimiter, :precision => 2, :format => "%u%n")
  83 + end
  84 +
  85 + def select_delivery_options(options, environment)
  86 + result = options.map do |option, price|
  87 + ["#{option} (#{float_to_currency_cart(price, environment)})", option]
  88 + end
  89 + result << ["#{_('Delivery')} (#{float_to_currency_cart(0, environment)})", 'delivery'] if result.empty?
  90 + result
59 end 91 end
60 end 92 end
plugins/shopping_cart/lib/shopping_cart_plugin/mailer.rb
1 class ShoppingCartPlugin::Mailer < Noosfero::Plugin::MailerBase 1 class ShoppingCartPlugin::Mailer < Noosfero::Plugin::MailerBase
2 2
3 - def customer_notification(customer, supplier, items) 3 + include ShoppingCartPlugin::CartHelper
  4 +
  5 + def customer_notification(customer, supplier, items, delivery_option)
4 domain = supplier.hostname || supplier.environment.default_hostname 6 domain = supplier.hostname || supplier.environment.default_hostname
5 recipients customer[:email] 7 recipients customer[:email]
6 from 'no-reply@' + domain 8 from 'no-reply@' + domain
@@ -10,10 +12,12 @@ class ShoppingCartPlugin::Mailer &lt; Noosfero::Plugin::MailerBase @@ -10,10 +12,12 @@ class ShoppingCartPlugin::Mailer &lt; Noosfero::Plugin::MailerBase
10 body :customer => customer, 12 body :customer => customer,
11 :supplier => supplier, 13 :supplier => supplier,
12 :items => items, 14 :items => items,
13 - :environment => supplier.environment 15 + :environment => supplier.environment,
  16 + :helper => self,
  17 + :delivery_option => delivery_option
14 end 18 end
15 19
16 - def supplier_notification(customer, supplier, items) 20 + def supplier_notification(customer, supplier, items, delivery_option)
17 domain = supplier.environment.default_hostname 21 domain = supplier.environment.default_hostname
18 recipients supplier.contact_email 22 recipients supplier.contact_email
19 from 'no-reply@' + domain 23 from 'no-reply@' + domain
@@ -23,6 +27,8 @@ class ShoppingCartPlugin::Mailer &lt; Noosfero::Plugin::MailerBase @@ -23,6 +27,8 @@ class ShoppingCartPlugin::Mailer &lt; Noosfero::Plugin::MailerBase
23 body :customer => customer, 27 body :customer => customer,
24 :supplier => supplier, 28 :supplier => supplier,
25 :items => items, 29 :items => items,
26 - :environment => supplier.environment 30 + :environment => supplier.environment,
  31 + :helper => self,
  32 + :delivery_option => delivery_option
27 end 33 end
28 end 34 end
plugins/shopping_cart/lib/shopping_cart_plugin/purchase_order.rb
@@ -12,8 +12,12 @@ class ShoppingCartPlugin::PurchaseOrder &lt; Noosfero::Plugin::ActiveRecord @@ -12,8 +12,12 @@ class ShoppingCartPlugin::PurchaseOrder &lt; Noosfero::Plugin::ActiveRecord
12 settings_items :customer_email, :type => String 12 settings_items :customer_email, :type => String
13 settings_items :customer_contact_phone, :type => String 13 settings_items :customer_contact_phone, :type => String
14 settings_items :customer_address, :type => String 14 settings_items :customer_address, :type => String
  15 + settings_items :customer_district, :type => String
15 settings_items :customer_city, :type => String 16 settings_items :customer_city, :type => String
16 settings_items :customer_zip_code, :type => String 17 settings_items :customer_zip_code, :type => String
  18 + settings_items :customer_delivery_option, :type => String
  19 + settings_items :customer_payment, :type => String
  20 + settings_items :customer_change, :type => String
17 21
18 before_create do |order| 22 before_create do |order|
19 order.created_at = Time.now.utc 23 order.created_at = Time.now.utc
plugins/shopping_cart/public/buy.js 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +jQuery(document).ready(function(){
  2 + jQuery("#cart-request-form").validate({
  3 + submitHandler: function(form) {
  4 + jQuery(form).find('input.submit').attr('disabled', true);
  5 + jQuery('#cboxLoadingOverlay').show().addClass('loading');
  6 + jQuery('#cboxLoadingGraphic').show().addClass('loading');
  7 + }
  8 + });
  9 +});
  10 +
  11 +jQuery('#delivery_option').change(function(){
  12 + jQuery('#cboxLoadingGraphic').show();
  13 + me = this;
  14 + enterprise = jQuery(me).attr('data-profile-identifier');
  15 + option = jQuery(me).val();
  16 + jQuery.ajax({
  17 + url: '/plugin/shopping_cart/update_delivery_option',
  18 + dataType: "json",
  19 + data: 'delivery_option='+option,
  20 + success: function(data, st, ajax) {
  21 + jQuery('#delivery-price').text(data.delivery_price);
  22 + jQuery('.cart-table-total-value').text(data.total_price);
  23 + jQuery('#delivery-name').text(option);
  24 + jQuery('#cboxLoadingGraphic').hide();
  25 + },
  26 + error: function(ajax, st, errorThrown) {
  27 + alert('Update delivery option - HTTP '+st+': '+errorThrown);
  28 + },
  29 + });
  30 +});
  31 +
  32 +jQuery('#customer_payment').change(function(){
  33 + jQuery(this).closest('.formfieldline').next().slideToggle('fast');
  34 +});
plugins/shopping_cart/public/cart.js
@@ -11,10 +11,9 @@ function Cart(config) { @@ -11,10 +11,9 @@ function Cart(config) {
11 $(".cart-buy", this.cartElem).button({ icons: { primary: 'ui-icon-cart'} }); 11 $(".cart-buy", this.cartElem).button({ icons: { primary: 'ui-icon-cart'} });
12 if (!this.empty) { 12 if (!this.empty) {
13 $(this.cartElem).show(); 13 $(this.cartElem).show();
14 - this.enterprise = config.enterprise;  
15 me = this; 14 me = this;
16 $.ajax({ 15 $.ajax({
17 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/visibility', 16 + url: '/plugin/shopping_cart/visibility',
18 dataType: 'json', 17 dataType: 'json',
19 success: function(data, status, ajax){ 18 success: function(data, status, ajax){
20 me.visible = /^true$/i.test(data); 19 me.visible = /^true$/i.test(data);
@@ -25,7 +24,7 @@ function Cart(config) { @@ -25,7 +24,7 @@ function Cart(config) {
25 alert('Visibility - HTTP '+status+': '+errorThrown); 24 alert('Visibility - HTTP '+status+': '+errorThrown);
26 } 25 }
27 }); 26 });
28 - $(".cart-buy", this.cartElem).colorbox({href: '/profile/' + this.enterprise + '/plugins/shopping_cart/buy'}); 27 + $(".cart-buy", this.cartElem).colorbox({ href: '/plugin/shopping_cart/buy' });
29 } 28 }
30 } 29 }
31 30
@@ -34,7 +33,7 @@ function Cart(config) { @@ -34,7 +33,7 @@ function Cart(config) {
34 Cart.prototype.listProducts = function() { 33 Cart.prototype.listProducts = function() {
35 var me = this; 34 var me = this;
36 $.ajax({ 35 $.ajax({
37 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/list', 36 + url: '/plugin/shopping_cart/list',
38 dataType: 'json', 37 dataType: 'json',
39 success: function(data, ststus, ajax){ 38 success: function(data, ststus, ajax){
40 if ( !data.ok ) alert(data.error.message); 39 if ( !data.ok ) alert(data.error.message);
@@ -61,7 +60,7 @@ function Cart(config) { @@ -61,7 +60,7 @@ function Cart(config) {
61 '<span class="item-name">'+ item.name +'</span>' + 60 '<span class="item-name">'+ item.name +'</span>' +
62 '<div class="item-price">' + 61 '<div class="item-price">' +
63 '<input size="1" value="'+item.quantity+'" />'+ (item.price ? '&times; '+ item.price : '') +'</div>' + 62 '<input size="1" value="'+item.quantity+'" />'+ (item.price ? '&times; '+ item.price : '') +'</div>' +
64 - ' <a href="remove:'+item.name+'" onclick="Cart.removeItem(\''+this.enterprise+'\', '+item.id+'); return false"' + 63 + ' <a href="remove:'+item.name+'" onclick="Cart.removeItem('+item.id+'); return false"' +
65 ' class="button icon-remove"><span>remove</span></a>' 64 ' class="button icon-remove"><span>remove</span></a>'
66 ).appendTo(li); 65 ).appendTo(li);
67 var input = $("input", li)[0]; 66 var input = $("input", li)[0];
@@ -100,7 +99,7 @@ function Cart(config) { @@ -100,7 +99,7 @@ function Cart(config) {
100 var me = this; 99 var me = this;
101 if( quantity == NaN ) return input.value = input.lastValue; 100 if( quantity == NaN ) return input.value = input.lastValue;
102 $.ajax({ 101 $.ajax({
103 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/update_quantity/'+ itemId +'?quantity='+ quantity, 102 + url: '/plugin/shopping_cart/update_quantity/'+ itemId +'?quantity='+ quantity,
104 dataType: 'json', 103 dataType: 'json',
105 success: function(data, status, ajax){ 104 success: function(data, status, ajax){
106 if ( !data.ok ) { 105 if ( !data.ok ) {
@@ -132,8 +131,7 @@ function Cart(config) { @@ -132,8 +131,7 @@ function Cart(config) {
132 this.updateTotal(); 131 this.updateTotal();
133 } 132 }
134 133
135 - Cart.addItem = function(enterprise, itemId, link) {  
136 - // on the future, the instance may be found by the enterprise identifier. 134 + Cart.addItem = function(itemId, link) {
137 link.intervalId = setInterval(function() { 135 link.intervalId = setInterval(function() {
138 steps = ['w', 'n', 'e', 's']; 136 steps = ['w', 'n', 'e', 's'];
139 if( !link.step || link.step==3 ) link.step = 0; 137 if( !link.step || link.step==3 ) link.step = 0;
@@ -144,18 +142,13 @@ function Cart(config) { @@ -144,18 +142,13 @@ function Cart(config) {
144 clearInterval(link.intervalId); 142 clearInterval(link.intervalId);
145 $(link).button({ icons: { primary: 'ui-icon-cart'}, disable: false }); 143 $(link).button({ icons: { primary: 'ui-icon-cart'}, disable: false });
146 }; 144 };
147 - this.instance.addItem(enterprise, itemId, stopBtLoading); 145 + this.instance.addItem(itemId, stopBtLoading);
148 } 146 }
149 147
150 - Cart.prototype.addItem = function(enterprise, itemId, callback) {  
151 - if(!this.enterprise) {  
152 - this.enterprise = enterprise;  
153 - $(".cart-buy", this.cartElem).colorbox({href: '/profile/' + this.enterprise + '/plugins/shopping_cart/buy'});  
154 -// $(this.cartElem).show();  
155 - } 148 + Cart.prototype.addItem = function(itemId, callback) {
156 var me = this; 149 var me = this;
157 $.ajax({ 150 $.ajax({
158 - url: '/profile/'+ enterprise +'/plugins/shopping_cart/add/'+ itemId, 151 + url: '/plugin/shopping_cart/add/'+ itemId,
159 dataType: 'json', 152 dataType: 'json',
160 success: function(data, status, ajax){ 153 success: function(data, status, ajax){
161 if ( !data.ok ) alert(data.error.message); 154 if ( !data.ok ) alert(data.error.message);
@@ -169,16 +162,16 @@ function Cart(config) { @@ -169,16 +162,16 @@ function Cart(config) {
169 }); 162 });
170 } 163 }
171 164
172 - Cart.removeItem = function(enterprise, itemId) { 165 + Cart.removeItem = function(itemId) {
173 var message = this.instance.cartElem.getAttribute('data-l10nRemoveItem'); 166 var message = this.instance.cartElem.getAttribute('data-l10nRemoveItem');
174 - if( confirm(message) ) this.instance.removeItem(enterprise, itemId); 167 + if( confirm(message) ) this.instance.removeItem(itemId);
175 } 168 }
176 169
177 - Cart.prototype.removeItem = function(enterprise, itemId) { 170 + Cart.prototype.removeItem = function(itemId) {
178 if ($("li", this.itemsBox).size() < 2) return this.clean(); 171 if ($("li", this.itemsBox).size() < 2) return this.clean();
179 var me = this; 172 var me = this;
180 $.ajax({ 173 $.ajax({
181 - url: '/profile/'+ enterprise +'/plugins/shopping_cart/remove/'+ itemId, 174 + url: '/plugin/shopping_cart/remove/'+ itemId,
182 dataType: 'json', 175 dataType: 'json',
183 success: function(data, status, ajax){ 176 success: function(data, status, ajax){
184 if ( !data.ok ) alert(data.error.message); 177 if ( !data.ok ) alert(data.error.message);
@@ -200,7 +193,7 @@ function Cart(config) { @@ -200,7 +193,7 @@ function Cart(config) {
200 193
201 Cart.prototype.show = function() { 194 Cart.prototype.show = function() {
202 $.ajax({ 195 $.ajax({
203 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/show', 196 + url: '/plugin/shopping_cart/show',
204 dataType: 'json', 197 dataType: 'json',
205 cache: false, 198 cache: false,
206 error: function(ajax, status, errorThrown) { 199 error: function(ajax, status, errorThrown) {
@@ -215,7 +208,7 @@ function Cart(config) { @@ -215,7 +208,7 @@ function Cart(config) {
215 } 208 }
216 Cart.prototype.hide = function() { 209 Cart.prototype.hide = function() {
217 $.ajax({ 210 $.ajax({
218 - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/hide', 211 + url: '/plugin/shopping_cart/hide',
219 dataType: 'json', 212 dataType: 'json',
220 cache: false, 213 cache: false,
221 error: function(ajax, status, errorThrown) { 214 error: function(ajax, status, errorThrown) {
@@ -252,7 +245,7 @@ function Cart(config) { @@ -252,7 +245,7 @@ function Cart(config) {
252 Cart.prototype.clean = function() { 245 Cart.prototype.clean = function() {
253 var me = this; 246 var me = this;
254 $.ajax({ 247 $.ajax({
255 - url: '/profile/'+ me.enterprise +'/plugins/shopping_cart/clean', 248 + url: '/plugin/shopping_cart/clean',
256 dataType: 'json', 249 dataType: 'json',
257 success: function(data, status, ajax){ 250 success: function(data, status, ajax){
258 if ( !data.ok ) alert(data.error.message); 251 if ( !data.ok ) alert(data.error.message);
@@ -261,7 +254,6 @@ function Cart(config) { @@ -261,7 +254,6 @@ function Cart(config) {
261 $(me.cartElem).slideUp(500, function() { 254 $(me.cartElem).slideUp(500, function() {
262 $(me.itemsBox).empty(); 255 $(me.itemsBox).empty();
263 me.hide(); 256 me.hide();
264 - me.enterprise = null;  
265 me.updateTotal(); 257 me.updateTotal();
266 me.empty = true; 258 me.empty = true;
267 }); 259 });
@@ -284,7 +276,7 @@ function Cart(config) { @@ -284,7 +276,7 @@ function Cart(config) {
284 var me = this; 276 var me = this;
285 $.ajax({ 277 $.ajax({
286 type: 'POST', 278 type: 'POST',
287 - url: '/profile/'+ me.enterprise +'/plugins/shopping_cart/send_request', 279 + url: '/plugin/shopping_cart/send_request',
288 data: params, 280 data: params,
289 dataType: 'json', 281 dataType: 'json',
290 success: function(data, status, ajax){ 282 success: function(data, status, ajax){
@@ -309,7 +301,19 @@ function Cart(config) { @@ -309,7 +301,19 @@ function Cart(config) {
309 } 301 }
310 302
311 $(function(){ 303 $(function(){
312 - $('.cart-add-item').button({ icons: { primary: 'ui-icon-cart'} }) 304 +
  305 + $.ajax({
  306 + url: "/plugin/shopping_cart/get",
  307 + dataType: 'json',
  308 + success: function(data) {
  309 + new Cart(data);
  310 + $('.cart-add-item').button({ icons: { primary: 'ui-icon-cart'} })
  311 + },
  312 + cache: false,
  313 + error: function(ajax, status, errorThrown) {
  314 + alert('Error getting shopping cart - HTTP '+status+': '+errorThrown);
  315 + }
  316 + });
313 }); 317 });
314 318
315 })(jQuery); 319 })(jQuery);
plugins/shopping_cart/public/edit.js 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +jQuery('#settings_delivery').click(function(){
  2 + jQuery('#delivery_settings').toggle('fast');
  3 +});
  4 +
  5 +jQuery('#add-new-option').click(function(){
  6 + new_option = jQuery('#empty-option').clone();
  7 + new_option.removeAttr('id');
  8 + jQuery('#add-new-option-row').before(new_option);
  9 + new_option.show();
  10 + return false;
  11 +});
  12 +
  13 +jQuery('.remove-option').live('click', function(){
  14 + jQuery(this).closest('tr').remove();
  15 + return false;
  16 +});
plugins/shopping_cart/public/style.css
1 -@import url(colorbox/colorbox.css);  
2 -  
3 .cart-add-item .ui-icon-cart { 1 .cart-add-item .ui-icon-cart {
4 background: url("/plugins/shopping_cart/images/button-icon.png") no-repeat scroll left center transparent; 2 background: url("/plugins/shopping_cart/images/button-icon.png") no-repeat scroll left center transparent;
5 width: 22px; 3 width: 22px;
plugins/shopping_cart/test/functional/shopping_cart_plugin_controller_test.rb 0 → 100644
@@ -0,0 +1,225 @@ @@ -0,0 +1,225 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../controllers/shopping_cart_plugin_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class ShoppingCartPluginController; def rescue_action(e) raise e end; end
  6 +
  7 +class ShoppingCartPluginControllerTest < ActionController::TestCase
  8 +
  9 + def setup
  10 + @controller = ShoppingCartPluginController.new
  11 + @request = ActionController::TestRequest.new
  12 + @response = ActionController::TestResponse.new
  13 + @enterprise = fast_create(Enterprise)
  14 + @product = fast_create(Product, :enterprise_id => @enterprise.id)
  15 + end
  16 + attr_reader :enterprise
  17 + attr_reader :product
  18 +
  19 + should 'force cookie expiration with explicit path for an empty cart' do
  20 + get :get
  21 + assert @response.headers['Set-Cookie'].any? { |c| c =~ /_noosfero_plugin_shopping_cart=; path=\/plugin\/shopping_cart; expires=.*-1970/}
  22 + end
  23 +
  24 + should 'add a new product to cart' do
  25 + get :add, :id => product.id
  26 +
  27 + assert product_in_cart?(product)
  28 + assert_equal 1, product_quantity(product)
  29 + end
  30 +
  31 + should 'grow quantity through add' do
  32 + get :add, :id => product.id
  33 + assert_equal 1, product_quantity(product)
  34 +
  35 + get :add, :id => product.id
  36 + assert_equal 2, product_quantity(product)
  37 + end
  38 +
  39 + should 'not add product to cart if it does not exists' do
  40 + assert_nothing_raised { get :add, :id => 9999 }
  41 +
  42 + assert !product_in_cart?(product)
  43 + assert !response_ok?
  44 + assert 3, reponse_error_code
  45 + end
  46 +
  47 + should 'remove cart if the product being removed is the last one' do
  48 + get :add, :id => product.id
  49 + assert cart?
  50 +
  51 + get :remove, :id => product.id
  52 + assert !cart?
  53 + end
  54 +
  55 + should 'not try to remove a product if there is no cart' do
  56 + instantiate_cart
  57 + assert !cart?
  58 +
  59 + assert_nothing_raised { get :remove, :id => 9999 }
  60 + assert !response_ok?
  61 + assert_equal 2, reponse_error_code
  62 + end
  63 +
  64 + should 'just remove product if there are other products on cart' do
  65 + another_product = fast_create(Product, :enterprise_id => enterprise.id)
  66 + get :add, :id => product.id
  67 + get :add, :id => another_product.id
  68 +
  69 + get :remove, :id => product.id
  70 + assert cart?
  71 + assert !product_in_cart?(product)
  72 + end
  73 +
  74 + should 'not try to remove a product that is not in the cart' do
  75 + get :add, :id => product.id
  76 + assert cart?
  77 + assert_nothing_raised { get :remove, :id => 9999 }
  78 +
  79 + assert !response_ok?
  80 + assert_equal 4, reponse_error_code
  81 + end
  82 +
  83 + should 'not try to list the cart if there is no cart' do
  84 + instantiate_cart
  85 + assert !cart?
  86 +
  87 + assert_nothing_raised { get :list }
  88 + assert !response_ok?
  89 + assert_equal 2, reponse_error_code
  90 + end
  91 +
  92 + should 'list products without errors' do
  93 + get :add, :id => product.id
  94 +
  95 + assert_nothing_raised { get :list }
  96 + assert response_ok?
  97 + end
  98 +
  99 + should 'update the quantity of a product' do
  100 + get :add, :id => product.id
  101 + assert 1, product_quantity(product)
  102 +
  103 + get :update_quantity, :id => product.id, :quantity => 3
  104 + assert 3, product_quantity(product)
  105 + end
  106 +
  107 + should 'not try to update quantity the quantity of a product if there is no cart' do
  108 + instantiate_cart
  109 + assert !cart?
  110 +
  111 + assert_nothing_raised { get :update_quantity, :id => 9999, :quantity => 3 }
  112 + assert !response_ok?
  113 + assert_equal 2, reponse_error_code
  114 + end
  115 +
  116 + should 'not try to update the quantity of a product that is not in the cart' do
  117 + get :add, :id => product.id
  118 + assert cart?
  119 + assert_nothing_raised { get :update_quantity, :id => 9999, :quantity => 3 }
  120 +
  121 + assert !response_ok?
  122 + assert_equal 4, reponse_error_code
  123 + end
  124 +
  125 + should 'not update the quantity of a product with a invalid value' do
  126 + get :add, :id => product.id
  127 +
  128 + assert_nothing_raised { get :update_quantity, :id => product.id, :quantity => -1}
  129 + assert !response_ok?
  130 + assert_equal 5, reponse_error_code
  131 +
  132 + assert_nothing_raised { get :update_quantity, :id => product.id, :quantity => 'asdf'}
  133 + assert !response_ok?
  134 + assert_equal 5, reponse_error_code
  135 + end
  136 +
  137 + should 'clean the cart' do
  138 + another_product = fast_create(Product, :enterprise_id => enterprise.id)
  139 + get :add, :id => product.id
  140 + get :add, :id => another_product.id
  141 +
  142 + assert_nothing_raised { get :clean }
  143 + assert !cart?
  144 + end
  145 +
  146 + should 'not crash if there is no cart' do
  147 + instantiate_cart
  148 + assert !cart?
  149 + assert_nothing_raised { get :clean }
  150 + end
  151 +
  152 + should 'register order on send request' do
  153 + product1 = fast_create(Product, :enterprise_id => enterprise.id, :price => 1.99)
  154 + product2 = fast_create(Product, :enterprise_id => enterprise.id, :price => 2.23)
  155 + @controller.stubs(:cart).returns({ :enterprise_id => enterprise.id, :items => {product1.id => 1, product2.id => 2}})
  156 + assert_difference ShoppingCartPlugin::PurchaseOrder, :count, 1 do
  157 + post :send_request,
  158 + :customer => {:name => "Manuel", :email => "manuel@ceu.com"}
  159 + end
  160 +
  161 + order = ShoppingCartPlugin::PurchaseOrder.last
  162 +
  163 + assert_equal 1.99, order.products_list[product1.id][:price]
  164 + assert_equal 1, order.products_list[product1.id][:quantity]
  165 + assert_equal 2.23, order.products_list[product2.id][:price]
  166 + assert_equal 2, order.products_list[product2.id][:quantity]
  167 + assert_equal ShoppingCartPlugin::PurchaseOrder::Status::OPENED, order.status
  168 + end
  169 +
  170 + should 'register order on send request and not crash if product is not defined' do
  171 + product1 = fast_create(Product, :enterprise_id => enterprise.id)
  172 + @controller.stubs(:cart).returns({ :enterprise_id => enterprise.id, :items => {product1.id => 1}})
  173 + assert_difference ShoppingCartPlugin::PurchaseOrder, :count, 1 do
  174 + post :send_request,
  175 + :customer => {:name => "Manuel", :email => "manuel@ceu.com"}
  176 + end
  177 +
  178 + order = ShoppingCartPlugin::PurchaseOrder.last
  179 +
  180 + assert_equal 0, order.products_list[product1.id][:price]
  181 + end
  182 +
  183 + should 'clean the cart after placing the order' do
  184 + product1 = fast_create(Product, :enterprise_id => enterprise.id)
  185 + post :add, :id => product1.id
  186 + post :send_request, :customer => { :name => "Manuel", :email => "manuel@ceu.com" }
  187 + assert !cart?, "cart expected to be empty!"
  188 + end
  189 +
  190 + private
  191 +
  192 + def json_response
  193 + ActiveSupport::JSON.decode @response.body
  194 + end
  195 +
  196 + def cart?
  197 + !@controller.send(:cart).nil?
  198 + end
  199 +
  200 + def product_in_cart?(product)
  201 + @controller.send(:cart) &&
  202 + @controller.send(:cart)[:items] &&
  203 + @controller.send(:cart)[:items].has_key?(product.id)
  204 + end
  205 +
  206 + def product_quantity(product)
  207 + @controller.send(:cart)[:items][product.id]
  208 + end
  209 +
  210 + def response_ok?
  211 + json_response['ok']
  212 + end
  213 +
  214 + def reponse_error_code
  215 + json_response['error']['code']
  216 + end
  217 +
  218 + # temporary hack...if I don't do this the session stays as an Array instead
  219 + # of a TestSession
  220 + def instantiate_cart
  221 + get :add, :id => product.id
  222 + get :remove, :id => product.id
  223 + end
  224 +
  225 +end
plugins/shopping_cart/test/functional/shopping_cart_plugin_myprofile_controller_test.rb
@@ -13,46 +13,42 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase @@ -13,46 +13,42 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase
13 attr_reader :enterprise 13 attr_reader :enterprise
14 14
15 should 'be able to enable shopping cart' do 15 should 'be able to enable shopping cart' do
16 - enterprise.shopping_cart = false  
17 - enterprise.save  
18 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart => '1'}  
19 - enterprise.reload 16 + settings.enabled = false
  17 + settings.save!
  18 + post :edit, :profile => enterprise.identifier, :settings => {:enabled => '1'}
20 19
21 - assert enterprise.shopping_cart 20 + assert settings.enabled
22 end 21 end
23 22
24 should 'be able to disable shopping cart' do 23 should 'be able to disable shopping cart' do
25 - enterprise.shopping_cart = true  
26 - enterprise.save  
27 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart => '0'}  
28 - enterprise.reload 24 + settings.enabled = true
  25 + settings.save!
  26 + post :edit, :profile => enterprise.identifier, :settings => {:enabled => '0'}
29 27
30 - assert !enterprise.shopping_cart 28 + assert !settings.enabled
31 end 29 end
32 30
33 should 'be able to enable shopping cart delivery' do 31 should 'be able to enable shopping cart delivery' do
34 - enterprise.shopping_cart_delivery = false  
35 - enterprise.save  
36 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart_delivery => '1'}  
37 - enterprise.reload 32 + settings.delivery = false
  33 + settings.save!
  34 + post :edit, :profile => enterprise.identifier, :settings => {:delivery => '1'}
38 35
39 - assert enterprise.shopping_cart_delivery 36 + assert settings.delivery
40 end 37 end
41 38
42 should 'be able to disable shopping cart delivery' do 39 should 'be able to disable shopping cart delivery' do
43 - enterprise.shopping_cart_delivery = true  
44 - enterprise.save  
45 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart_delivery => '0'}  
46 - enterprise.reload 40 + settings.delivery = true
  41 + settings.save!
  42 + post :edit, :profile => enterprise.identifier, :settings => {:delivery => '0'}
47 43
48 - assert !enterprise.shopping_cart_delivery 44 + assert !settings.delivery
49 end 45 end
50 46
51 should 'be able to choose the delivery price' do 47 should 'be able to choose the delivery price' do
52 price = 4.35 48 price = 4.35
53 - post :edit, :profile => enterprise.identifier, :profile_attr => {:shopping_cart_delivery_price => price}  
54 - enterprise.reload  
55 - assert enterprise.shopping_cart_delivery_price == price 49 + post :edit, :profile => enterprise.identifier, :settings => {:delivery_price => price}
  50 +
  51 + assert settings.delivery_price == price
56 end 52 end
57 53
58 should 'filter the reports correctly' do 54 should 'filter the reports correctly' do
@@ -112,4 +108,11 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase @@ -112,4 +108,11 @@ class ShoppingCartPluginMyprofileControllerTest &lt; ActionController::TestCase
112 po.reload 108 po.reload
113 assert_equal ShoppingCartPlugin::PurchaseOrder::Status::CONFIRMED, po.status 109 assert_equal ShoppingCartPlugin::PurchaseOrder::Status::CONFIRMED, po.status
114 end 110 end
  111 +
  112 + private
  113 +
  114 + def settings
  115 + @enterprise.reload
  116 + Noosfero::Plugin::Settings.new(@enterprise, ShoppingCartPlugin)
  117 + end
115 end 118 end
plugins/shopping_cart/test/functional/shopping_cart_plugin_profile_controller_test.rb
@@ -1,213 +0,0 @@ @@ -1,213 +0,0 @@
1 -require File.dirname(__FILE__) + '/../../../../test/test_helper'  
2 -require File.dirname(__FILE__) + '/../../controllers/shopping_cart_plugin_profile_controller'  
3 -  
4 -# Re-raise errors caught by the controller.  
5 -class ShoppingCartPluginProfileController; def rescue_action(e) raise e end; end  
6 -  
7 -class ShoppingCartPluginProfileControllerTest < ActionController::TestCase  
8 -  
9 - def setup  
10 - @controller = ShoppingCartPluginProfileController.new  
11 - @request = ActionController::TestRequest.new  
12 - @response = ActionController::TestResponse.new  
13 - @enterprise = fast_create(Enterprise)  
14 - @product = fast_create(Product, :enterprise_id => @enterprise.id)  
15 - end  
16 - attr_reader :enterprise  
17 - attr_reader :product  
18 -  
19 - should 'add a new product to cart' do  
20 - get :add, :profile => enterprise.identifier, :id => product.id  
21 -  
22 - assert product_in_cart?(product)  
23 - assert_equal 1, product_quantity(product)  
24 - end  
25 -  
26 - should 'grow quantity through add' do  
27 - get :add, :profile => enterprise.identifier, :id => product.id  
28 - assert_equal 1, product_quantity(product)  
29 -  
30 - get :add, :profile => enterprise.identifier, :id => product.id  
31 - assert_equal 2, product_quantity(product)  
32 - end  
33 -  
34 - should 'not add product to cart if it does not exists' do  
35 - assert_nothing_raised { get :add, :profile => enterprise.identifier, :id => 9999 }  
36 -  
37 - assert !product_in_cart?(product)  
38 - assert !response_ok?  
39 - assert 3, reponse_error_code  
40 - end  
41 -  
42 - should 'remove cart if the product being removed is the last one' do  
43 - get :add, :profile => enterprise.identifier, :id => product.id  
44 - assert cart?  
45 -  
46 - get :remove, :profile => enterprise.identifier, :id => product.id  
47 - assert !cart?  
48 - end  
49 -  
50 - should 'not try to remove a product if there is no cart' do  
51 - instantiate_session  
52 - assert !cart?  
53 -  
54 - assert_nothing_raised { get :remove, :profile => enterprise.identifier, :id => 9999 }  
55 - assert !response_ok?  
56 - assert_equal 2, reponse_error_code  
57 - end  
58 -  
59 - should 'just remove product if there are other products on cart' do  
60 - another_product = fast_create(Product, :enterprise_id => enterprise.id)  
61 - get :add, :profile => enterprise.identifier, :id => product.id  
62 - get :add, :profile => enterprise.identifier, :id => another_product.id  
63 -  
64 - get :remove, :profile => enterprise.identifier, :id => product.id  
65 - assert cart?  
66 - assert !product_in_cart?(product)  
67 - end  
68 -  
69 - should 'not try to remove a product that is not in the cart' do  
70 - get :add, :profile => enterprise.identifier, :id => product.id  
71 - assert cart?  
72 - assert_nothing_raised { get :remove, :profile => enterprise.identifier, :id => 9999 }  
73 -  
74 - assert !response_ok?  
75 - assert_equal 4, reponse_error_code  
76 - end  
77 -  
78 - should 'not try to list the cart if there is no cart' do  
79 - instantiate_session  
80 - assert !cart?  
81 -  
82 - assert_nothing_raised { get :list, :profile => enterprise.identifier }  
83 - assert !response_ok?  
84 - assert_equal 2, reponse_error_code  
85 - end  
86 -  
87 - should 'list products without errors' do  
88 - get :add, :profile => enterprise.identifier, :id => product.id  
89 -  
90 - assert_nothing_raised { get :list, :profile => enterprise.identifier }  
91 - assert response_ok?  
92 - end  
93 -  
94 - should 'update the quantity of a product' do  
95 - get :add, :profile => enterprise.identifier, :id => product.id  
96 - assert 1, product_quantity(product)  
97 -  
98 - get :update_quantity, :profile => enterprise.identifier, :id => product.id, :quantity => 3  
99 - assert 3, product_quantity(product)  
100 - end  
101 -  
102 - should 'not try to update quantity the quantity of a product if there is no cart' do  
103 - instantiate_session  
104 - assert !cart?  
105 -  
106 - assert_nothing_raised { get :update_quantity, :profile => enterprise.identifier, :id => 9999, :quantity => 3 }  
107 - assert !response_ok?  
108 - assert_equal 2, reponse_error_code  
109 - end  
110 -  
111 - should 'not try to update the quantity of a product that is not in the cart' do  
112 - get :add, :profile => enterprise.identifier, :id => product.id  
113 - assert cart?  
114 - assert_nothing_raised { get :update_quantity, :profile => enterprise.identifier, :id => 9999, :quantity => 3 }  
115 -  
116 - assert !response_ok?  
117 - assert_equal 4, reponse_error_code  
118 - end  
119 -  
120 - should 'not update the quantity of a product with a invalid value' do  
121 - get :add, :profile => enterprise.identifier, :id => product.id  
122 -  
123 - assert_nothing_raised { get :update_quantity, :profile => enterprise.identifier, :id => product.id, :quantity => -1}  
124 - assert !response_ok?  
125 - assert_equal 5, reponse_error_code  
126 -  
127 - assert_nothing_raised { get :update_quantity, :profile => enterprise.identifier, :id => product.id, :quantity => 'asdf'}  
128 - assert !response_ok?  
129 - assert_equal 5, reponse_error_code  
130 - end  
131 -  
132 - should 'clean the cart' do  
133 - another_product = fast_create(Product, :enterprise_id => enterprise.id)  
134 - get :add, :profile => enterprise.identifier, :id => product.id  
135 - get :add, :profile => enterprise.identifier, :id => another_product.id  
136 -  
137 - assert_nothing_raised { get :clean, :profile => enterprise.identifier }  
138 - assert !cart?  
139 - end  
140 -  
141 - should 'not crash if there is no cart' do  
142 - instantiate_session  
143 - assert !cart?  
144 - assert_nothing_raised { get :clean, :profile => enterprise.identifier }  
145 - end  
146 -  
147 - should 'register order on send request' do  
148 - product1 = fast_create(Product, :enterprise_id => enterprise.id, :price => 1.99)  
149 - product2 = fast_create(Product, :enterprise_id => enterprise.id, :price => 2.23)  
150 - @controller.stubs(:session).returns({:cart => {:items => {product1.id => 1, product2.id => 2}}})  
151 - assert_difference ShoppingCartPlugin::PurchaseOrder, :count, 1 do  
152 - post :send_request,  
153 - :customer => {:name => "Manuel", :email => "manuel@ceu.com"},  
154 - :profile => enterprise.identifier  
155 - end  
156 -  
157 - order = ShoppingCartPlugin::PurchaseOrder.last  
158 -  
159 - assert_equal 1.99, order.products_list[product1.id][:price]  
160 - assert_equal 1, order.products_list[product1.id][:quantity]  
161 - assert_equal 2.23, order.products_list[product2.id][:price]  
162 - assert_equal 2, order.products_list[product2.id][:quantity]  
163 - assert_equal ShoppingCartPlugin::PurchaseOrder::Status::OPENED, order.status  
164 - end  
165 -  
166 - should 'register order on send request and not crash if product is not defined' do  
167 - product1 = fast_create(Product, :enterprise_id => enterprise.id)  
168 - @controller.stubs(:session).returns({:cart => {:items => {product1.id => 1}}})  
169 - assert_difference ShoppingCartPlugin::PurchaseOrder, :count, 1 do  
170 - post :send_request,  
171 - :customer => {:name => "Manuel", :email => "manuel@ceu.com"},  
172 - :profile => enterprise.identifier  
173 - end  
174 -  
175 - order = ShoppingCartPlugin::PurchaseOrder.last  
176 -  
177 - assert_equal 0, order.products_list[product1.id][:price]  
178 - end  
179 -  
180 - private  
181 -  
182 - def json_response  
183 - ActiveSupport::JSON.decode @response.body  
184 - end  
185 -  
186 - def cart?  
187 - !session[:cart].nil?  
188 - end  
189 -  
190 - def product_in_cart?(product)  
191 - session[:cart][:items].has_key?(product.id)  
192 - end  
193 -  
194 - def product_quantity(product)  
195 - session[:cart][:items][product.id]  
196 - end  
197 -  
198 - def response_ok?  
199 - json_response['ok']  
200 - end  
201 -  
202 - def reponse_error_code  
203 - json_response['error']['code']  
204 - end  
205 -  
206 - # temporary hack...if I don't do this the session stays as an Array instead  
207 - # of a TestSession  
208 - def instantiate_session  
209 - get :add, :profile => enterprise.identifier, :id => product.id  
210 - get :remove, :profile => enterprise.identifier, :id => product.id  
211 - end  
212 -  
213 -end  
plugins/shopping_cart/test/unit/shopping_cart_plugin_test.rb
@@ -7,7 +7,6 @@ class ShoppingCartPluginTest &lt; ActiveSupport::TestCase @@ -7,7 +7,6 @@ class ShoppingCartPluginTest &lt; ActiveSupport::TestCase
7 @context = mock() 7 @context = mock()
8 @profile = mock() 8 @profile = mock()
9 @profile.stubs(:identifier).returns('random-user') 9 @profile.stubs(:identifier).returns('random-user')
10 - @context.stubs(:profile).returns(@profile)  
11 @shopping_cart.context = @context 10 @shopping_cart.context = @context
12 @shopping_cart.stubs(:profile).returns(@profile) 11 @shopping_cart.stubs(:profile).returns(@profile)
13 end 12 end
@@ -23,7 +22,8 @@ class ShoppingCartPluginTest &lt; ActiveSupport::TestCase @@ -23,7 +22,8 @@ class ShoppingCartPluginTest &lt; ActiveSupport::TestCase
23 product = fast_create(Product, :available => false) 22 product = fast_create(Product, :available => false)
24 enterprise = mock() 23 enterprise = mock()
25 enterprise.stubs(:shopping_cart).returns(true) 24 enterprise.stubs(:shopping_cart).returns(true)
  25 + product.stubs(:enterprise).returns(enterprise)
26 26
27 - assert_nil shopping_cart.add_to_cart_button(product, enterprise) 27 + assert_nil shopping_cart.add_to_cart_button(product)
28 end 28 end
29 end 29 end
plugins/shopping_cart/views/cart.html.erb
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 <a href="cart:clean" onclick="Cart.clean(this); return false" class="cart-clean"><%=_('Clean basket')%></a> 7 <a href="cart:clean" onclick="Cart.clean(this); return false" class="cart-clean"><%=_('Clean basket')%></a>
8 <ul class="cart-items"></ul> 8 <ul class="cart-items"></ul>
9 <div class="cart-total"><%=_('Total:')%> <b></b></div> 9 <div class="cart-total"><%=_('Total:')%> <b></b></div>
10 - <a href="cart:buy" class="cart-buy"><%=_('Shopping checkout')%></a> 10 + <a href="/plugin/shopping_cart/buy" class="cart-buy"><%=_('Shopping checkout')%></a>
11 </div> 11 </div>
12 <a href="#" onclick="Cart.toggle(this); return false" class="cart-toggle"> 12 <a href="#" onclick="Cart.toggle(this); return false" class="cart-toggle">
13 <span class="str-show"><%=_('Show basket')%></span> 13 <span class="str-show"><%=_('Show basket')%></span>
@@ -15,9 +15,3 @@ @@ -15,9 +15,3 @@
15 </a> 15 </a>
16 </div> 16 </div>
17 </div> 17 </div>
18 -  
19 -<script type="text/javascript">  
20 -//<![CDATA[  
21 - new Cart({hasProducts:<%= !locals[:cart].nil? ? "true, enterprise:'#{Enterprise.find(locals[:cart][:enterprise_id]).identifier}'" : "false" %>});  
22 -//]]>  
23 -</script>  
plugins/shopping_cart/views/shopping_cart_plugin/buy.html.erb 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +<% person = user.nil? ? Person.new : user %>
  2 +<div id='cart-request-box'>
  3 + <% form_for(:customer, person, :url => {:action => 'send_request'},
  4 + :html => {:onsubmit => "return Cart.send_request(this)", :id => 'cart-request-form' }) do |f| %>
  5 + <div id="cart-form-main">
  6 + <%= labelled_form_field('* ' + _("Name"), f.text_field(:name, :class => 'required') ) %>
  7 + <%= labelled_form_field('* ' + _("Email"), f.text_field(:email, :class => 'required email') ) %>
  8 + <%= labelled_form_field('* ' + _("Contact phone"), f.text_field(:contact_phone, :class => 'required') ) %>
  9 + <%= labelled_form_field(_('Delivery option'), select_tag(:delivery_option, options_for_select(select_delivery_options(@settings.delivery_options, environment)), 'data-profile-identifier' => @enterprise.identifier)) unless !@settings.delivery || (@settings.free_delivery_price && get_total(@cart[:items]) >= @settings.free_delivery_price) %>
  10 + <%= labelled_form_field(_('Payment'), select_tag('customer[payment]', options_for_select([[_("Money"), :money],[_('Check'), :check]]))) %>
  11 + <%= labelled_form_field(_('Change'), text_field_tag('customer[change]')) %>
  12 + </div>
  13 + <% if @settings.delivery %>
  14 + <fieldset><legend><%=_('Delivery Address')%></legend>
  15 + <%= labelled_form_field(_('Address (street and number)'), f.text_field(:address)) %>
  16 + <%= labelled_form_field(_('Address reference'), f.text_field(:address_reference)) %>
  17 + <%= labelled_form_field(_('District'), f.text_field(:district)) %>
  18 + <%= labelled_form_field( _("City"), f.text_field(:city)) %>
  19 + <%= labelled_form_field(_('ZIP code'), f.text_field(:zip_code)) %>
  20 + </fieldset>
  21 + <% end %>
  22 + <div id="cart-form-actions">
  23 + <%= submit_button(:send, _('Send buy request')) %>
  24 + </div>
  25 + <% end %>
  26 + <% delivery_option = @settings.delivery_options.first && @settings.delivery_options.first.first %>
  27 + <%= items_table(@cart[:items], @enterprise, delivery_option) %>
  28 + <%= link_to '', '#', :onclick => "Cart.colorbox_close(this);", :class => 'cart-box-close icon-cancel' %>
  29 +</div>
  30 +
  31 +<%= javascript_include_tag '../plugins/shopping_cart/buy' %>
plugins/shopping_cart/views/shopping_cart_plugin/mailer/customer_notification.html.erb
@@ -17,26 +17,35 @@ @@ -17,26 +17,35 @@
17 <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li> 17 <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li>
18 <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li> 18 <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li>
19 <li><b><%= _('Phone number') %>: </b><%= @customer[:contact_phone] %></li> 19 <li><b><%= _('Phone number') %>: </b><%= @customer[:contact_phone] %></li>
20 - <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? %> 20 + <li><b><%= _('Payment') %>: </b><%= @customer[:payment] == 'money' ? _('Money') : _('Check') %></li>
  21 + <% if @customer[:payment] == 'money' %>
  22 + <li><b><%= _('Change') %>: </b><%= @customer[:change] %></li>
  23 + <% end %>
  24 + <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? || !@customer[:district].blank? || !@customer[:address_reference].blank? %>
21 <li><b><%= _('Address') %>:</b> 25 <li><b><%= _('Address') %>:</b>
22 <% end %> 26 <% end %>
23 <% if !@customer[:address].blank? %> 27 <% if !@customer[:address].blank? %>
24 <%= @customer[:address] %><br \> 28 <%= @customer[:address] %><br \>
25 <% end %> 29 <% end %>
  30 + <% if !@customer[:district].blank? %>
  31 + <%= @customer[:district] %><br \>
  32 + <% end %>
26 <% if !@customer[:city].blank? %> 33 <% if !@customer[:city].blank? %>
27 <%= @customer[:city] %><br \> 34 <%= @customer[:city] %><br \>
28 <% end %> 35 <% end %>
29 <% if !@customer[:zip_code].blank? %> 36 <% if !@customer[:zip_code].blank? %>
30 <%= @customer[:zip_code] %> 37 <%= @customer[:zip_code] %>
31 <% end %> 38 <% end %>
32 - <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? %> 39 + <% if !@customer[:address_reference].blank? %>
  40 + <%= @customer[:address_reference] %><br \>
  41 + <% end %>
  42 + <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? || !@customer[:district].blank? || !@customer[:address_reference].blank? %>
33 </li> 43 </li>
34 <% end %> 44 <% end %>
35 </ul> 45 </ul>
36 46
37 <p><%=_('Here are the products you bought:')%></p> 47 <p><%=_('Here are the products you bought:')%></p>
38 - <%= items_table(@items, @supplier, true) %>  
39 - 48 + <%= @helper.items_table(@items, @supplier, @delivery_option, true) %>
40 <p> 49 <p>
41 --<br/> 50 --<br/>
42 <%=_('Thanks for buying with us!')%><br/> 51 <%=_('Thanks for buying with us!')%><br/>
plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb
@@ -15,26 +15,35 @@ @@ -15,26 +15,35 @@
15 <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li> 15 <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li>
16 <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li> 16 <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li>
17 <li><b><%= _('Phone number') %>: </b><%= @customer[:contact_phone] %></li> 17 <li><b><%= _('Phone number') %>: </b><%= @customer[:contact_phone] %></li>
18 - <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? %> 18 + <li><b><%= _('Payment') %>: </b><%= @customer[:payment] == 'money' ? _('Money') : _('Check') %></li>
  19 + <% if @customer[:payment] == 'money' %>
  20 + <li><b><%= _('Change') %>: </b><%= @customer[:change] %></li>
  21 + <% end %>
  22 + <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? || !@customer[:district].blank? || !@customer[:address_reference].blank? %>
19 <li><b><%= _('Address') %>:</b> 23 <li><b><%= _('Address') %>:</b>
20 <% end %> 24 <% end %>
21 <% if !@customer[:address].blank? %> 25 <% if !@customer[:address].blank? %>
22 <%= @customer[:address] %><br \> 26 <%= @customer[:address] %><br \>
23 <% end %> 27 <% end %>
  28 + <% if !@customer[:district].blank? %>
  29 + <%= @customer[:district] %><br \>
  30 + <% end %>
24 <% if !@customer[:city].blank? %> 31 <% if !@customer[:city].blank? %>
25 <%= @customer[:city] %><br \> 32 <%= @customer[:city] %><br \>
26 <% end %> 33 <% end %>
27 <% if !@customer[:zip_code].blank? %> 34 <% if !@customer[:zip_code].blank? %>
28 <%= @customer[:zip_code] %> 35 <%= @customer[:zip_code] %>
29 <% end %> 36 <% end %>
30 - <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? %> 37 + <% if !@customer[:address_reference].blank? %>
  38 + <%= @customer[:address_reference] %><br \>
  39 + <% end %>
  40 + <% if !@customer[:address].blank? || !@customer[:city].blank? || !@customer[:zip_code].blank? || !@customer[:district].blank? || !@customer[:address_reference].blank? %>
31 </li> 41 </li>
32 <% end %> 42 <% end %>
33 </ul> 43 </ul>
34 44
35 <p><%=_('And here are the items bought by this customer:')%></p> 45 <p><%=_('And here are the items bought by this customer:')%></p>
36 - <%= items_table(@items, @supplier, true) %>  
37 - 46 + <%= @helper.items_table(@items, @supplier, @delivery_option, true) %>
38 <p> 47 <p>
39 --<br/> 48 --<br/>
40 <%=_('If there are any problems with this email contact the admin of %s.') % @environment.name %> 49 <%=_('If there are any problems with this email contact the admin of %s.') % @environment.name %>
plugins/shopping_cart/views/shopping_cart_plugin/send_request.html.erb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +<%= _("Request sent successfully check your email.")%>
plugins/shopping_cart/views/shopping_cart_plugin_myprofile/_orders_list.html.erb
@@ -32,7 +32,9 @@ @@ -32,7 +32,9 @@
32 <td class="order-info" colspan="5"> 32 <td class="order-info" colspan="5">
33 <div style="display:none"> 33 <div style="display:none">
34 <ul class="customer-details"> 34 <ul class="customer-details">
35 - <% { 'name' =>_('Name'), 'email' => _('E-mail'), 'contact_phone' => _('Contact phone'), 'address' => _('Address'), 'city' => _('City'), 'zip_code' => _('Zip code')}.each do |attribute, name| %> 35 + <% [['name', _('Name')], ['email', _('E-mail')], ['contact_phone', _('Contact phone')], ['address', _('Address')], ['district', _('District')], ['city', _('City')], ['zip_code', _('Zip code')], ['delivery_option', _('Delivery option')], ['payment', _('Payment')], ['change', _('Change')]].each do |field| %>
  36 + <% attribute = field.first %>
  37 + <% name = field.last %>
36 <%= content_tag('li', content_tag('strong', name+': ') + order.send('customer_'+attribute)) if !order.send('customer_'+attribute).blank? %> 38 <%= content_tag('li', content_tag('strong', name+': ') + order.send('customer_'+attribute)) if !order.send('customer_'+attribute).blank? %>
37 <% end %> 39 <% end %>
38 </ul> 40 </ul>