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