Commit 55f22c3ea607c019bd260c753e50d87ebecfa690
Exists in
master
and in
28 other branches
[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.
AUTHORS
... | ... | @@ -6,45 +6,144 @@ noosfero, that's not a problem). |
6 | 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 | 27 | Antonio Terceiro + Carlos Morais <terceiro@colivre.coop.br> |
10 | 28 | Antonio Terceiro + Paulo Meirelles <terceiro@colivre.coop.br> |
11 | 29 | Antonio Terceiro <terceiro@colivre.coop.br> |
12 | 30 | Aurelio A. Heckert <aurelio@colivre.coop.br> |
13 | 31 | Braulio Bhavamitra <brauliobo@gmail.com> |
14 | 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 | 54 | Caio SBA <caio@colivre.coop.br> |
16 | 55 | Carlos Morais <carlos88morais@gmail.com> |
17 | 56 | Carlos Morais + Diego Araújo <diegoamc90@gmail.com> |
57 | +Carlos Morais + Eduardo Morais <carlos88morais@gmail.com> | |
18 | 58 | Carlos Morais + Paulo Meirelles <carlos88morais@gmail.com> |
59 | +Carlos Morais + Pedro Leal <carlos88morais@gmail.com> | |
19 | 60 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> |
20 | 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 | 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 | 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 | 81 | Fernanda Lopes <nanda.listas+psl@gmail.com> |
24 | 82 | Grazieno Pellegrino <grazieno@gmail.com> |
25 | 83 | Isaac Canan <isaac@intelletto.com.br> |
26 | 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 | 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 | 96 | João M. M. da Silva + Carlos Morais <jaodsilv@linux.ime.usp.br> |
29 | 97 | João M. M. da Silva + Diego Araújo <diegoamc90@gmail.com> |
30 | 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 | 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 | 115 | Joenio Costa <joenio@colivre.coop.br> |
33 | 116 | Josef Spillner <josef.spillner@tu-dresden.de> |
34 | 117 | Keilla Menezes <keilla@colivre.coop.br> |
35 | 118 | Larissa Reis <larissa@colivre.coop.br> |
36 | 119 | Larissa Reis <reiss.larissa@gmail.com> |
37 | 120 | Leandro Nunes dos Santos <leandronunes@gmail.com> |
121 | +Leandro Nunes dos Santos <leandro.santos@serpro.gov.br> | |
38 | 122 | LinguÁgil 2010 <linguagil.bahia@gmail.com> |
123 | +Luis David Aguilar Carlos <ludwig9003@gmail.com> | |
39 | 124 | Martín Olivera <molivera@solar.org.ar> |
40 | 125 | Moises Machado <moises@colivre.coop.br> |
41 | 126 | Nanda Lopes <nanda.listas+psl@gmail.com> |
127 | +Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org> | |
42 | 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 | 131 | Paulo Meirelles <paulo@softwarelivre.org> |
44 | 132 | Rafael Gomes <rafaelgomes@techfree.com.br> |
133 | +Rafael Manzo + João M. M. Silva <rr.manzo@gmail.com> | |
45 | 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 | 139 | Raphaël Rousseau <raph@r4f.org> |
47 | 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 | 147 | Rodrigo Souto <rodrigo@colivre.coop.br> |
49 | 148 | Ronny Kursawe <kursawe.ronny@googlemail.com> |
50 | 149 | Samuel R. C. Vale <srcvale@holoscopio.com> | ... | ... |
HACKING
1 | 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. | ... | ... |
INSTALL
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 | 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 | 32 | You need to install some packages Noosfero depends on. On Debian GNU/Linux or |
13 | 33 | Debian-based systems, all of these packages are available through the Debian |
14 | 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 | 58 | please feel free to contact the Noosfero development mailing with the |
39 | 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 | 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 | 64 | Install memcached. On Debian: |
57 | 65 | |
58 | 66 | # apt-get install memcached |
... | ... | @@ -96,11 +104,11 @@ $ git checkout -b stable origin/stable |
96 | 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 | 112 | $ cd current |
105 | 113 | |
106 | 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 | 116 | |
109 | 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 | 121 | Edit config/thin.yml to suit your needs. Make sure your apache |
114 | 122 | configuration matches the thin cluster configuration, specially in respect |
... | ... | @@ -147,48 +155,7 @@ Restart postgresql: |
147 | 155 | |
148 | 156 | Noosfero needs a functional e-mail setup to work: the local mail system should |
149 | 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 | 160 | As noosfero user |
194 | 161 | ================ |
... | ... | @@ -220,19 +187,6 @@ $ ./script/dbconsole production |
220 | 187 | If it connects to your database, then everything is fine. If you got an error |
221 | 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 | 190 | Create the database structure: |
237 | 191 | |
238 | 192 | $ RAILS_ENV=production rake db:schema:load |
... | ... | @@ -245,7 +199,7 @@ Run Solr: |
245 | 199 | |
246 | 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 | 203 | (the first one), run the command below: |
250 | 204 | |
251 | 205 | $ RAILS_ENV=production ./script/runner 'Environment.create!(:name => "My environment", :is_default => true)' |
... | ... | @@ -261,10 +215,10 @@ $ RAILS_ENV=production ./script/runner "Environment.default.domains << Domain.ne |
261 | 215 | |
262 | 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 | 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 | 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 | 228 | thing left is to configure your webserver as a reverse proxy to pass requests |
275 | 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 | 233 | Apache instalation |
... | ... | @@ -384,6 +321,26 @@ Now restart your apache server (as root): |
384 | 321 | |
385 | 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 | 345 | Maintainance |
389 | 346 | ============ | ... | ... |
... | ... | @@ -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 | 5 | |
6 | 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 | 11 | 2) install Varnish |
11 | 12 | |
12 | 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 | 15 | Install the RPAF apache module (or skip this step if not using apache): |
19 | 16 | |
20 | 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 | 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 | 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 | 66 | Thanks to Cosimo Streppone for varnish-accept-language. See |
77 | 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 < MyProfileController |
16 | 16 | |
17 | 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 | 26 | user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)) |
21 | 27 | end |
22 | 28 | |
... | ... | @@ -92,7 +98,7 @@ class CmsController < MyProfileController |
92 | 98 | @article_types = [] |
93 | 99 | available_article_types.each do |type| |
94 | 100 | @article_types.push({ |
95 | - :name => type.name, | |
101 | + :class => type, | |
96 | 102 | :short_description => type.short_description, |
97 | 103 | :description => type.description |
98 | 104 | }) |
... | ... | @@ -154,7 +160,7 @@ class CmsController < MyProfileController |
154 | 160 | end |
155 | 161 | if request.post? && params[:uploaded_files] |
156 | 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 | 164 | end |
159 | 165 | @errors = @uploaded_files.select { |f| f.errors.any? } |
160 | 166 | if @errors.any? | ... | ... |
app/controllers/my_profile/tasks_controller.rb
... | ... | @@ -9,7 +9,7 @@ class TasksController < MyProfileController |
9 | 9 | end |
10 | 10 | |
11 | 11 | def processed |
12 | - @tasks = Task.to(profile).finished.sort_by(&:created_at) | |
12 | + @tasks = Task.to(profile).closed.sort_by(&:created_at) | |
13 | 13 | end |
14 | 14 | |
15 | 15 | VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] | ... | ... |
app/controllers/public/account_controller.rb
... | ... | @@ -4,6 +4,7 @@ class AccountController < ApplicationController |
4 | 4 | |
5 | 5 | before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise] |
6 | 6 | before_filter :redirect_if_logged_in, :only => [:login, :signup] |
7 | + before_filter :protect_from_bots, :only => :signup | |
7 | 8 | |
8 | 9 | # say something nice, you goof! something sweet. |
9 | 10 | def index |
... | ... | @@ -55,6 +56,11 @@ class AccountController < ApplicationController |
55 | 56 | render :action => 'login', :layout => false |
56 | 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 | 64 | # action to register an user to the application |
59 | 65 | def signup |
60 | 66 | if @plugins.dispatch(:allow_user_registration).include?(false) |
... | ... | @@ -62,6 +68,7 @@ class AccountController < ApplicationController |
62 | 68 | session[:notice] = _("This environment doesn't allow user registration.") |
63 | 69 | end |
64 | 70 | |
71 | + @block_bot = !!session[:may_be_a_bot] | |
65 | 72 | @invitation_code = params[:invitation_code] |
66 | 73 | begin |
67 | 74 | if params[:user] |
... | ... | @@ -76,19 +83,28 @@ class AccountController < ApplicationController |
76 | 83 | @person = Person.new(params[:profile_data]) |
77 | 84 | @person.environment = @user.environment |
78 | 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 | 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 | 108 | end |
93 | 109 | end |
94 | 110 | rescue ActiveRecord::RecordInvalid |
... | ... | @@ -97,6 +113,7 @@ class AccountController < ApplicationController |
97 | 113 | @person.errors.delete(:user_id) |
98 | 114 | render :action => 'signup' |
99 | 115 | end |
116 | + clear_signup_start_time | |
100 | 117 | end |
101 | 118 | |
102 | 119 | # action to perform logout from the application |
... | ... | @@ -271,7 +288,36 @@ class AccountController < ApplicationController |
271 | 288 | def no_redirect |
272 | 289 | @cannot_redirect = true |
273 | 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 | 321 | def check_answer |
276 | 322 | unless answer_correct |
277 | 323 | @enterprise.block | ... | ... |
app/controllers/public/catalog_controller.rb
1 | 1 | class CatalogController < PublicController |
2 | 2 | needs_profile |
3 | + no_design_blocks | |
3 | 4 | |
4 | 5 | before_filter :check_enterprise_and_environment |
5 | 6 | |
6 | 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 | 11 | end |
9 | 12 | |
10 | 13 | protected | ... | ... |
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -25,24 +25,26 @@ class ContentViewerController < ApplicationController |
25 | 25 | return |
26 | 26 | end |
27 | 27 | end |
28 | - | |
29 | - # page not found, give error | |
30 | - if @page.nil? | |
31 | - render_not_found(@path) | |
32 | - return | |
33 | - end | |
34 | 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 | 37 | render_access_denied(message) |
40 | - elsif !profile.public? | |
41 | - redirect_to :controller => 'profile', :action => 'index', :profile => profile.identifier | |
42 | 38 | end |
43 | 39 | return |
44 | 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 | 48 | if request.xhr? && params[:toolbar] |
47 | 49 | render :partial => 'article_toolbar' |
48 | 50 | return | ... | ... |
app/controllers/public/profile_controller.rb
... | ... | @@ -368,18 +368,13 @@ class ProfileController < PublicController |
368 | 368 | end |
369 | 369 | |
370 | 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 | 372 | end |
380 | 373 | |
381 | 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 | 378 | end |
384 | 379 | |
385 | 380 | def per_page | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -1284,7 +1284,7 @@ module ApplicationHelper |
1284 | 1284 | (user.already_reported?(profile) ? |
1285 | 1285 | content_tag('a', text, :class => klass + ' disabled comment-footer comment-footer-link', :title => already_reported_message) : |
1286 | 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 | 1288 | end |
1289 | 1289 | end |
1290 | 1290 | |
... | ... | @@ -1337,11 +1337,12 @@ module ApplicationHelper |
1337 | 1337 | counter = 0 |
1338 | 1338 | radios = klass.templates.map do |template| |
1339 | 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 | 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 | 1346 | :id => 'template-options', |
1346 | 1347 | :style => 'margin-top: 1em' |
1347 | 1348 | ) |
... | ... | @@ -1410,4 +1411,16 @@ module ApplicationHelper |
1410 | 1411 | options[:class] = "comment-footer comment-footer-link comment-footer-hide" |
1411 | 1412 | expirable_content_reference content, action, text, url, options |
1412 | 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 | 1426 | end | ... | ... |
app/helpers/catalog_helper.rb
... | ... | @@ -3,4 +3,28 @@ module CatalogHelper |
3 | 3 | include DisplayHelper |
4 | 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(' → '), :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 | 30 | end | ... | ... |
app/helpers/content_viewer_helper.rb
... | ... | @@ -26,7 +26,7 @@ module ContentViewerHelper |
26 | 26 | end |
27 | 27 | title << content_tag('span', |
28 | 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 | 30 | content_tag('span', comments, :class => 'comments'), |
31 | 31 | :class => 'created-at' |
32 | 32 | ) | ... | ... |
app/helpers/display_helper.rb
... | ... | @@ -8,6 +8,14 @@ module DisplayHelper |
8 | 8 | opts |
9 | 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 | 19 | def image_link_to_product(product, opts={}) |
12 | 20 | return _('No product') unless product |
13 | 21 | target = product_path(product) | ... | ... |
app/helpers/folder_helper.rb
... | ... | @@ -52,8 +52,8 @@ module FolderHelper |
52 | 52 | end |
53 | 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 | 57 | end |
58 | 58 | |
59 | 59 | def custom_options_for_article(article) | ... | ... |
app/helpers/forms_helper.rb
... | ... | @@ -142,6 +142,38 @@ module FormsHelper |
142 | 142 | content_tag('table',rows.join("\n")) |
143 | 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 | 177 | def date_field(name, value, format = '%Y-%m-%d', datepicker_options = {}, html_options = {}) |
146 | 178 | datepicker_options[:disabled] ||= false |
147 | 179 | datepicker_options[:alt_field] ||= '' | ... | ... |
app/helpers/language_helper.rb
... | ... | @@ -13,19 +13,20 @@ module LanguageHelper |
13 | 13 | |
14 | 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 | 19 | current = language |
19 | 20 | separator = options[:separator] || ' — ' |
20 | 21 | |
21 | 22 | if options[:element] == 'dropdown' |
22 | 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 | 25 | :onchange => "document.location.href= #{url_for(params.merge(:lang => 'LANGUAGE')).inspect}.replace(/LANGUAGE/, this.value) ;", |
25 | 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 | 28 | else |
28 | - languages = environment.locales.map do |code,name| | |
29 | + languages = locales.map do |code,name| | |
29 | 30 | if code == current |
30 | 31 | content_tag('strong', name) |
31 | 32 | else | ... | ... |
app/models/approve_article.rb
... | ... | @@ -48,7 +48,7 @@ class ApproveArticle < Task |
48 | 48 | end |
49 | 49 | |
50 | 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 | 52 | end |
53 | 53 | |
54 | 54 | def title | ... | ... |
app/models/article.rb
... | ... | @@ -13,10 +13,18 @@ class Article < ActiveRecord::Base |
13 | 13 | # xss_terminate plugin can't sanitize array fields |
14 | 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 | 22 | belongs_to :profile |
17 | 23 | validates_presence_of :profile_id, :name |
18 | 24 | validates_presence_of :slug, :path, :if => lambda { |article| !article.name.blank? } |
19 | 25 | |
26 | + validates_length_of :name, :maximum => 150 | |
27 | + | |
20 | 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 | 30 | belongs_to :last_changed_by, :class_name => 'Person', :foreign_key => 'last_changed_by_id' |
... | ... | @@ -289,7 +297,7 @@ class Article < ActiveRecord::Base |
289 | 297 | if last_comment |
290 | 298 | {:date => last_comment.created_at, :author_name => last_comment.author_name, :author_url => last_comment.author_url} |
291 | 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 | 301 | end |
294 | 302 | end |
295 | 303 | |
... | ... | @@ -441,7 +449,7 @@ class Article < ActiveRecord::Base |
441 | 449 | end |
442 | 450 | |
443 | 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 | 453 | end |
446 | 454 | |
447 | 455 | def allow_publish_content?(user = nil) |
... | ... | @@ -496,7 +504,6 @@ class Article < ActiveRecord::Base |
496 | 504 | :slug, |
497 | 505 | :updated_at, |
498 | 506 | :created_at, |
499 | - :last_changed_by_id, | |
500 | 507 | :version, |
501 | 508 | :lock_version, |
502 | 509 | :type, |
... | ... | @@ -539,15 +546,24 @@ class Article < ActiveRecord::Base |
539 | 546 | end |
540 | 547 | |
541 | 548 | def author |
542 | - if reference_article | |
543 | - reference_article.author | |
549 | + if versions.empty? | |
550 | + last_changed_by | |
544 | 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 | 554 | end |
547 | 555 | end |
548 | 556 | |
549 | 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 | 567 | end |
552 | 568 | |
553 | 569 | alias :active_record_cache_key :cache_key |
... | ... | @@ -572,11 +588,6 @@ class Article < ActiveRecord::Base |
572 | 588 | truncate sanitize_html(self.lead), :length => 170, :omission => '...' |
573 | 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 | 591 | def notifiable? |
581 | 592 | false |
582 | 593 | end | ... | ... |
app/models/category.rb
... | ... | @@ -13,6 +13,16 @@ class Category < ActiveRecord::Base |
13 | 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 | 26 | acts_as_filesystem |
17 | 27 | |
18 | 28 | has_many :article_categorizations, :dependent => :destroy | ... | ... |
app/models/comment.rb
... | ... | @@ -74,6 +74,10 @@ class Comment < ActiveRecord::Base |
74 | 74 | self.find(:all, :order => 'created_at desc, id desc', :limit => limit) |
75 | 75 | end |
76 | 76 | |
77 | + def notification_emails | |
78 | + self.article.profile.notification_emails - [self.author_email || self.email] | |
79 | + end | |
80 | + | |
77 | 81 | after_save :notify_article |
78 | 82 | after_destroy :notify_article |
79 | 83 | def notify_article |
... | ... | @@ -114,7 +118,7 @@ class Comment < ActiveRecord::Base |
114 | 118 | |
115 | 119 | def notify_by_mail |
116 | 120 | if source.kind_of?(Article) && article.notify_comments? |
117 | - if !article.profile.notification_emails.empty? | |
121 | + if !notification_emails.empty? | |
118 | 122 | Comment::Notifier.deliver_mail(self) |
119 | 123 | end |
120 | 124 | emails = article.followers - [author_email] |
... | ... | @@ -174,7 +178,7 @@ class Comment < ActiveRecord::Base |
174 | 178 | class Notifier < ActionMailer::Base |
175 | 179 | def mail(comment) |
176 | 180 | profile = comment.article.profile |
177 | - recipients profile.notification_emails | |
181 | + recipients comment.notification_emails | |
178 | 182 | from "#{profile.environment.name} <#{profile.environment.contact_email}>" |
179 | 183 | subject _("[%s] you got a new comment!") % [profile.environment.name] |
180 | 184 | body :recipient => profile.nickname || profile.name, |
... | ... | @@ -224,6 +228,7 @@ class Comment < ActiveRecord::Base |
224 | 228 | def spam! |
225 | 229 | self.spam = true |
226 | 230 | self.save! |
231 | + SpammerLogger.log(ip_address, self) | |
227 | 232 | Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam)) |
228 | 233 | self |
229 | 234 | end | ... | ... |
app/models/enterprise_homepage.rb
app/models/environment.rb
... | ... | @@ -233,8 +233,10 @@ class Environment < ActiveRecord::Base |
233 | 233 | settings[:message_for_member_invitation] || InviteMember.mail_template |
234 | 234 | end |
235 | 235 | |
236 | + settings_items :min_signup_delay, :type => Integer, :default => 3 #seconds | |
236 | 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 | 240 | settings_items :location, :type => String |
239 | 241 | settings_items :layout_template, :type => String, :default => 'default' |
240 | 242 | settings_items :homepage, :type => String | ... | ... |
app/models/event.rb
... | ... | @@ -38,7 +38,7 @@ class Event < Article |
38 | 38 | filter_iframes :body, :link, :address, :whitelist => lambda { profile && profile.environment && profile.environment.trusted_sites_for_iframe } |
39 | 39 | |
40 | 40 | def self.description |
41 | - _('A calendar event') | |
41 | + _('A calendar event.') | |
42 | 42 | end |
43 | 43 | |
44 | 44 | def self.short_description | ... | ... |
app/models/organization.rb
... | ... | @@ -78,6 +78,8 @@ class Organization < Profile |
78 | 78 | country |
79 | 79 | tag_list |
80 | 80 | template_id |
81 | + district | |
82 | + address_reference | |
81 | 83 | ] |
82 | 84 | |
83 | 85 | def self.fields |
... | ... | @@ -96,8 +98,8 @@ class Organization < Profile |
96 | 98 | [] |
97 | 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 | 104 | validates_format_of :foundation_year, :with => Noosfero::Constants::INTEGER_FORMAT |
103 | 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 < Profile |
67 | 67 | :order => 'total DESC', |
68 | 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 | 73 | after_destroy do |person| |
71 | 74 | Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy } |
72 | 75 | end |
... | ... | @@ -144,6 +147,9 @@ class Person < Profile |
144 | 147 | contact_phone |
145 | 148 | contact_information |
146 | 149 | description |
150 | + image | |
151 | + district | |
152 | + address_reference | |
147 | 153 | ] |
148 | 154 | |
149 | 155 | validates_multiparameter_assignments |
... | ... | @@ -198,8 +204,8 @@ class Person < Profile |
198 | 204 | N_('Education'); N_('Custom education'); N_('Custom area of study'); |
199 | 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 | 210 | extend SetProfileRegionFromCityState::ClassMethods |
205 | 211 | set_profile_region_from_city_state |
... | ... | @@ -440,6 +446,10 @@ class Person < Profile |
440 | 446 | abuse_report.save! |
441 | 447 | end |
442 | 448 | |
449 | + def abuser? | |
450 | + AbuseComplaint.finished.where(:requestor_id => self).count > 0 | |
451 | + end | |
452 | + | |
443 | 453 | def control_panel_settings_button |
444 | 454 | {:title => _('Edit Profile'), :icon => 'edit-profile'} |
445 | 455 | end | ... | ... |
app/models/product.rb
... | ... | @@ -23,6 +23,10 @@ class Product < ActiveRecord::Base |
23 | 23 | |
24 | 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 | 30 | after_update :save_image |
27 | 31 | |
28 | 32 | def lat | ... | ... |
app/models/profile.rb
... | ... | @@ -141,6 +141,10 @@ class Profile < ActiveRecord::Base |
141 | 141 | |
142 | 142 | acts_as_having_settings :field => :data |
143 | 143 | |
144 | + def settings | |
145 | + data | |
146 | + end | |
147 | + | |
144 | 148 | settings_items :redirect_l10n, :type => :boolean, :default => false |
145 | 149 | settings_items :public_content, :type => :boolean, :default => true |
146 | 150 | settings_items :description |
... | ... | @@ -229,7 +233,7 @@ class Profile < ActiveRecord::Base |
229 | 233 | if myregion |
230 | 234 | myregion.hierarchy.reverse.first(2).map(&:name).join(separator) |
231 | 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 | 237 | end |
234 | 238 | end |
235 | 239 | |
... | ... | @@ -463,6 +467,10 @@ class Profile < ActiveRecord::Base |
463 | 467 | { :profile => identifier, :controller => 'profile_editor', :action => 'index' } |
464 | 468 | end |
465 | 469 | |
470 | + def tasks_url | |
471 | + { :profile => identifier, :controller => 'tasks', :action => 'index', :host => default_hostname } | |
472 | + end | |
473 | + | |
466 | 474 | def leave_url(reload = false) |
467 | 475 | { :profile => identifier, :controller => 'profile', :action => 'leave', :reload => reload } |
468 | 476 | end |
... | ... | @@ -694,7 +702,7 @@ private :generate_url, :url_options |
694 | 702 | def custom_footer_expanded |
695 | 703 | footer = custom_footer |
696 | 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 | 706 | if self.respond_to?(att) && footer.match(/\{[^{]*#{att}\}/) |
699 | 707 | if !self.send(att).nil? && !self.send(att).blank? |
700 | 708 | footer = footer.gsub(/\{([^{]*)#{att}\}/, '\1' + self.send(att)) | ... | ... |
app/models/raw_html_article.rb
... | ... | @@ -5,11 +5,11 @@ class RawHTMLArticle < TextArticle |
5 | 5 | end |
6 | 6 | |
7 | 7 | def self.short_description |
8 | - _('Raw HTML text article.') | |
8 | + _('Raw HTML text article') | |
9 | 9 | end |
10 | 10 | |
11 | 11 | def self.description |
12 | - _('Allows HTML without filter (only for admins)') | |
12 | + _('Allows HTML without filter (only for admins).') | |
13 | 13 | end |
14 | 14 | |
15 | 15 | xss_terminate :only => [ ] | ... | ... |
... | ... | @@ -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 < ActiveRecord::Base |
267 | 267 | end |
268 | 268 | |
269 | 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 | 274 | named_scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } |
272 | 275 | named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } |
273 | 276 | named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } | ... | ... |
app/models/task_mailer.rb
... | ... | @@ -14,7 +14,7 @@ class TaskMailer < ActionMailer::Base |
14 | 14 | |
15 | 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 | 19 | from self.class.generate_from(task) |
20 | 20 | subject '[%s] %s' % [task.environment.name, task.target_notification_description] | ... | ... |
app/models/tiny_mce_article.rb
app/models/user.rb
... | ... | @@ -31,7 +31,7 @@ class User < ActiveRecord::Base |
31 | 31 | after_create do |user| |
32 | 32 | user.person ||= Person.new |
33 | 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 | 35 | user.person.visible = false unless user.activated? |
36 | 36 | user.person.save! |
37 | 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 | 26 | <%= hidden_field_tag :invitation_code, @invitation_code %> |
6 | 27 | |
7 | 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> </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> </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 | 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 | 41 | </div> |
18 | 42 | <%= observe_field 'user_login', |
19 | 43 | :url => { :action => 'check_url' }, |
... | ... | @@ -26,20 +50,19 @@ |
26 | 50 | |
27 | 51 | <div id='signup-password'> |
28 | 52 | <%= required f.password_field(:password, :id => 'user_pw') %> |
29 | - <%= f.text_field(:password_clear, :value => _('password')) %> | |
30 | 53 | <%= content_tag(:small,_('Choose a password that you can remember easily. It must have at least 4 characters.'), :id => 'password-balloon') %> |
31 | 54 | <div id='fake-check'><p> </p></div> |
32 | 55 | </div> |
33 | 56 | |
34 | 57 | <div id='signup-password-confirmation'> |
35 | 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 | 60 | <div id='password-check'><p> </p></div> |
38 | 61 | </div> |
39 | 62 | |
40 | 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 | 66 | <div id='email-check'><p> </p></div> |
44 | 67 | </div> |
45 | 68 | <%= observe_field "user_email", |
... | ... | @@ -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 | 93 | </div> |
69 | 94 | |
70 | 95 | <div id="signup-form-profile"> |
71 | 96 | |
72 | - <%= template_options(Person, 'profile_data') %> | |
73 | - | |
74 | 97 | <% labelled_fields_for :profile_data, @person do |f| %> |
75 | 98 | <%= render :partial => 'profile_editor/person_form', :locals => {:f => f} %> |
76 | 99 | <% end %> |
77 | 100 | |
78 | 101 | <%= @plugins.dispatch(:signup_extra_contents).collect { |content| instance_eval(&content) }.join("") %> |
79 | 102 | |
103 | + <%= template_options(Person, 'profile_data') %> | |
104 | + | |
80 | 105 | <% unless @terms_of_use.blank? %> |
81 | 106 | <div id='terms-of-use-box' class='formfieldline'> |
82 | 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 | 116 | <% end %> |
92 | 117 | </div> |
93 | 118 | |
119 | +<%= recaptcha_tags :ajax => true, :display => {:theme => 'clean'} if @block_bot %> | |
120 | + | |
94 | 121 | <p style="text-align: center"> |
95 | 122 | <%= submit_button('save', _('Create my account')) %> |
96 | 123 | </p> |
... | ... | @@ -99,70 +126,59 @@ |
99 | 126 | |
100 | 127 | <script type="text/javascript"> |
101 | 128 | jQuery(function($) { |
129 | + | |
130 | + $('#signup-form #user_login').css('width', 335 - $('#signup-domain').outerWidth()); | |
131 | + | |
102 | 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 | 133 | $(this).bind('blur', function() { |
109 | 134 | if ($(this).val() == '') { |
110 | - $(this).val(default_value); | |
111 | 135 | $(this).removeClass('filled-in'); |
112 | 136 | } |
113 | 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 | 141 | $('#user_pw').focus(function() { |
134 | 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 | 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> </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> </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 | 159 | $('#password-balloon').fadeOut('slow'); |
160 | + $('#password-confirmation-balloon').fadeOut('slow'); | |
161 | 161 | }); |
162 | 162 | $('#user_login').focus(function() { |
163 | 163 | $('#signup-balloon').fadeIn('slow'); |
164 | 164 | }); |
165 | 165 | $('#user_login').blur(function() { $('#signup-balloon').fadeOut('slow'); }); |
166 | 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 | 184 | </script> | ... | ... |
app/views/catalog/index.rhtml
1 | 1 | <% extra_content = [] %> |
2 | 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 | 22 | <% @products.each do |product| %> |
8 | 23 | <% extra_content = @plugins.dispatch(:catalog_item_extras, product).collect { |content| instance_eval(&content) } %> |
9 | 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 | 31 | <ul> |
13 | 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 | 36 | <% if product.image %> |
15 | 37 | <div class="zoomable-image"> |
16 | 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 | 4 | |
5 | 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 | 9 | <%= render :partial => 'general_fields' %> |
10 | 10 | ... | ... |
app/views/cms/_event.rhtml
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | <%# TODO add Textile help here %> |
4 | 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 | 8 | <%= render :partial => 'general_fields' %> |
9 | 9 | <%= render :partial => 'translatable' %> | ... | ... |
app/views/cms/_folder.rhtml
1 | 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 | <%= render :partial => 'general_fields' %> |
5 | 5 | |
6 | 6 | <%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 3, :cols => 64)) %> | ... | ... |
app/views/cms/_forum.rhtml
... | ... | @@ -4,7 +4,7 @@ |
4 | 4 | |
5 | 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 | 9 | <%= render :partial => 'general_fields' %> |
10 | 10 | ... | ... |
app/views/cms/_gallery.rhtml
app/views/cms/_published_article.rhtml
1 | -<%= f.text_field 'name', :size => '64' %> | |
1 | +<%= f.text_field 'name', :size => '64', :maxlength => 150 %> | |
2 | 2 | <%= render :partial => 'general_fields' %> |
3 | 3 | |
4 | 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 | 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 | 5 | <%= render :partial => 'general_fields' %> |
6 | 6 | <%= render :partial => 'translatable' %> | ... | ... |
app/views/cms/_textile_article.rhtml
... | ... | @@ -2,7 +2,7 @@ |
2 | 2 | |
3 | 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 | 7 | <%= render :partial => 'general_fields' %> |
8 | 8 | <%= render :partial => 'translatable' %> | ... | ... |
app/views/cms/_tiny_mce_article.rhtml
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | <%= render :file => 'shared/tiny_mce' %> |
4 | 4 | |
5 | 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 | 8 | <%= render :partial => 'general_fields' %> |
9 | 9 | <%= render :partial => 'translatable' %> | ... | ... |
app/views/cms/select_article_type.rhtml
... | ... | @@ -2,9 +2,9 @@ |
2 | 2 | |
3 | 3 | <ul id="article_types"> |
4 | 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 | 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 | 8 | <strong><%= type[:short_description] %></strong> |
9 | 9 | <div class='description'><%= type[:description] %></div> |
10 | 10 | </li> | ... | ... |
app/views/content_viewer/_article_toolbar.rhtml
... | ... | @@ -34,11 +34,11 @@ |
34 | 34 | <%= expirable_button @page, :locale, content, url %> |
35 | 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 | 38 | <% end %> |
39 | 39 | |
40 | 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 | 42 | <% end %> |
43 | 43 | |
44 | 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 | 9 | <div class='profile-activity-lead'> |
10 | 10 | <div class='article-name'><%= link_to(activity.params['name'], activity.params['url']) %></div> |
11 | 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 | 13 | </div> |
14 | 14 | <%= content_tag(:p, link_to(_('See complete forum'), activity.get_url), :class => 'see-forum') if activity.target.is_a?(Forum) %> |
15 | 15 | <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p> | ... | ... |
app/views/profile/communities.rhtml
app/views/profile_editor/_person_form.rhtml
... | ... | @@ -21,6 +21,8 @@ |
21 | 21 | <%= optional_field(@person, 'city', f.text_field(:city, :rel => _('City'))) %> |
22 | 22 | <%= optional_field(@person, 'zip_code', labelled_form_field(_('ZIP code'), text_field(:profile_data, :zip_code, :rel => _('ZIP code')))) %> |
23 | 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 | 27 | <% optional_field(@person, 'schooling') do %> |
26 | 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 | 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 | 6 | <div class="search-product-item-first-column"> |
7 | 7 | <%= render :partial => 'search/image', :object => product %> | ... | ... |
app/views/shared/_organization_custom_fields.rhtml
... | ... | @@ -10,6 +10,8 @@ |
10 | 10 | <%= optional_field(profile, 'economic_activity', f.text_field(:economic_activity)) %> |
11 | 11 | <%= optional_field(profile, 'management_information', f.text_area(:management_information, :rows => 5)) %> |
12 | 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 | 15 | <%= optional_field(profile, 'zip_code', labelled_form_field(_('ZIP code'), text_field(object_name, :zip_code))) %> |
14 | 16 | <%= optional_field(profile, 'city', f.text_field(:city)) %> |
15 | 17 | <%= optional_field(profile, 'state', f.text_field(:state)) %> | ... | ... |
config/initializers/plugins.rb
db/migrate/20130111232201_aggressive_indexing_strategy3.rb
0 → 100644
... | ... | @@ -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 | ... | ... |
db/schema.rb
... | ... | @@ -9,7 +9,7 @@ |
9 | 9 | # |
10 | 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 | 14 | create_table "abuse_reports", :force => true do |t| |
15 | 15 | t.integer "reporter_id" |
... | ... | @@ -88,6 +88,8 @@ ActiveRecord::Schema.define(:version => 20121008185303) do |
88 | 88 | t.integer "license_id" |
89 | 89 | end |
90 | 90 | |
91 | + add_index "article_versions", ["article_id"], :name => "index_article_versions_on_article_id" | |
92 | + | |
91 | 93 | create_table "articles", :force => true do |t| |
92 | 94 | t.string "name" |
93 | 95 | t.string "slug" |
... | ... | @@ -129,6 +131,10 @@ ActiveRecord::Schema.define(:version => 20121008185303) do |
129 | 131 | t.integer "license_id" |
130 | 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 | 138 | add_index "articles", ["translation_of_id"], :name => "index_articles_on_translation_of_id" |
133 | 139 | |
134 | 140 | create_table "articles_categories", :id => false, :force => true do |t| |
... | ... | @@ -217,6 +223,8 @@ ActiveRecord::Schema.define(:version => 20121008185303) do |
217 | 223 | t.string "referrer" |
218 | 224 | end |
219 | 225 | |
226 | + add_index "comments", ["source_id", "spam"], :name => "index_comments_on_source_id_and_spam" | |
227 | + | |
220 | 228 | create_table "contact_lists", :force => true do |t| |
221 | 229 | t.text "list" |
222 | 230 | t.string "error_fetching" |
... | ... | @@ -280,6 +288,7 @@ ActiveRecord::Schema.define(:version => 20121008185303) do |
280 | 288 | t.integer "update_errors", :default => 0 |
281 | 289 | end |
282 | 290 | |
291 | + add_index "external_feeds", ["blog_id"], :name => "index_external_feeds_on_blog_id" | |
283 | 292 | add_index "external_feeds", ["enabled"], :name => "index_external_feeds_on_enabled" |
284 | 293 | add_index "external_feeds", ["fetched_at"], :name => "index_external_feeds_on_fetched_at" |
285 | 294 | |
... | ... | @@ -295,6 +304,10 @@ ActiveRecord::Schema.define(:version => 20121008185303) do |
295 | 304 | t.string "group" |
296 | 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 | 311 | create_table "images", :force => true do |t| |
299 | 312 | t.integer "parent_id" |
300 | 313 | t.string "content_type" |
... | ... | @@ -446,6 +459,7 @@ ActiveRecord::Schema.define(:version => 20121008185303) do |
446 | 459 | end |
447 | 460 | |
448 | 461 | add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id" |
462 | + add_index "profiles", ["identifier"], :name => "index_profiles_on_identifier" | |
449 | 463 | add_index "profiles", ["region_id"], :name => "index_profiles_on_region_id" |
450 | 464 | |
451 | 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 | 37 | noosfero (0.39.0~1) UNRELEASED; urgency=low |
2 | 38 | |
3 | 39 | * Pre-release to test the antispam mechanism. |
4 | 40 | |
5 | 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 | 49 | noosfero (0.38.2) unstable; urgency=low |
8 | 50 | |
9 | 51 | * Bugfixes release | ... | ... |
debian/noosfero.install
... | ... | @@ -32,6 +32,8 @@ debian/solr.yml etc/noosfero |
32 | 32 | debian/thin.yml etc/noosfero |
33 | 33 | etc/logrotate.d/noosfero etc/logrotate.d |
34 | 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 | 38 | locale usr/share/noosfero |
37 | 39 | doc/noosfero usr/share/noosfero/doc | ... | ... |
etc/noosfero/varnish-noosfero.vcl
1 | 1 | sub vcl_recv { |
2 | 2 | if (req.request == "GET" || req.request == "HEAD") { |
3 | 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 | 6 | # strip all cookies |
8 | 7 | unset req.http.Cookie; |
9 | 8 | } | ... | ... |
features/signup.feature
... | ... | @@ -12,7 +12,7 @@ Feature: signup |
12 | 12 | | Username | josesilva | |
13 | 13 | | Password | secret | |
14 | 14 | | Password confirmation | secret | |
15 | - | Name | José da Silva | | |
15 | + | Full name | José da Silva | | |
16 | 16 | And I press "Create my account" |
17 | 17 | Then I should not be logged in |
18 | 18 | And I should receive an e-mail on josesilva@example.com | ... | ... |
gitignore.example
... | ... | @@ -19,9 +19,10 @@ public/image_uploads |
19 | 19 | public/thumbnails |
20 | 20 | public/user_themes |
21 | 21 | public/designs/themes/default |
22 | +public/designs/themes/* | |
22 | 23 | public/designs/icons/default |
23 | -public/javascripts/cache*.js | |
24 | -public/stylesheets/cache*.css | |
24 | +public/javascripts/cache* | |
25 | +public/stylesheets/cache* | |
25 | 26 | public/plugins |
26 | 27 | db/development.db |
27 | 28 | db/production.db | ... | ... |
lib/noosfero.rb
lib/noosfero/plugin.rb
... | ... | @@ -130,7 +130,7 @@ class Noosfero::Plugin |
130 | 130 | end |
131 | 131 | |
132 | 132 | # -> Adds plugin-specific content types to CMS |
133 | - # returns = { content type class } | |
133 | + # returns = [ContentClass1, ContentClass2, ...] | |
134 | 134 | def content_types |
135 | 135 | nil |
136 | 136 | end |
... | ... | @@ -161,7 +161,7 @@ class Noosfero::Plugin |
161 | 161 | |
162 | 162 | # -> Adds content to products on asset list |
163 | 163 | # returns = lambda block that creates html code |
164 | - def asset_product_extras(product, enterprise) | |
164 | + def asset_product_extras(product) | |
165 | 165 | nil |
166 | 166 | end |
167 | 167 | |
... | ... | @@ -384,7 +384,9 @@ class Noosfero::Plugin |
384 | 384 | private |
385 | 385 | |
386 | 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 | 390 | end |
389 | 391 | |
390 | 392 | end | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 < AdminController |
2 | 2 | append_view_path File.join(File.dirname(__FILE__) + '/../views') |
3 | 3 | |
4 | 4 | def index |
5 | - @settings = AntiSpamPlugin::Settings.new(environment, params[:settings]) | |
5 | + @settings = Noosfero::Plugin::Settings.new(environment, AntiSpamPlugin, params[:settings]) | |
6 | 6 | if request.post? |
7 | 7 | @settings.save! |
8 | 8 | redirect_to :action => 'index' | ... | ... |
plugins/anti_spam/lib/anti_spam_plugin.rb
... | ... | @@ -8,6 +8,10 @@ class AntiSpamPlugin < Noosfero::Plugin |
8 | 8 | _("Checks comments against a spam checking service compatible with the Akismet API") |
9 | 9 | end |
10 | 10 | |
11 | + def self.host_default_setting | |
12 | + 'api.antispam.typepad.com' | |
13 | + end | |
14 | + | |
11 | 15 | def check_comment_for_spam(comment) |
12 | 16 | if rakismet_call(comment, :spam?) |
13 | 17 | comment.spam = true |
... | ... | @@ -26,7 +30,7 @@ class AntiSpamPlugin < Noosfero::Plugin |
26 | 30 | protected |
27 | 31 | |
28 | 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 | 35 | Rakismet.host = settings.host |
32 | 36 | Rakismet.key = settings.api_key | ... | ... |
plugins/anti_spam/lib/anti_spam_plugin/settings.rb
... | ... | @@ -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 | -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 | -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 | -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 < ActiveSupport::TestCase |
7 | 7 | article = fast_create(TextileArticle, :profile_id => profile.id) |
8 | 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 | 11 | @settings.api_key = 'b8b80ddb8084062d0c9119c945ce3bc3' |
12 | 12 | @settings.save! |
13 | 13 | ... | ... |
plugins/bsc/test/functional/bsc_plugin_admin_controller_test.rb
1 | 1 | require File.dirname(__FILE__) + '/../../../../test/test_helper' |
2 | 2 | require File.dirname(__FILE__) + '/../../controllers/bsc_plugin_admin_controller' |
3 | -require File.dirname(__FILE__) + '/../../../../app/models/uploaded_file' | |
4 | 3 | |
5 | 4 | # Re-raise errors caught by the controller. |
6 | 5 | class BscPluginAdminController; def rescue_action(e) raise e end; end | ... | ... |
plugins/bsc/test/functional/bsc_plugin_myprofile_controller_test.rb
1 | 1 | require File.dirname(__FILE__) + '/../../../../test/test_helper' |
2 | 2 | require File.dirname(__FILE__) + '/../../controllers/bsc_plugin_myprofile_controller' |
3 | -require File.dirname(__FILE__) + '/../../../../app/models/uploaded_file' | |
4 | 3 | |
5 | 4 | # Re-raise errors caught by the controller. |
6 | 5 | class BscPluginMyprofileController; def rescue_action(e) raise e end; end | ... | ... |
plugins/bsc/test/unit/bsc_plugin/associate_enterprise_test.rb
plugins/bsc/test/unit/bsc_plugin/contract_test.rb
1 | 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 | 3 | class BscPlugin::ContractTest < ActiveSupport::TestCase |
6 | 4 | def setup | ... | ... |
plugins/bsc/test/unit/ext/product_test.rb
plugins/mezuro/dependencies.rb
plugins/shopping_cart/controllers/shopping_cart_plugin_controller.rb
0 → 100644
... | ... | @@ -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 < MyProfileController |
4 | 4 | append_view_path File.join(File.dirname(__FILE__) + '/../views') |
5 | 5 | |
6 | 6 | def edit |
7 | + params[:settings] = treat_cart_options(params[:settings]) | |
8 | + | |
9 | + @settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin, params[:settings]) | |
7 | 10 | if request.post? |
8 | 11 | begin |
9 | - profile.update_attributes!(params[:profile_attr]) | |
12 | + @settings.save! | |
10 | 13 | session[:notice] = _('Option updated successfully.') |
11 | 14 | rescue Exception => exception |
12 | 15 | session[:notice] = _('Option wasn\'t updated successfully.') |
... | ... | @@ -46,4 +49,25 @@ class ShoppingCartPluginMyprofileController < MyProfileController |
46 | 49 | order.save! |
47 | 50 | redirect_to :action => 'reports', :from => params[:context_from], :to => params[:context_to], :filter_status => params[:context_status] |
48 | 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 | 73 | end | ... | ... |
plugins/shopping_cart/controllers/shopping_cart_plugin_profile_controller.rb
... | ... | @@ -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 @@ |
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 'shopping_cart_plugin/ext/person' |
3 | 3 | |
4 | 4 | class ShoppingCartPlugin < Noosfero::Plugin |
5 | 5 | |
6 | - def self.plugin_name | |
6 | + class << self | |
7 | + def plugin_name | |
7 | 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 | 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 | 36 | lambda { |
17 | 37 | link_to(_('Add to basket'), "add:#{item.name}", |
18 | 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 | 42 | end |
... | ... | @@ -35,15 +55,16 @@ class ShoppingCartPlugin < Noosfero::Plugin |
35 | 55 | end |
36 | 56 | |
37 | 57 | def body_beginning |
38 | - expanded_template('cart.html.erb',{:cart => context.session[:cart]}) | |
58 | + expanded_template('cart.html.erb') | |
39 | 59 | end |
40 | 60 | |
41 | 61 | def control_panel_buttons |
62 | + settings = Noosfero::Plugin::Settings.new(context.profile, ShoppingCartPlugin) | |
42 | 63 | buttons = [] |
43 | 64 | if context.profile.enterprise? |
44 | 65 | buttons << { :title => _('Shopping basket'), :icon => 'shopping-cart-icon', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'edit'} } |
45 | 66 | end |
46 | - if context.profile.enterprise? && context.profile.shopping_cart | |
67 | + if context.profile.enterprise? && settings.enabled | |
47 | 68 | buttons << { :title => _('Purchase reports'), :icon => 'shopping-cart-purchase-report', :url => {:controller => 'shopping_cart_plugin_myprofile', :action => 'reports'} } |
48 | 69 | end |
49 | 70 | ... | ... |
plugins/shopping_cart/lib/shopping_cart_plugin/cart_helper.rb
1 | 1 | module ShoppingCartPlugin::CartHelper |
2 | 2 | |
3 | 3 | include ActionView::Helpers::NumberHelper |
4 | + include ActionView::Helpers::TagHelper | |
4 | 5 | |
5 | 6 | def sell_price(product) |
6 | 7 | return 0 if product.price.nil? |
7 | 8 | product.discount ? product.price_with_discount : product.price |
8 | 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 | 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 | 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 | 29 | environment = profile.environment |
30 | + settings = Noosfero::Plugin::Settings.new(profile, ShoppingCartPlugin) | |
20 | 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 | 33 | quantity_opts = { :class => 'cart-table-quantity' } |
27 | 34 | quantity_opts.merge!({:align => 'center'}) if by_mail |
28 | 35 | price_opts = {:class => 'cart-table-price'} |
29 | 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 | 49 | table = '<table id="cart-items-table" cellpadding="2" cellspacing="0" |
32 | 50 | border="'+(by_mail ? '1' : '0')+'" |
33 | 51 | style="'+(by_mail ? 'border-collapse:collapse' : '')+'">' + |
34 | 52 | content_tag('tr', |
35 | - content_tag('th', _('Item name')) + | |
36 | - content_tag('th', by_mail ? ' # ' : '#') + | |
37 | - content_tag('th', _('Price')) | |
53 | + content_tag('th', _('Item name')) + | |
54 | + content_tag('th', by_mail ? ' # ' : '#') + | |
55 | + content_tag('th', _('Price')) | |
38 | 56 | ) + |
39 | 57 | items.map do |id, quantity| |
40 | 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 | 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 | 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 | 75 | table + |
52 | 76 | content_tag('th', _('Total:'), :colspan => 2, :class => 'cart-table-total-label') + |
... | ... | @@ -55,6 +79,14 @@ module ShoppingCartPlugin::CartHelper |
55 | 79 | end |
56 | 80 | |
57 | 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 | 91 | end |
60 | 92 | end | ... | ... |
plugins/shopping_cart/lib/shopping_cart_plugin/mailer.rb
1 | 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 | 6 | domain = supplier.hostname || supplier.environment.default_hostname |
5 | 7 | recipients customer[:email] |
6 | 8 | from 'no-reply@' + domain |
... | ... | @@ -10,10 +12,12 @@ class ShoppingCartPlugin::Mailer < Noosfero::Plugin::MailerBase |
10 | 12 | body :customer => customer, |
11 | 13 | :supplier => supplier, |
12 | 14 | :items => items, |
13 | - :environment => supplier.environment | |
15 | + :environment => supplier.environment, | |
16 | + :helper => self, | |
17 | + :delivery_option => delivery_option | |
14 | 18 | end |
15 | 19 | |
16 | - def supplier_notification(customer, supplier, items) | |
20 | + def supplier_notification(customer, supplier, items, delivery_option) | |
17 | 21 | domain = supplier.environment.default_hostname |
18 | 22 | recipients supplier.contact_email |
19 | 23 | from 'no-reply@' + domain |
... | ... | @@ -23,6 +27,8 @@ class ShoppingCartPlugin::Mailer < Noosfero::Plugin::MailerBase |
23 | 27 | body :customer => customer, |
24 | 28 | :supplier => supplier, |
25 | 29 | :items => items, |
26 | - :environment => supplier.environment | |
30 | + :environment => supplier.environment, | |
31 | + :helper => self, | |
32 | + :delivery_option => delivery_option | |
27 | 33 | end |
28 | 34 | end | ... | ... |
plugins/shopping_cart/lib/shopping_cart_plugin/purchase_order.rb
... | ... | @@ -12,8 +12,12 @@ class ShoppingCartPlugin::PurchaseOrder < Noosfero::Plugin::ActiveRecord |
12 | 12 | settings_items :customer_email, :type => String |
13 | 13 | settings_items :customer_contact_phone, :type => String |
14 | 14 | settings_items :customer_address, :type => String |
15 | + settings_items :customer_district, :type => String | |
15 | 16 | settings_items :customer_city, :type => String |
16 | 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 | 22 | before_create do |order| |
19 | 23 | order.created_at = Time.now.utc | ... | ... |
... | ... | @@ -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 | 11 | $(".cart-buy", this.cartElem).button({ icons: { primary: 'ui-icon-cart'} }); |
12 | 12 | if (!this.empty) { |
13 | 13 | $(this.cartElem).show(); |
14 | - this.enterprise = config.enterprise; | |
15 | 14 | me = this; |
16 | 15 | $.ajax({ |
17 | - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/visibility', | |
16 | + url: '/plugin/shopping_cart/visibility', | |
18 | 17 | dataType: 'json', |
19 | 18 | success: function(data, status, ajax){ |
20 | 19 | me.visible = /^true$/i.test(data); |
... | ... | @@ -25,7 +24,7 @@ function Cart(config) { |
25 | 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 | 33 | Cart.prototype.listProducts = function() { |
35 | 34 | var me = this; |
36 | 35 | $.ajax({ |
37 | - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/list', | |
36 | + url: '/plugin/shopping_cart/list', | |
38 | 37 | dataType: 'json', |
39 | 38 | success: function(data, ststus, ajax){ |
40 | 39 | if ( !data.ok ) alert(data.error.message); |
... | ... | @@ -61,7 +60,7 @@ function Cart(config) { |
61 | 60 | '<span class="item-name">'+ item.name +'</span>' + |
62 | 61 | '<div class="item-price">' + |
63 | 62 | '<input size="1" value="'+item.quantity+'" />'+ (item.price ? '× '+ 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 | 64 | ' class="button icon-remove"><span>remove</span></a>' |
66 | 65 | ).appendTo(li); |
67 | 66 | var input = $("input", li)[0]; |
... | ... | @@ -100,7 +99,7 @@ function Cart(config) { |
100 | 99 | var me = this; |
101 | 100 | if( quantity == NaN ) return input.value = input.lastValue; |
102 | 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 | 103 | dataType: 'json', |
105 | 104 | success: function(data, status, ajax){ |
106 | 105 | if ( !data.ok ) { |
... | ... | @@ -132,8 +131,7 @@ function Cart(config) { |
132 | 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 | 135 | link.intervalId = setInterval(function() { |
138 | 136 | steps = ['w', 'n', 'e', 's']; |
139 | 137 | if( !link.step || link.step==3 ) link.step = 0; |
... | ... | @@ -144,18 +142,13 @@ function Cart(config) { |
144 | 142 | clearInterval(link.intervalId); |
145 | 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 | 149 | var me = this; |
157 | 150 | $.ajax({ |
158 | - url: '/profile/'+ enterprise +'/plugins/shopping_cart/add/'+ itemId, | |
151 | + url: '/plugin/shopping_cart/add/'+ itemId, | |
159 | 152 | dataType: 'json', |
160 | 153 | success: function(data, status, ajax){ |
161 | 154 | if ( !data.ok ) alert(data.error.message); |
... | ... | @@ -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 | 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 | 171 | if ($("li", this.itemsBox).size() < 2) return this.clean(); |
179 | 172 | var me = this; |
180 | 173 | $.ajax({ |
181 | - url: '/profile/'+ enterprise +'/plugins/shopping_cart/remove/'+ itemId, | |
174 | + url: '/plugin/shopping_cart/remove/'+ itemId, | |
182 | 175 | dataType: 'json', |
183 | 176 | success: function(data, status, ajax){ |
184 | 177 | if ( !data.ok ) alert(data.error.message); |
... | ... | @@ -200,7 +193,7 @@ function Cart(config) { |
200 | 193 | |
201 | 194 | Cart.prototype.show = function() { |
202 | 195 | $.ajax({ |
203 | - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/show', | |
196 | + url: '/plugin/shopping_cart/show', | |
204 | 197 | dataType: 'json', |
205 | 198 | cache: false, |
206 | 199 | error: function(ajax, status, errorThrown) { |
... | ... | @@ -215,7 +208,7 @@ function Cart(config) { |
215 | 208 | } |
216 | 209 | Cart.prototype.hide = function() { |
217 | 210 | $.ajax({ |
218 | - url: '/profile/'+ this.enterprise +'/plugins/shopping_cart/hide', | |
211 | + url: '/plugin/shopping_cart/hide', | |
219 | 212 | dataType: 'json', |
220 | 213 | cache: false, |
221 | 214 | error: function(ajax, status, errorThrown) { |
... | ... | @@ -252,7 +245,7 @@ function Cart(config) { |
252 | 245 | Cart.prototype.clean = function() { |
253 | 246 | var me = this; |
254 | 247 | $.ajax({ |
255 | - url: '/profile/'+ me.enterprise +'/plugins/shopping_cart/clean', | |
248 | + url: '/plugin/shopping_cart/clean', | |
256 | 249 | dataType: 'json', |
257 | 250 | success: function(data, status, ajax){ |
258 | 251 | if ( !data.ok ) alert(data.error.message); |
... | ... | @@ -261,7 +254,6 @@ function Cart(config) { |
261 | 254 | $(me.cartElem).slideUp(500, function() { |
262 | 255 | $(me.itemsBox).empty(); |
263 | 256 | me.hide(); |
264 | - me.enterprise = null; | |
265 | 257 | me.updateTotal(); |
266 | 258 | me.empty = true; |
267 | 259 | }); |
... | ... | @@ -284,7 +276,7 @@ function Cart(config) { |
284 | 276 | var me = this; |
285 | 277 | $.ajax({ |
286 | 278 | type: 'POST', |
287 | - url: '/profile/'+ me.enterprise +'/plugins/shopping_cart/send_request', | |
279 | + url: '/plugin/shopping_cart/send_request', | |
288 | 280 | data: params, |
289 | 281 | dataType: 'json', |
290 | 282 | success: function(data, status, ajax){ |
... | ... | @@ -309,7 +301,19 @@ function Cart(config) { |
309 | 301 | } |
310 | 302 | |
311 | 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 | 319 | })(jQuery); | ... | ... |
... | ... | @@ -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
plugins/shopping_cart/test/functional/shopping_cart_plugin_controller_test.rb
0 → 100644
... | ... | @@ -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 < ActionController::TestCase |
13 | 13 | attr_reader :enterprise |
14 | 14 | |
15 | 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 | 21 | end |
23 | 22 | |
24 | 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 | 29 | end |
32 | 30 | |
33 | 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 | 37 | end |
41 | 38 | |
42 | 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 | 45 | end |
50 | 46 | |
51 | 47 | should 'be able to choose the delivery price' do |
52 | 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 | 52 | end |
57 | 53 | |
58 | 54 | should 'filter the reports correctly' do |
... | ... | @@ -112,4 +108,11 @@ class ShoppingCartPluginMyprofileControllerTest < ActionController::TestCase |
112 | 108 | po.reload |
113 | 109 | assert_equal ShoppingCartPlugin::PurchaseOrder::Status::CONFIRMED, po.status |
114 | 110 | end |
111 | + | |
112 | + private | |
113 | + | |
114 | + def settings | |
115 | + @enterprise.reload | |
116 | + Noosfero::Plugin::Settings.new(@enterprise, ShoppingCartPlugin) | |
117 | + end | |
115 | 118 | end | ... | ... |
plugins/shopping_cart/test/functional/shopping_cart_plugin_profile_controller_test.rb
... | ... | @@ -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 < ActiveSupport::TestCase |
7 | 7 | @context = mock() |
8 | 8 | @profile = mock() |
9 | 9 | @profile.stubs(:identifier).returns('random-user') |
10 | - @context.stubs(:profile).returns(@profile) | |
11 | 10 | @shopping_cart.context = @context |
12 | 11 | @shopping_cart.stubs(:profile).returns(@profile) |
13 | 12 | end |
... | ... | @@ -23,7 +22,8 @@ class ShoppingCartPluginTest < ActiveSupport::TestCase |
23 | 22 | product = fast_create(Product, :available => false) |
24 | 23 | enterprise = mock() |
25 | 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 | 28 | end |
29 | 29 | end | ... | ... |
plugins/shopping_cart/views/cart.html.erb
... | ... | @@ -7,7 +7,7 @@ |
7 | 7 | <a href="cart:clean" onclick="Cart.clean(this); return false" class="cart-clean"><%=_('Clean basket')%></a> |
8 | 8 | <ul class="cart-items"></ul> |
9 | 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 | 11 | </div> |
12 | 12 | <a href="#" onclick="Cart.toggle(this); return false" class="cart-toggle"> |
13 | 13 | <span class="str-show"><%=_('Show basket')%></span> |
... | ... | @@ -15,9 +15,3 @@ |
15 | 15 | </a> |
16 | 16 | </div> |
17 | 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 @@ |
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 | 17 | <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li> |
18 | 18 | <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li> |
19 | 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 | 25 | <li><b><%= _('Address') %>:</b> |
22 | 26 | <% end %> |
23 | 27 | <% if !@customer[:address].blank? %> |
24 | 28 | <%= @customer[:address] %><br \> |
25 | 29 | <% end %> |
30 | + <% if !@customer[:district].blank? %> | |
31 | + <%= @customer[:district] %><br \> | |
32 | + <% end %> | |
26 | 33 | <% if !@customer[:city].blank? %> |
27 | 34 | <%= @customer[:city] %><br \> |
28 | 35 | <% end %> |
29 | 36 | <% if !@customer[:zip_code].blank? %> |
30 | 37 | <%= @customer[:zip_code] %> |
31 | 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 | 43 | </li> |
34 | 44 | <% end %> |
35 | 45 | </ul> |
36 | 46 | |
37 | 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 | 49 | <p> |
41 | 50 | --<br/> |
42 | 51 | <%=_('Thanks for buying with us!')%><br/> | ... | ... |
plugins/shopping_cart/views/shopping_cart_plugin/mailer/supplier_notification.html.erb
... | ... | @@ -15,26 +15,35 @@ |
15 | 15 | <li><b><%= _('Full name') %>: </b><%= @customer[:name] %></li> |
16 | 16 | <li><b><%= _('Email') %>: </b><%= @customer[:email] %></li> |
17 | 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 | 23 | <li><b><%= _('Address') %>:</b> |
20 | 24 | <% end %> |
21 | 25 | <% if !@customer[:address].blank? %> |
22 | 26 | <%= @customer[:address] %><br \> |
23 | 27 | <% end %> |
28 | + <% if !@customer[:district].blank? %> | |
29 | + <%= @customer[:district] %><br \> | |
30 | + <% end %> | |
24 | 31 | <% if !@customer[:city].blank? %> |
25 | 32 | <%= @customer[:city] %><br \> |
26 | 33 | <% end %> |
27 | 34 | <% if !@customer[:zip_code].blank? %> |
28 | 35 | <%= @customer[:zip_code] %> |
29 | 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 | 41 | </li> |
32 | 42 | <% end %> |
33 | 43 | </ul> |
34 | 44 | |
35 | 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 | 47 | <p> |
39 | 48 | --<br/> |
40 | 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 @@ |
1 | +<%= _("Request sent successfully check your email.")%> | ... | ... |
plugins/shopping_cart/views/shopping_cart_plugin_myprofile/_orders_list.html.erb
... | ... | @@ -32,7 +32,9 @@ |
32 | 32 | <td class="order-info" colspan="5"> |
33 | 33 | <div style="display:none"> |
34 | 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 | 38 | <%= content_tag('li', content_tag('strong', name+': ') + order.send('customer_'+attribute)) if !order.send('customer_'+attribute).blank? %> |
37 | 39 | <% end %> |
38 | 40 | </ul> | ... | ... |