Commit c54913e745b269468394ba18e92294ba3acb8ade
Exists in
theme-brasil-digital-from-staging
and in
9 other branches
Merge branch 'production' of gitlab.com:participa/noosfero into production
Showing
195 changed files
with
79917 additions
and
75163 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 195 files displayed.
@@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
1 | +before_script: | ||
2 | + - mkdir -p tmp/pids log | ||
3 | + - bundle check || bundle install | ||
4 | + - script/noosfero-plugins disableall | ||
5 | + - bundle exec rake makemo &>/dev/null | ||
6 | +# database | ||
7 | + - cp config/database.yml.gitlab-ci config/database.yml | ||
8 | + - createdb gitlab_ci_test || true | ||
9 | + - bundle exec rake db:schema:load &>/dev/null | ||
10 | + - bundle exec rake db:migrate &>/dev/null | ||
11 | + | ||
12 | +units: | ||
13 | + script: bundle exec rake test:units | ||
14 | +functionals: | ||
15 | + script: bundle exec rake test:functionals | ||
16 | +integration: | ||
17 | + script: bundle exec rake test:integration | ||
18 | +cucumber: | ||
19 | + script: bundle exec rake cucumber | ||
20 | +selenium: | ||
21 | + script: bundle exec rake selenium | ||
22 | +plugins: | ||
23 | + script: bundle exec rake test:noosfero_plugins | ||
24 | + |
.gitmodules
@@ -28,3 +28,9 @@ | @@ -28,3 +28,9 @@ | ||
28 | [submodule "public/proposal-app"] | 28 | [submodule "public/proposal-app"] |
29 | path = public/proposal-app | 29 | path = public/proposal-app |
30 | url = https://gitlab.com/participa/proposal-app.git | 30 | url = https://gitlab.com/participa/proposal-app.git |
31 | +[submodule "plugins/gravatar-provider"] | ||
32 | + path = plugins/gravatar-provider | ||
33 | + url = https://gitlab.com/noosfero-plugins/gravatar-provider.git | ||
34 | +[submodule "plugins/juventude"] | ||
35 | + path = plugins/juventude | ||
36 | + url = https://gitlab.com/noosfero-plugins/juventude.git |
@@ -0,0 +1,50 @@ | @@ -0,0 +1,50 @@ | ||
1 | +language: ruby | ||
2 | +rvm: | ||
3 | +# for 2.2 support we need to upgrade the pg gem | ||
4 | + - 2.1.6 | ||
5 | +cache: bundler | ||
6 | + | ||
7 | +sudo: false | ||
8 | +addons: | ||
9 | + apt: | ||
10 | + packages: | ||
11 | + - po4a | ||
12 | + - iso-codes | ||
13 | + - tango-icon-theme | ||
14 | + - pidgin-data | ||
15 | + # for gem extensions | ||
16 | + - libmagickwand-dev | ||
17 | + - libpq-dev | ||
18 | + - libreadline-dev | ||
19 | + - libsqlite3-dev | ||
20 | + - libxslt1-dev | ||
21 | + | ||
22 | +before_install: | ||
23 | +# FIXME: workaround while https://github.com/travis-ci/travis-ci/issues/4210 is open | ||
24 | + - rm config/initializers/default_icon_theme.rb | ||
25 | +# selenium support | ||
26 | + - export DISPLAY=:99.0 | ||
27 | + - sh -e /etc/init.d/xvfb start | ||
28 | + | ||
29 | +before_script: | ||
30 | + - mkdir -p tmp/pids log | ||
31 | + - bundle check || bundle install | ||
32 | + - script/noosfero-plugins disableall | ||
33 | + - bundle exec rake makemo &>/dev/null | ||
34 | +# database | ||
35 | + - cp config/database.yml.travis config/database.yml | ||
36 | + - psql -c 'create database myapp_test;' -U postgres | ||
37 | + - bundle exec rake db:schema:load &>/dev/null | ||
38 | + - bundle exec rake db:migrate &>/dev/null | ||
39 | + | ||
40 | +env: | ||
41 | + - TASK=test:units | ||
42 | + - TASK=test:functionals | ||
43 | + - TASK=test:integration | ||
44 | + - TASK=cucumber | ||
45 | + - TASK=selenium | ||
46 | + - TASK=test:noosfero_plugins | ||
47 | + | ||
48 | +script: | ||
49 | + - bundle exec rake $TASK | ||
50 | + |
AUTHORS.md
@@ -6,131 +6,136 @@ noosfero, that's not a problem). | @@ -6,131 +6,136 @@ noosfero, that's not a problem). | ||
6 | Developers | 6 | Developers |
7 | ========== | 7 | ========== |
8 | 8 | ||
9 | -Ábner Silva de Oliveira <abner.oliveira@serpro.gov.br> | ||
10 | Alan Freihof Tygel <alantygel@gmail.com> | 9 | Alan Freihof Tygel <alantygel@gmail.com> |
11 | -alcampelo <alcampelo@alcampelo.(none)> | ||
12 | -Alessandro Palmeira <alessandro.palmeira@gmail.com> | ||
13 | Alessandro Palmeira + Caio C. Salgado <alessandro.palmeira@gmail.com> | 10 | Alessandro Palmeira + Caio C. Salgado <alessandro.palmeira@gmail.com> |
11 | +Alessandro Palmeira + Caio Salgado + Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com> | ||
14 | Alessandro Palmeira + Caio Salgado <alessandro.palmeira@gmail.com> | 12 | Alessandro Palmeira + Caio Salgado <alessandro.palmeira@gmail.com> |
15 | Alessandro Palmeira + Caio Salgado <caio.csalgado@gmail.com> | 13 | Alessandro Palmeira + Caio Salgado <caio.csalgado@gmail.com> |
16 | -Alessandro Palmeira + Caio Salgado + Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com> | ||
17 | Alessandro Palmeira + Carlos Morais <alessandro.palmeira@gmail.com> | 14 | Alessandro Palmeira + Carlos Morais <alessandro.palmeira@gmail.com> |
18 | -Alessandro Palmeira + Daniel Alves <alessandro.palmeira@gmail.com> | ||
19 | -Alessandro Palmeira + Daniel Alves + Diego Araújo <diegoamc90@gmail.com> | ||
20 | Alessandro Palmeira + Daniel Alves + Diego Araújo + Guilherme Rojas <danpaulalves@gmail.com> | 15 | Alessandro Palmeira + Daniel Alves + Diego Araújo + Guilherme Rojas <danpaulalves@gmail.com> |
21 | -Alessandro Palmeira + Diego Araujo <alessandro.palmeira@gmail.com> | ||
22 | -Alessandro Palmeira + Diego Araújo <alessandro.palmeira@gmail.com> | 16 | +Alessandro Palmeira + Daniel Alves + Diego Araújo <diegoamc90@gmail.com> |
17 | +Alessandro Palmeira + Daniel Alves <alessandro.palmeira@gmail.com> | ||
23 | Alessandro Palmeira + Diego Araujo + Daniela Feitosa <alessandro.palmeira@gmail.com> | 18 | Alessandro Palmeira + Diego Araujo + Daniela Feitosa <alessandro.palmeira@gmail.com> |
24 | -Alessandro Palmeira + Diego Araujo <diegoamc90@gmail.com> | ||
25 | -Alessandro Palmeira + Diego Araújo <diegoamc90@gmail.com> | ||
26 | Alessandro Palmeira + Diego Araujo + Eduardo Morais <alessandro.palmeira@gmail.com> | 19 | Alessandro Palmeira + Diego Araujo + Eduardo Morais <alessandro.palmeira@gmail.com> |
20 | +Alessandro Palmeira + Diego Araujo + João M. M. da Silva + Paulo Meirelles <alessandro.palmeira@gmail.com> | ||
21 | +Alessandro Palmeira + Diego Araujo + Rafael Manzo <alessandro.palmeira@gmail.com> | ||
22 | +Alessandro Palmeira + Diego Araujo <alessandro.palmeira@gmail.com> | ||
23 | +Alessandro Palmeira + Diego Araujo <diegoamc90@gmail.com> | ||
27 | Alessandro Palmeira + Diego Araújo + João M. M. da Silva <alessandro.palmeira@gmail.com> | 24 | Alessandro Palmeira + Diego Araújo + João M. M. da Silva <alessandro.palmeira@gmail.com> |
28 | Alessandro Palmeira + Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com> | 25 | Alessandro Palmeira + Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com> |
29 | -Alessandro Palmeira + Diego Araujo + João M. M. da Silva + Paulo Meirelles <alessandro.palmeira@gmail.com> | ||
30 | -Alessandro Palmeira + Diego Araújo + Pedro Leal <diegoamc90@gmail.com> | ||
31 | Alessandro Palmeira + Diego Araújo + Pedro Leal + João M. M. da Silva <diegoamc90@gmail.com> | 26 | Alessandro Palmeira + Diego Araújo + Pedro Leal + João M. M. da Silva <diegoamc90@gmail.com> |
32 | -Alessandro Palmeira + Diego Araujo + Rafael Manzo <alessandro.palmeira@gmail.com> | 27 | +Alessandro Palmeira + Diego Araújo + Pedro Leal <diegoamc90@gmail.com> |
28 | +Alessandro Palmeira + Diego Araújo <alessandro.palmeira@gmail.com> | ||
29 | +Alessandro Palmeira + Diego Araújo <diegoamc90@gmail.com> | ||
33 | Alessandro Palmeira + Eduardo Morais <alessandro.palmeira@gmail.com> | 30 | Alessandro Palmeira + Eduardo Morais <alessandro.palmeira@gmail.com> |
34 | Alessandro Palmeira + Guilherme Rojas <alessandro.palmeira@gmail.com> | 31 | Alessandro Palmeira + Guilherme Rojas <alessandro.palmeira@gmail.com> |
35 | Alessandro Palmeira + Jefferson Fernandes <alessandro.palmeira@gmail.com> | 32 | Alessandro Palmeira + Jefferson Fernandes <alessandro.palmeira@gmail.com> |
36 | -Alessandro Palmeira + João M. M. da Silva <alessandro.palmeira@gmail.com> | ||
37 | Alessandro Palmeira + Joao M. M. da Silva + Diego Araujo <alessandro.palmeira@gmail.com> | 33 | Alessandro Palmeira + Joao M. M. da Silva + Diego Araujo <alessandro.palmeira@gmail.com> |
38 | -Alessandro Palmeira + João M. M. da Silva + Renan Teruo <alessandro.palmeira@gmail.com> | ||
39 | Alessandro Palmeira + João M. M. Silva <alessandro.palmeira@gmail.com> | 34 | Alessandro Palmeira + João M. M. Silva <alessandro.palmeira@gmail.com> |
40 | -Alessandro Palmeira + Paulo Meirelles <alessandro.palmeira@gmail.com> | 35 | +Alessandro Palmeira + João M. M. da Silva + Renan Teruo <alessandro.palmeira@gmail.com> |
36 | +Alessandro Palmeira + João M. M. da Silva <alessandro.palmeira@gmail.com> | ||
41 | Alessandro Palmeira + Paulo Meirelles + João M. M. da Silva <alessandro.palmeira@gmail.com> | 37 | Alessandro Palmeira + Paulo Meirelles + João M. M. da Silva <alessandro.palmeira@gmail.com> |
38 | +Alessandro Palmeira + Paulo Meirelles <alessandro.palmeira@gmail.com> | ||
42 | Alessandro Palmeira + Rafael Manzo <alessandro.palmeira@gmail.com> | 39 | Alessandro Palmeira + Rafael Manzo <alessandro.palmeira@gmail.com> |
43 | -analosnak <analosnak@gmail.com> | 40 | +Alessandro Palmeira <alessandro.palmeira@gmail.com> |
44 | Ana Losnak <analosnak@gmail.com> | 41 | Ana Losnak <analosnak@gmail.com> |
45 | Andre Bernardes <andrebsguedes@gmail.com> | 42 | Andre Bernardes <andrebsguedes@gmail.com> |
43 | +André Bernardes <andrebsguedes@gmail.com> | ||
44 | +André Guedes <andrebsguedes@fedora.local> | ||
45 | +André Guedes <andrebsguedes@gmail.com> | ||
46 | Antonio Terceiro + Carlos Morais <terceiro@colivre.coop.br> | 46 | Antonio Terceiro + Carlos Morais <terceiro@colivre.coop.br> |
47 | Antonio Terceiro + Paulo Meirelles <terceiro@colivre.coop.br> | 47 | Antonio Terceiro + Paulo Meirelles <terceiro@colivre.coop.br> |
48 | Antonio Terceiro <terceiro@colivre.coop.br> | 48 | Antonio Terceiro <terceiro@colivre.coop.br> |
49 | Arthur Del Esposte <arthurmde@gmail.com> | 49 | Arthur Del Esposte <arthurmde@gmail.com> |
50 | Arthur Del Esposte <arthurmde@yahoo.com.br> | 50 | Arthur Del Esposte <arthurmde@yahoo.com.br> |
51 | +Athos Ribeiro <athoscribeiro@gmail.com> | ||
51 | Aurelio A. Heckert <aurelio@colivre.coop.br> | 52 | Aurelio A. Heckert <aurelio@colivre.coop.br> |
53 | +Braulio Bhavamitra <braulio@eita.org.br> | ||
52 | Braulio Bhavamitra <brauliobo@gmail.com> | 54 | Braulio Bhavamitra <brauliobo@gmail.com> |
53 | Bráulio Bhavamitra <brauliobo@gmail.com> | 55 | Bráulio Bhavamitra <brauliobo@gmail.com> |
54 | -Braulio Bhavamitra <braulio@eita.org.br> | ||
55 | -Caio <caio.csalgado@gmail.com> | ||
56 | Caio + Diego + Pedro + João <caio.csalgado@gmail.com> | 56 | Caio + Diego + Pedro + João <caio.csalgado@gmail.com> |
57 | +Caio <caio.csalgado@gmail.com> | ||
57 | Caio Formiga <caio.formiga@gmail.com> | 58 | Caio Formiga <caio.formiga@gmail.com> |
58 | -Caio, Pedro <caio.csalgado@gmail.com> | 59 | +Caio SBA <caio@colivre.coop.br> |
59 | Caio Salgado + Alessandro Palmeira <caio.csalgado@gmail.com> | 60 | Caio Salgado + Alessandro Palmeira <caio.csalgado@gmail.com> |
60 | -Caio Salgado <caio.csalgado@gmail.com> | ||
61 | Caio Salgado + Carlos Morais + Diego Araújo + Pedro Leal <diegoamc90@gmail.com> | 61 | Caio Salgado + Carlos Morais + Diego Araújo + Pedro Leal <diegoamc90@gmail.com> |
62 | Caio Salgado + Diego Araujo <caio.csalgado@gmail.com> | 62 | Caio Salgado + Diego Araujo <caio.csalgado@gmail.com> |
63 | -Caio Salgado + Diego Araújo <caio.csalgado@gmail.com> | ||
64 | -Caio Salgado + Diego Araújo <diegoamc90@gmail.com> | ||
65 | Caio Salgado + Diego Araújo + Jefferson Fernandes <caio.csalgado@gmail.com> | 63 | Caio Salgado + Diego Araújo + Jefferson Fernandes <caio.csalgado@gmail.com> |
66 | Caio Salgado + Diego Araújo + João M. M. da Silva <caio.csalgado@gmail.com> | 64 | Caio Salgado + Diego Araújo + João M. M. da Silva <caio.csalgado@gmail.com> |
67 | Caio Salgado + Diego Araújo + Pedro Leal <caio.csalgado@gmail.com> | 65 | Caio Salgado + Diego Araújo + Pedro Leal <caio.csalgado@gmail.com> |
68 | Caio Salgado + Diego Araújo + Pedro Leal <diegoamc90@gmail.com> | 66 | Caio Salgado + Diego Araújo + Pedro Leal <diegoamc90@gmail.com> |
69 | Caio Salgado + Diego Araújo + Rafael Manzo <diegoamc90@gmail.com> | 67 | Caio Salgado + Diego Araújo + Rafael Manzo <diegoamc90@gmail.com> |
68 | +Caio Salgado + Diego Araújo <caio.csalgado@gmail.com> | ||
69 | +Caio Salgado + Diego Araújo <diegoamc90@gmail.com> | ||
70 | Caio Salgado + Jefferson Fernandes <caio.csalgado@gmail.com> | 70 | Caio Salgado + Jefferson Fernandes <caio.csalgado@gmail.com> |
71 | Caio Salgado + Jefferson Fernandes <jeffs.fernandes@gmail.com> | 71 | Caio Salgado + Jefferson Fernandes <jeffs.fernandes@gmail.com> |
72 | Caio Salgado + Rafael Manzo <caio.csalgado@gmail.com> | 72 | Caio Salgado + Rafael Manzo <caio.csalgado@gmail.com> |
73 | +Caio Salgado + Renan Teruo + Jefferson Fernandes <jeffs.fernandes@gmail.com> | ||
73 | Caio Salgado + Renan Teruo <caio.csalgado@gmail.com> | 74 | Caio Salgado + Renan Teruo <caio.csalgado@gmail.com> |
74 | Caio Salgado + Renan Teruo <caio.salgado@gmail.com> | 75 | Caio Salgado + Renan Teruo <caio.salgado@gmail.com> |
75 | -Caio Salgado + Renan Teruo + Jefferson Fernandes <jeffs.fernandes@gmail.com> | ||
76 | Caio Salgado + Renan Teruo <renanteruoc@gmail.com> | 76 | Caio Salgado + Renan Teruo <renanteruoc@gmail.com> |
77 | -Caio SBA <caio@colivre.coop.br> | 77 | +Caio Salgado <caio.csalgado@gmail.com> |
78 | Caio Tiago Oliveira <caiotiago@colivre.coop.br> | 78 | Caio Tiago Oliveira <caiotiago@colivre.coop.br> |
79 | +Caio, Pedro <caio.csalgado@gmail.com> | ||
79 | Carlos Andre de Souza <carlos.andre.souza@msn.com> | 80 | Carlos Andre de Souza <carlos.andre.souza@msn.com> |
80 | -Carlos Morais <carlos88morais@gmail.com> | ||
81 | Carlos Morais + Diego Araújo <diegoamc90@gmail.com> | 81 | Carlos Morais + Diego Araújo <diegoamc90@gmail.com> |
82 | Carlos Morais + Eduardo Morais <carlos88morais@gmail.com> | 82 | Carlos Morais + Eduardo Morais <carlos88morais@gmail.com> |
83 | Carlos Morais + Paulo Meirelles <carlos88morais@gmail.com> | 83 | Carlos Morais + Paulo Meirelles <carlos88morais@gmail.com> |
84 | Carlos Morais + Pedro Leal <carlos88morais@gmail.com> | 84 | Carlos Morais + Pedro Leal <carlos88morais@gmail.com> |
85 | +Carlos Morais <carlos88morais@gmail.com> | ||
86 | +Christophe DANIEL <papaeng@gmail.com> | ||
85 | Daniel Alves + Diego Araújo <danpaulalves@gmail.com> | 87 | Daniel Alves + Diego Araújo <danpaulalves@gmail.com> |
86 | Daniel Alves + Diego Araújo <diegoamc90@gmail.com> | 88 | Daniel Alves + Diego Araújo <diegoamc90@gmail.com> |
87 | Daniel Alves + Diego Araújo + Guilherme Rojas <danpaulalves@gmail.com> | 89 | Daniel Alves + Diego Araújo + Guilherme Rojas <danpaulalves@gmail.com> |
88 | Daniel Alves + Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com> | 90 | Daniel Alves + Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com> |
89 | Daniel Alves + Diego Araújo + Guilherme Rojas <guilhermehrojas@gmail.com> | 91 | Daniel Alves + Diego Araújo + Guilherme Rojas <guilhermehrojas@gmail.com> |
92 | +Daniel Alves + Diego Araújo <danpaulalves@gmail.com> | ||
93 | +Daniel Alves + Diego Araújo <diegoamc90@gmail.com> | ||
90 | Daniel Alves + Guilherme Rojas <danpaulalves@gmail.com> | 94 | Daniel Alves + Guilherme Rojas <danpaulalves@gmail.com> |
91 | Daniel Alves + Rafael Manzo <rr.manzo@gmail.com> | 95 | Daniel Alves + Rafael Manzo <rr.manzo@gmail.com> |
92 | -Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> | ||
93 | Daniel Bucher <daniel.bucher88@gmail.com> | 96 | Daniel Bucher <daniel.bucher88@gmail.com> |
94 | Daniel Cunha <daniel@colivre.coop.br> | 97 | Daniel Cunha <daniel@colivre.coop.br> |
95 | -daniel <dtygel@eita.org.br> | 98 | +Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> |
96 | David Carlos <ddavidcarlos1392@gmail.com> | 99 | David Carlos <ddavidcarlos1392@gmail.com> |
97 | -diegoamc <diegoamc90@gmail.com> | ||
98 | -Diego Araújo + Alessandro Palmeira <diegoamc90@gmail.com> | 100 | +Diego + Jefferson <diegoamc90@gmail.com> |
101 | +Diego + Renan <renanteruoc@gmail.com> | ||
102 | +Diego Araujo + Caio Salgado <diegoamc90@gmail.com> | ||
103 | +Diego Araujo + Jefferson Fernandes <jeffs.fernandes@gmail.com> | ||
104 | +Diego Araujo + Rafael Manzo <diegoamc90@gmail.com> | ||
105 | +Diego Araujo + Rodrigo Souto + Rafael Manzo <rr.manzo@gmail.com> | ||
99 | Diego Araújo + Alessandro Palmeira + João M. M. da Silva <diegoamc90@gmail.com> | 106 | Diego Araújo + Alessandro Palmeira + João M. M. da Silva <diegoamc90@gmail.com> |
100 | Diego Araújo + Alessandro Palmeira + Rafael Manzo <rr.manzo@gmail.com> | 107 | Diego Araújo + Alessandro Palmeira + Rafael Manzo <rr.manzo@gmail.com> |
101 | -Diego Araujo + Caio Salgado <diegoamc90@gmail.com> | 108 | +Diego Araújo + Alessandro Palmeira <diegoamc90@gmail.com> |
102 | Diego Araújo + Daniel Alves + Rafael Manzo <rr.manzo@gmail.com> | 109 | Diego Araújo + Daniel Alves + Rafael Manzo <rr.manzo@gmail.com> |
103 | -Diego Araújo <diegoamc90@gmail.com> | ||
104 | Diego Araújo + Eduardo Morais + Paulo Meirelles <diegoamc90@gmail.com> | 110 | Diego Araújo + Eduardo Morais + Paulo Meirelles <diegoamc90@gmail.com> |
105 | Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com> | 111 | Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com> |
106 | Diego Araújo + Jefferson Fernandes <diegoamc90@gmail.com> | 112 | Diego Araújo + Jefferson Fernandes <diegoamc90@gmail.com> |
107 | -Diego Araujo + Jefferson Fernandes <jeffs.fernandes@gmail.com> | ||
108 | -Diego Araújo + João Machini <diegoamc90@gmail.com> | ||
109 | -Diego Araújo + João Machini <digoamc90@gmail.com> | ||
110 | Diego Araújo + João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br> | 113 | Diego Araújo + João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br> |
111 | -Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com> | ||
112 | Diego Araújo + João M. M. da Silva + João Machini <diegoamc90@gmail.com> | 114 | Diego Araújo + João M. M. da Silva + João Machini <diegoamc90@gmail.com> |
113 | Diego Araújo + João M. M. da Silva + Pedro Leal <diegoamc90@gmail.com> | 115 | Diego Araújo + João M. M. da Silva + Pedro Leal <diegoamc90@gmail.com> |
116 | +Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com> | ||
117 | +Diego Araújo + João Machini <diegoamc90@gmail.com> | ||
118 | +Diego Araújo + João Machini <digoamc90@gmail.com> | ||
114 | Diego Araújo + Paulo Meirelles <diegoamc90@gmail.com> | 119 | Diego Araújo + Paulo Meirelles <diegoamc90@gmail.com> |
115 | Diego Araújo + Pedro Leal <diegoamc90@gmail.com> | 120 | Diego Araújo + Pedro Leal <diegoamc90@gmail.com> |
116 | -Diego Araujo + Rafael Manzo <diegoamc90@gmail.com> | ||
117 | Diego Araújo + Rafael Manzo <diegoamc90@gmail.com> | 121 | Diego Araújo + Rafael Manzo <diegoamc90@gmail.com> |
118 | Diego Araújo + Renan Teruo + Alessandro Palmeira <diegoamc90@gmail.com> | 122 | Diego Araújo + Renan Teruo + Alessandro Palmeira <diegoamc90@gmail.com> |
119 | Diego Araújo + Renan Teruo <diegoamc90@gmail.com> | 123 | Diego Araújo + Renan Teruo <diegoamc90@gmail.com> |
120 | -Diego Araujo + Rodrigo Souto + Rafael Manzo <rr.manzo@gmail.com> | ||
121 | -Diego + Jefferson <diegoamc90@gmail.com> | 124 | +Diego Araújo <diegoamc90@gmail.com> |
122 | Diego Martinez <diegoamc90@gmail.com> | 125 | Diego Martinez <diegoamc90@gmail.com> |
123 | -Diego + Renan <renanteruoc@gmail.com> | ||
124 | -dtygel <dtygel@gmail.com> | ||
125 | DylanGuedes <djmgguedes@gmail.com> | 126 | DylanGuedes <djmgguedes@gmail.com> |
126 | Eduardo Passos <eduardo@risa.localdomain.localhost> | 127 | Eduardo Passos <eduardo@risa.localdomain.localhost> |
127 | Eduardo Passos <eduardosteps@gmail.com> | 128 | Eduardo Passos <eduardosteps@gmail.com> |
128 | Eduardo Tourinho Edington <eduardo.edington@serpro.gov.br> | 129 | Eduardo Tourinho Edington <eduardo.edington@serpro.gov.br> |
130 | +Eduardo Vital <vitaldu@gmail.com> | ||
129 | Evandro Jr <evandrojr@gmail.com> | 131 | Evandro Jr <evandrojr@gmail.com> |
130 | Evandro Junior <evandrojr@gmail.com> | 132 | Evandro Junior <evandrojr@gmail.com> |
133 | +Evandro Magalhaes Leite Junior <evandro.leite@serpro.gov.br> | ||
131 | Fabio Teixeira <fabio1079@gmail.com> | 134 | Fabio Teixeira <fabio1079@gmail.com> |
132 | FAMMA TV NOTICIAS MEDIOS DE CO <revistafammatvmusic.oficial@gmail.com> | 135 | FAMMA TV NOTICIAS MEDIOS DE CO <revistafammatvmusic.oficial@gmail.com> |
136 | +Fabio Teixeira <fabio1079@gmail.com> | ||
133 | Fernanda Lopes <nanda.listas+psl@gmail.com> | 137 | Fernanda Lopes <nanda.listas+psl@gmail.com> |
138 | +Filipe Ribeiro <firibeiro77@live.com> | ||
134 | Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br> | 139 | Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br> |
135 | Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)> | 140 | Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)> |
136 | Francisco Marcelo de Araújo Lima Júnior <francisco.lima-junior@serpro.gov.br> | 141 | Francisco Marcelo de Araújo Lima Júnior <francisco.lima-junior@serpro.gov.br> |
@@ -144,25 +149,29 @@ Hugo Melo <hugo@riseup.net> | @@ -144,25 +149,29 @@ Hugo Melo <hugo@riseup.net> | ||
144 | Isaac Canan <isaac@intelletto.com.br> | 149 | Isaac Canan <isaac@intelletto.com.br> |
145 | Italo Valcy <italo@dcc.ufba.br> | 150 | Italo Valcy <italo@dcc.ufba.br> |
146 | Jefferson Fernandes + Diego Araujo + Rafael Manzo <jeffs.fernandes@gmail.com> | 151 | Jefferson Fernandes + Diego Araujo + Rafael Manzo <jeffs.fernandes@gmail.com> |
147 | -Jefferson Fernandes + Joao M. M. da Silva <jeffs.fernandes@gmail.com> | ||
148 | Jefferson Fernandes + Joao M. M. Silva <jeffs.fernandes@gmail.com> | 152 | Jefferson Fernandes + Joao M. M. Silva <jeffs.fernandes@gmail.com> |
149 | -Jérôme Jutteau <j.jutteau@gmail.com> | ||
150 | -João da Silva + Eduardo Morais + Rafael Manzo <rr.manzo@gmail.com> | ||
151 | -João da Silva <jaodsilv@linux.ime.usp.br> | ||
152 | -João Marco Maciel da Silva + Rafael Manzo + Renan Teruo <jaodsilv@linux.ime.usp.br> | 153 | +Jefferson Fernandes + Joao M. M. da Silva <jeffs.fernandes@gmail.com> |
154 | +Joao M. M. Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br> | ||
155 | +Joao M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br> | ||
156 | +Joao M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br> | ||
157 | +Joenio Costa <joenio@colivre.coop.br> | ||
158 | +Jose Pedro <1jpsneto@gmail.com> | ||
159 | +Josef Spillner <josef.spillner@tu-dresden.de> | ||
160 | +João M. M. Silva + Caio Salgado <jaodsilv@linux.ime.usp.br> | ||
161 | +João M. M. Silva + Diego Araújo <jaodsilv@linux.ime.usp.br> | ||
162 | +João M. M. Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br> | ||
163 | +João M. M. Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br> | ||
164 | +João M. M. Silva + Renan Teruo <jaodsilv@linux.ime.usp.br> | ||
153 | João M. M. da Silva + Alessandro Palmeira + Diego Araújo + Caio Salgado <jaodsilv@linux.ime.usp.br> | 165 | João M. M. da Silva + Alessandro Palmeira + Diego Araújo + Caio Salgado <jaodsilv@linux.ime.usp.br> |
154 | João M. M. da Silva + Alessandro Palmeira + Diego Araújo <jaodsilv@linux.ime.usp.br> | 166 | João M. M. da Silva + Alessandro Palmeira + Diego Araújo <jaodsilv@linux.ime.usp.br> |
155 | -Joao M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br> | ||
156 | -João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br> | ||
157 | João M. M. da Silva + Alessandro Palmeira + João Machini <jaodsilv@linux.ime.usp.br> | 167 | João M. M. da Silva + Alessandro Palmeira + João Machini <jaodsilv@linux.ime.usp.br> |
168 | +João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br> | ||
158 | João M. M. da Silva + Caio Salgado + Alessandro Palmeira <jaodsilv@linux.ime.usp.br> | 169 | João M. M. da Silva + Caio Salgado + Alessandro Palmeira <jaodsilv@linux.ime.usp.br> |
159 | João M. M. da Silva + Caio Salgado <jaodsilv@linux.ime.usp.br> | 170 | João M. M. da Silva + Caio Salgado <jaodsilv@linux.ime.usp.br> |
160 | João M. M. da Silva + Carlos Morais <jaodsilv@linux.ime.usp.br> | 171 | João M. M. da Silva + Carlos Morais <jaodsilv@linux.ime.usp.br> |
172 | +João M. M. da Silva + Diego Araújo + Pedro Leal <jaodsilv@linux.ime.usp.br> | ||
161 | João M. M. da Silva + Diego Araújo <diegoamc90@gmail.com> | 173 | João M. M. da Silva + Diego Araújo <diegoamc90@gmail.com> |
162 | João M. M. da Silva + Diego Araújo <jaodsilv@linux.ime.usp.br> | 174 | João M. M. da Silva + Diego Araújo <jaodsilv@linux.ime.usp.br> |
163 | -João M. M. da Silva + Diego Araújo + Pedro Leal <jaodsilv@linux.ime.usp.br> | ||
164 | -João M. M. da Silva <jaodsilv@linux.ime.usp.br> | ||
165 | -Joao M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br> | ||
166 | João M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br> | 175 | João M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br> |
167 | João M. M. da Silva + João M. Miranda <jaodsilv@linux.ime.usp.br> | 176 | João M. M. da Silva + João M. Miranda <jaodsilv@linux.ime.usp.br> |
168 | João M. M. da Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br> | 177 | João M. M. da Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br> |
@@ -170,46 +179,44 @@ João M. M. da Silva + Pedro Leal <jaodsilv@linux.ime.usp.br> | @@ -170,46 +179,44 @@ João M. M. da Silva + Pedro Leal <jaodsilv@linux.ime.usp.br> | ||
170 | João M. M. da Silva + Rafael Manzo + Diego Araújo <jaodsilv@linux.ime.usp.br> | 179 | João M. M. da Silva + Rafael Manzo + Diego Araújo <jaodsilv@linux.ime.usp.br> |
171 | João M. M. da Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br> | 180 | João M. M. da Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br> |
172 | João M. M. da Silva + Renan Teruo <jaodsilv@linux.ime.usp.br> | 181 | João M. M. da Silva + Renan Teruo <jaodsilv@linux.ime.usp.br> |
173 | -João M. M. Silva + Caio Salgado <jaodsilv@linux.ime.usp.br> | ||
174 | -João M. M. Silva + Diego Araújo <jaodsilv@linux.ime.usp.br> | ||
175 | -Joao M. M. Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br> | ||
176 | -João M. M. Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br> | ||
177 | -João M. M. Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br> | ||
178 | -João M. M. Silva + Renan Teruo <jaodsilv@linux.ime.usp.br> | ||
179 | -Joenio Costa <joenio@colivre.coop.br> | ||
180 | -Josef Spillner <josef.spillner@tu-dresden.de> | ||
181 | -Jose Pedro <1jpsneto@gmail.com> | 182 | +João M. M. da Silva <jaodsilv@linux.ime.usp.br> |
183 | +João Marco Maciel da Silva + Rafael Manzo + Renan Teruo <jaodsilv@linux.ime.usp.br> | ||
184 | +João da Silva + Eduardo Morais + Rafael Manzo <rr.manzo@gmail.com> | ||
185 | +João da Silva <jaodsilv@linux.ime.usp.br> | ||
182 | Junior Silva <junior@bajor.localhost.localdomain> | 186 | Junior Silva <junior@bajor.localhost.localdomain> |
183 | Junior Silva <junior@sedeantigo.colivre.coop.br> | 187 | Junior Silva <junior@sedeantigo.colivre.coop.br> |
184 | Junior Silva <juniorsilva1001@gmail.com> | 188 | Junior Silva <juniorsilva1001@gmail.com> |
185 | Junior Silva <juniorsilva7@juniorsilva-Aspire-5750Z.(none)> | 189 | Junior Silva <juniorsilva7@juniorsilva-Aspire-5750Z.(none)> |
186 | Junior Silva <juniorsilva@colivre.coop.br> | 190 | Junior Silva <juniorsilva@colivre.coop.br> |
187 | -juniorsilva <juniorsilva@QonoS.localhost.localdomain> | 191 | +Jérôme Jutteau <j.jutteau@gmail.com> |
188 | Keilla Menezes <keilla@colivre.coop.br> | 192 | Keilla Menezes <keilla@colivre.coop.br> |
189 | Larissa Reis <larissa@colivre.coop.br> | 193 | Larissa Reis <larissa@colivre.coop.br> |
190 | Larissa Reis <reiss.larissa@gmail.com> | 194 | Larissa Reis <reiss.larissa@gmail.com> |
191 | Leandro Alves <leandrosustenido@gmail.com> | 195 | Leandro Alves <leandrosustenido@gmail.com> |
192 | -Leandro Nunes dos Santos <81665687568@serpro-1541727.Home> | ||
193 | Leandro Nunes dos Santos <81665687568@serpro-1541727.(none)> | 196 | Leandro Nunes dos Santos <81665687568@serpro-1541727.(none)> |
194 | -Leandro Nunes dos Santos <leandronunes@gmail.com> | 197 | +Leandro Nunes dos Santos <81665687568@serpro-1541727.Home> |
195 | Leandro Nunes dos Santos <leandro.santos@serpro.gov.br> | 198 | Leandro Nunes dos Santos <leandro.santos@serpro.gov.br> |
199 | +Leandro Nunes dos Santos <leandronunes@gmail.com> | ||
196 | LinguÁgil 2010 <linguagil.bahia@gmail.com> | 200 | LinguÁgil 2010 <linguagil.bahia@gmail.com> |
197 | Lucas Kanashiro <kanashiro.duarte@gmail.com> | 201 | Lucas Kanashiro <kanashiro.duarte@gmail.com> |
198 | Lucas Melo <lucas@colivre.coop.br> | 202 | Lucas Melo <lucas@colivre.coop.br> |
199 | Lucas Melo <lucaspradomelo@gmail.com> | 203 | Lucas Melo <lucaspradomelo@gmail.com> |
200 | Luciano <lucianopcbr@gmail.com> | 204 | Luciano <lucianopcbr@gmail.com> |
205 | +Luciano Prestes Cavacanti <lucianopcbr@gmail.com> | ||
201 | Luciano Prestes Cavalcanti <lucianopcbr@gmail.com> | 206 | Luciano Prestes Cavalcanti <lucianopcbr@gmail.com> |
202 | Luis David Aguilar Carlos <ludwig9003@gmail.com> | 207 | Luis David Aguilar Carlos <ludwig9003@gmail.com> |
203 | Luiz Fernando de Freitas Matos <luiz@luizff.matos@gmail.com> | 208 | Luiz Fernando de Freitas Matos <luiz@luizff.matos@gmail.com> |
209 | +M for Momo <mo@rtnp.org> | ||
210 | +Marcelo Júnior <maljunior@gmail.com> | ||
204 | Marcos <marcos.rpj2@gmail.com> | 211 | Marcos <marcos.rpj2@gmail.com> |
205 | Marcos Ramos <ms.ramos@outlook.com> | 212 | Marcos Ramos <ms.ramos@outlook.com> |
206 | Martín Olivera <molivera@solar.org.ar> | 213 | Martín Olivera <molivera@solar.org.ar> |
207 | Maurilio Atila <cabelotaina@gmail.com> | 214 | Maurilio Atila <cabelotaina@gmail.com> |
208 | -M for Momo <mo@rtnp.org> | ||
209 | Michal Čihař <michal@cihar.com> | 215 | Michal Čihař <michal@cihar.com> |
216 | +Michel Felipe <mfelipeof@gmail.com> | ||
210 | Moises Machado <moises@colivre.coop.br> | 217 | Moises Machado <moises@colivre.coop.br> |
211 | -Naíla Alves <naila@colivre.coop.br> | ||
212 | Nanda Lopes <nanda.listas+psl@gmail.com> | 218 | Nanda Lopes <nanda.listas+psl@gmail.com> |
219 | +Naíla Alves <naila@colivre.coop.br> | ||
213 | Niemand Jedermann <predatorix@web.de> | 220 | Niemand Jedermann <predatorix@web.de> |
214 | Parley Martins <parleypachecomartins@gmail.com> | 221 | Parley Martins <parleypachecomartins@gmail.com> |
215 | Paulo Meirelles + Alessandro Palmeira + João M. M. da Silva <paulo@softwarelivre.org> | 222 | Paulo Meirelles + Alessandro Palmeira + João M. M. da Silva <paulo@softwarelivre.org> |
@@ -217,8 +224,8 @@ Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org> | @@ -217,8 +224,8 @@ Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org> | ||
217 | Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org> | 224 | Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org> |
218 | Paulo Meirelles + Diego Araújo <paulo@softwarelivre.org> | 225 | Paulo Meirelles + Diego Araújo <paulo@softwarelivre.org> |
219 | Paulo Meirelles + João M. M. da Silva <paulo@softwarelivre.org> | 226 | Paulo Meirelles + João M. M. da Silva <paulo@softwarelivre.org> |
220 | -Paulo Meirelles <paulo@softwarelivre.org> | ||
221 | Paulo Meirelles + Rafael Manzo <paulo@softwarelivre.org> | 227 | Paulo Meirelles + Rafael Manzo <paulo@softwarelivre.org> |
228 | +Paulo Meirelles <paulo@softwarelivre.org> | ||
222 | Rafael Gomes <rafaelgomes@techfree.com.br> | 229 | Rafael Gomes <rafaelgomes@techfree.com.br> |
223 | Rafael Manzo + Alessandro Palmeira <rr.manzo@gmail.com> | 230 | Rafael Manzo + Alessandro Palmeira <rr.manzo@gmail.com> |
224 | Rafael Manzo + Daniel Alves <danpaulalves@gmail.com> | 231 | Rafael Manzo + Daniel Alves <danpaulalves@gmail.com> |
@@ -233,37 +240,46 @@ Rafael Reggiani Manzo + Diego Araújo <rr.manzo@gmail.com> | @@ -233,37 +240,46 @@ Rafael Reggiani Manzo + Diego Araújo <rr.manzo@gmail.com> | ||
233 | Rafael Reggiani Manzo + João M. M. da Silva <rr.manzo@gmail.com> | 240 | Rafael Reggiani Manzo + João M. M. da Silva <rr.manzo@gmail.com> |
234 | Rafael Reggiani Manzo <rr.manzo@gmail.com> | 241 | Rafael Reggiani Manzo <rr.manzo@gmail.com> |
235 | Raphaël Rousseau <raph@r4f.org> | 242 | Raphaël Rousseau <raph@r4f.org> |
236 | -Raquel Lira <raquel.lira@gmail.com> | ||
237 | Raquel <rcordioli@gmail.com> | 243 | Raquel <rcordioli@gmail.com> |
244 | +Raquel Lira <raquel.lira@gmail.com> | ||
238 | Renan Teruo + Caio Salgado <renanteruoc@gmail.com> | 245 | Renan Teruo + Caio Salgado <renanteruoc@gmail.com> |
239 | -Renan Teruoc + Diego Araujo <renanteruoc@gmail.com> | ||
240 | Renan Teruo + Diego Araujo <renanteruoc@gmail.com> | 246 | Renan Teruo + Diego Araujo <renanteruoc@gmail.com> |
241 | Renan Teruo + Diego Araújo <renanteruoc@gmail.com> | 247 | Renan Teruo + Diego Araújo <renanteruoc@gmail.com> |
242 | Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com> | 248 | Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com> |
243 | Renan Teruo + Rafael Manzo <renanteruoc@gmail.com> | 249 | Renan Teruo + Rafael Manzo <renanteruoc@gmail.com> |
250 | +Renan Teruoc + Diego Araujo <renanteruoc@gmail.com> | ||
244 | Rodrigo Souto + Ana Losnak + Daniel Bucher + Caio Almeida + Leandro Nunes + Daniela Feitosa + Mariel Zasso <noosfero-br@listas.softwarelivre.org> | 251 | Rodrigo Souto + Ana Losnak + Daniel Bucher + Caio Almeida + Leandro Nunes + Daniela Feitosa + Mariel Zasso <noosfero-br@listas.softwarelivre.org> |
245 | Rodrigo Souto <rodrigo@colivre.coop.br> | 252 | Rodrigo Souto <rodrigo@colivre.coop.br> |
246 | Ronny Kursawe <kursawe.ronny@googlemail.com> | 253 | Ronny Kursawe <kursawe.ronny@googlemail.com> |
247 | -root <root@debian.sdr.serpro> | ||
248 | Samuel R. C. Vale <srcvale@holoscopio.com> | 254 | Samuel R. C. Vale <srcvale@holoscopio.com> |
255 | +TWS <tablettws@gmail.com> | ||
249 | Tallys Martins <tallysmartins@gmail.com> | 256 | Tallys Martins <tallysmartins@gmail.com> |
250 | Tallys Martins <tallysmartins@yahoo.com.br> | 257 | Tallys Martins <tallysmartins@yahoo.com.br> |
251 | -tallys <tallys@tallys> | ||
252 | -tallys <tallys@tallys.(none)> | ||
253 | Thiago Casotti <thiago.casotti@uol.com.br> | 258 | Thiago Casotti <thiago.casotti@uol.com.br> |
259 | +Thiago Ribeiro <thiagitosouza@hotmail.com> | ||
254 | Thiago Zoroastro <thiago.zoroastro@bol.com.br> | 260 | Thiago Zoroastro <thiago.zoroastro@bol.com.br> |
255 | Tuux <tuxa@galaxie.eu.org> | 261 | Tuux <tuxa@galaxie.eu.org> |
256 | -TWS <tablettws@gmail.com> | ||
257 | Valessio Brito <contato@valessiobrito.com.br> | 262 | Valessio Brito <contato@valessiobrito.com.br> |
258 | Valessio Brito <contato@valessiobrito.info> | 263 | Valessio Brito <contato@valessiobrito.info> |
259 | Valessio Brito <valessio@gmail.com> | 264 | Valessio Brito <valessio@gmail.com> |
260 | -vfcosta <vfcosta@gmail.com> | ||
261 | Victor Carvalho <victorhugodf.ac@gmail.com> | 265 | Victor Carvalho <victorhugodf.ac@gmail.com> |
262 | Victor Costa <vfcosta@gmail.com> | 266 | Victor Costa <vfcosta@gmail.com> |
263 | Victor Hugo Alves de Carvalho <victorhugodf.ac@gmail.com> | 267 | Victor Hugo Alves de Carvalho <victorhugodf.ac@gmail.com> |
264 | Vinicius Cubas Brand <viniciuscb@gmail.com> | 268 | Vinicius Cubas Brand <viniciuscb@gmail.com> |
265 | Visita <visita@debian.(none)> | 269 | Visita <visita@debian.(none)> |
266 | Yann Lugrin <yann.lugrin@liquid-concept.ch> | 270 | Yann Lugrin <yann.lugrin@liquid-concept.ch> |
271 | +alcampelo <alcampelo@alcampelo.(none)> | ||
272 | +analosnak <analosnak@gmail.com> | ||
273 | +daniel <dtygel@eita.org.br> | ||
274 | +diegoamc <diegoamc90@gmail.com> | ||
275 | +dtygel <dtygel@gmail.com> | ||
276 | +juniorsilva <juniorsilva@QonoS.localhost.localdomain> | ||
277 | +root <root@17edebf1ae91> | ||
278 | +root <root@debian.sdr.serpro> | ||
279 | +tallys <tallys@tallys.(none)> | ||
280 | +tallys <tallys@tallys> | ||
281 | +vfcosta <vfcosta@gmail.com> | ||
282 | +Ábner Silva de Oliveira <abner.oliveira@serpro.gov.br> | ||
267 | 283 | ||
268 | Ideas, specifications and incentive | 284 | Ideas, specifications and incentive |
269 | =================================== | 285 | =================================== |
Gemfile
1 | source "https://rubygems.org" | 1 | source "https://rubygems.org" |
2 | -gem 'rails', '~> 3.2.21' | 2 | +gem 'rails', '~> 3.2.22' |
3 | gem 'minitest', '~> 3.2.0' | 3 | gem 'minitest', '~> 3.2.0' |
4 | gem 'fast_gettext', '~> 0.6.8' | 4 | gem 'fast_gettext', '~> 0.6.8' |
5 | gem 'acts-as-taggable-on', '~> 3.4.2' | 5 | gem 'acts-as-taggable-on', '~> 3.4.2' |
@@ -18,7 +18,7 @@ gem 'exception_notification', '~> 4.0.1' | @@ -18,7 +18,7 @@ gem 'exception_notification', '~> 4.0.1' | ||
18 | gem 'gettext', '~> 2.2.1', :require => false | 18 | gem 'gettext', '~> 2.2.1', :require => false |
19 | gem 'locale', '~> 2.0.5' | 19 | gem 'locale', '~> 2.0.5' |
20 | gem 'whenever', :require => false | 20 | gem 'whenever', :require => false |
21 | -gem 'eita-jrails', '>= 0.9.5', :require => 'jrails' | 21 | +gem 'eita-jrails', '~> 0.9.5', require: 'jrails' |
22 | gem 'grape', '~> 0.11.0' | 22 | gem 'grape', '~> 0.11.0' |
23 | gem 'grape-entity' | 23 | gem 'grape-entity' |
24 | gem 'grape-swagger' | 24 | gem 'grape-swagger' |
@@ -27,6 +27,13 @@ gem 'api-pagination', '~> 4.1.1' | @@ -27,6 +27,13 @@ gem 'api-pagination', '~> 4.1.1' | ||
27 | gem 'rack-cors' | 27 | gem 'rack-cors' |
28 | gem 'rack-contrib' | 28 | gem 'rack-contrib' |
29 | gem 'liquid', '~> 3.0.3' | 29 | gem 'liquid', '~> 3.0.3' |
30 | +#gem 'grape-swagger-rails' | ||
31 | + | ||
32 | +# FIXME list here all actual dependencies (i.e. the ones in debian/control), | ||
33 | +# with their GEM names (not the Debian package names) | ||
34 | + | ||
35 | +# FIXME list here all actual dependencies (i.e. the ones in debian/control), | ||
36 | +# with their GEM names (not the Debian package names) | ||
30 | 37 | ||
31 | # asset pipeline | 38 | # asset pipeline |
32 | gem 'uglifier', '>= 1.0.3' | 39 | gem 'uglifier', '>= 1.0.3' |
app/controllers/application_controller.rb
@@ -77,8 +77,8 @@ class ApplicationController < ActionController::Base | @@ -77,8 +77,8 @@ class ApplicationController < ActionController::Base | ||
77 | FastGettext.available_locales = environment.available_locales | 77 | FastGettext.available_locales = environment.available_locales |
78 | FastGettext.default_locale = environment.default_locale | 78 | FastGettext.default_locale = environment.default_locale |
79 | FastGettext.locale = (params[:lang] || session[:lang] || environment.default_locale || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en') | 79 | FastGettext.locale = (params[:lang] || session[:lang] || environment.default_locale || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en') |
80 | - I18n.locale = FastGettext.locale | ||
81 | - I18n.default_locale = FastGettext.default_locale | 80 | + I18n.locale = FastGettext.locale.to_s.gsub '_', '-' |
81 | + I18n.default_locale = FastGettext.default_locale.to_s.gsub '_', '-' | ||
82 | if params[:lang] | 82 | if params[:lang] |
83 | session[:lang] = params[:lang] | 83 | session[:lang] = params[:lang] |
84 | end | 84 | end |
app/controllers/my_profile/cms_controller.rb
@@ -6,7 +6,7 @@ class CmsController < MyProfileController | @@ -6,7 +6,7 @@ class CmsController < MyProfileController | ||
6 | 6 | ||
7 | def search_tags | 7 | def search_tags |
8 | arg = params[:term].downcase | 8 | arg = params[:term].downcase |
9 | - result = ActsAsTaggableOn::Tag.find(:all, :conditions => ['LOWER(name) LIKE ?', "%#{arg}%"]) | 9 | + result = ActsAsTaggableOn::Tag.where('name ILIKE ?', "%#{arg}%").limit(10) |
10 | render :text => prepare_to_token_input_by_label(result).to_json, :content_type => 'application/json' | 10 | render :text => prepare_to_token_input_by_label(result).to_json, :content_type => 'application/json' |
11 | end | 11 | end |
12 | 12 | ||
@@ -94,6 +94,11 @@ class CmsController < MyProfileController | @@ -94,6 +94,11 @@ class CmsController < MyProfileController | ||
94 | record_coming | 94 | record_coming |
95 | if request.post? | 95 | if request.post? |
96 | @article.image = nil if params[:remove_image] == 'true' | 96 | @article.image = nil if params[:remove_image] == 'true' |
97 | + if @article.image.present? && params[:article][:image_builder] && | ||
98 | + params[:article][:image_builder][:label] | ||
99 | + @article.image.label = params[:article][:image_builder][:label] | ||
100 | + @article.image.save! | ||
101 | + end | ||
97 | @article.last_changed_by = user | 102 | @article.last_changed_by = user |
98 | if @article.update_attributes(params[:article]) | 103 | if @article.update_attributes(params[:article]) |
99 | if !continue | 104 | if !continue |
app/controllers/my_profile/tasks_controller.rb
1 | class TasksController < MyProfileController | 1 | class TasksController < MyProfileController |
2 | 2 | ||
3 | protect [:perform_task, :view_tasks], :profile, :only => [:index, :save_tags, :search_tags] | 3 | protect [:perform_task, :view_tasks], :profile, :only => [:index, :save_tags, :search_tags] |
4 | - protect :perform_task, :profile, :except => [:index, :save_tags, :search_tags] | 4 | + protect :perform_task, :profile, :only => [:processed, :change_responsible, :close, :new, :list_requested, :ticket_details, :search_tags] |
5 | 5 | ||
6 | def index | 6 | def index |
7 | - @email_templates = profile.email_templates.find_all_by_template_type(:task_rejection) | 7 | + @rejection_email_templates = profile.email_templates.find_all_by_template_type(:task_rejection) |
8 | + @acceptance_email_templates = profile.email_templates.find_all_by_template_type(:task_acceptance) | ||
8 | 9 | ||
9 | @filter_type = params[:filter_type].presence | 10 | @filter_type = params[:filter_type].presence |
10 | @filter_text = params[:filter_text].presence | 11 | @filter_text = params[:filter_text].presence |
@@ -21,13 +22,17 @@ class TasksController < MyProfileController | @@ -21,13 +22,17 @@ class TasksController < MyProfileController | ||
21 | 22 | ||
22 | @failed = params ? params[:failed] : {} | 23 | @failed = params ? params[:failed] : {} |
23 | 24 | ||
24 | - @responsible_candidates = profile.members.by_role(profile.roles.reject {|r| !r.has_permission?('perform_task')}) if profile.organization? | 25 | + @responsible_candidates = profile.members.by_role(profile.roles.reject {|r| !r.has_permission?('perform_task') && !r.has_permission?('view_tasks')}) if profile.organization? |
25 | 26 | ||
26 | @view_only = !current_person.has_permission?(:perform_task, profile) | 27 | @view_only = !current_person.has_permission?(:perform_task, profile) |
27 | end | 28 | end |
28 | 29 | ||
29 | def processed | 30 | def processed |
30 | - @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at) | 31 | + @tasks = Task.to(profile).without_spam.closed.includes(:requestor, :closed_by).order('tasks.created_at DESC') |
32 | + @filter = params[:filter] || {} | ||
33 | + @tasks = filter_tasks(@filter, @tasks) | ||
34 | + @tasks = @tasks.paginate(:per_page => Task.per_page, :page => params[:page]) | ||
35 | + @task_types = Task.closed_types_for(profile) | ||
31 | end | 36 | end |
32 | 37 | ||
33 | def change_responsible | 38 | def change_responsible |
@@ -111,9 +116,9 @@ class TasksController < MyProfileController | @@ -111,9 +116,9 @@ class TasksController < MyProfileController | ||
111 | end | 116 | end |
112 | 117 | ||
113 | def search_tasks | 118 | def search_tasks |
114 | - | ||
115 | - params[:filter_type] = params[:filter_type].blank? ? nil : params[:filter_type] | ||
116 | - result = Task.pending_all(profile,params) | 119 | + filter_type = params[:filter_type].presence |
120 | + filter_text = params[:filter_text].presence | ||
121 | + result = Task.pending_all(profile,filter_type, filter_text) | ||
117 | 122 | ||
118 | render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} } | 123 | render :json => result.map { |task| {:label => task.data[:name], :value => task.data[:name]} } |
119 | end | 124 | end |
@@ -151,7 +156,29 @@ class TasksController < MyProfileController | @@ -151,7 +156,29 @@ class TasksController < MyProfileController | ||
151 | result = ActsAsTaggableOn::Tag.find(:all, :conditions => ['LOWER(name) LIKE ?', "%#{arg}%"]) | 156 | result = ActsAsTaggableOn::Tag.find(:all, :conditions => ['LOWER(name) LIKE ?', "%#{arg}%"]) |
152 | 157 | ||
153 | render :text => prepare_to_token_input_by_label(result).to_json, :content_type => 'application/json' | 158 | render :text => prepare_to_token_input_by_label(result).to_json, :content_type => 'application/json' |
159 | + end | ||
160 | + | ||
161 | + protected | ||
162 | + | ||
163 | + def filter_tasks(filter, tasks) | ||
164 | + filter[:created_from] = Date.parse(filter[:created_from]) unless filter[:created_from].blank? | ||
165 | + filter[:created_until] = Date.parse(filter[:created_until]) unless filter[:created_until].blank? | ||
166 | + filter[:closed_from] = Date.parse(filter[:closed_from]) unless filter[:closed_from].blank? | ||
167 | + filter[:closed_until] = Date.parse(filter[:closed_until]) unless filter[:closed_until].blank? | ||
168 | + | ||
169 | + tasks = tasks.of(filter[:type].presence) | ||
170 | + tasks = tasks.where(:status => filter[:status]) unless filter[:status].blank? | ||
171 | + | ||
172 | + tasks = tasks.where('tasks.created_at >= ?', filter[:created_from].beginning_of_day) unless filter[:created_from].blank? | ||
173 | + tasks = tasks.where('tasks.created_at <= ?', filter[:created_until].end_of_day) unless filter[:created_until].blank? | ||
174 | + | ||
175 | + tasks = tasks.where('tasks.end_date >= ?', filter[:closed_from].beginning_of_day) unless filter[:closed_from].blank? | ||
176 | + tasks = tasks.where('tasks.end_date <= ?', filter[:closed_until].end_of_day) unless filter[:closed_until].blank? | ||
154 | 177 | ||
178 | + tasks = tasks.like('profiles.name', filter[:requestor]) unless filter[:requestor].blank? | ||
179 | + tasks = tasks.like('closed_bies_tasks.name', filter[:closed_by]) unless filter[:closed_by].blank? | ||
180 | + tasks = tasks.like('tasks.data', filter[:text]) unless filter[:text].blank? | ||
181 | + tasks | ||
155 | end | 182 | end |
156 | 183 | ||
157 | end | 184 | end |
app/controllers/public/account_controller.rb
@@ -98,11 +98,8 @@ class AccountController < ApplicationController | @@ -98,11 +98,8 @@ class AccountController < ApplicationController | ||
98 | @block_bot = !!session[:may_be_a_bot] | 98 | @block_bot = !!session[:may_be_a_bot] |
99 | @invitation_code = params[:invitation_code] | 99 | @invitation_code = params[:invitation_code] |
100 | begin | 100 | begin |
101 | - @user = User.new(params[:user]) | ||
102 | - @user.terms_of_use = environment.terms_of_use | ||
103 | - @user.environment = environment | 101 | + @user = User.build(params[:user], params[:profile_data], environment) |
104 | @terms_of_use = environment.terms_of_use | 102 | @terms_of_use = environment.terms_of_use |
105 | - @user.person_data = params[:profile_data] | ||
106 | @user.return_to = session[:return_to] | 103 | @user.return_to = session[:return_to] |
107 | @person = Person.new(params[:profile_data]) | 104 | @person = Person.new(params[:profile_data]) |
108 | @person.environment = @user.environment | 105 | @person.environment = @user.environment |
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | +class ApiController < PublicController | ||
2 | + | ||
3 | + no_design_blocks | ||
4 | + | ||
5 | + helper_method :endpoints | ||
6 | + | ||
7 | + def index | ||
8 | + end | ||
9 | + | ||
10 | + def playground | ||
11 | + end | ||
12 | + | ||
13 | + private | ||
14 | + | ||
15 | + def endpoints | ||
16 | + Noosfero::API::API.endpoints(environment) | ||
17 | + end | ||
18 | + | ||
19 | +end |
app/helpers/application_helper.rb
@@ -44,6 +44,8 @@ module ApplicationHelper | @@ -44,6 +44,8 @@ module ApplicationHelper | ||
44 | 44 | ||
45 | include PluginsHelper | 45 | include PluginsHelper |
46 | 46 | ||
47 | + include TaskHelper | ||
48 | + | ||
47 | def locale | 49 | def locale |
48 | (@page && !@page.language.blank?) ? @page.language : FastGettext.locale | 50 | (@page && !@page.language.blank?) ? @page.language : FastGettext.locale |
49 | end | 51 | end |
@@ -606,24 +608,14 @@ module ApplicationHelper | @@ -606,24 +608,14 @@ module ApplicationHelper | ||
606 | trigger_class = 'enterprise-trigger' | 608 | trigger_class = 'enterprise-trigger' |
607 | end | 609 | end |
608 | end | 610 | end |
609 | - | ||
610 | - extra_info_tag = '' | ||
611 | - img_class = 'profile-image' | ||
612 | - | ||
613 | - if extra_info.is_a? Hash | ||
614 | - extra_info_tag = content_tag( 'span', extra_info[:value], :class => 'extra_info '+extra_info[:class]) | ||
615 | - img_class +=' '+extra_info[:class] | ||
616 | - else | ||
617 | - extra_info_tag = content_tag( 'span', extra_info, :class => 'extra_info' ) | ||
618 | - end | ||
619 | - #extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) | 611 | + extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) |
620 | links = links_for_balloon(profile) | 612 | links = links_for_balloon(profile) |
621 | content_tag('div', content_tag(tag, | 613 | content_tag('div', content_tag(tag, |
622 | (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? popover_menu(_('Profile links'),profile.short_name,links,{:class => trigger_class, :url => url}) : "") + | 614 | (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? popover_menu(_('Profile links'),profile.short_name,links,{:class => trigger_class, :url => url}) : "") + |
623 | link_to( | 615 | link_to( |
624 | - content_tag( 'span', profile_image( profile, size ), :class => img_class ) + | 616 | + content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + |
625 | content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + | 617 | content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + |
626 | - extra_info_tag + profile_sex_icon( profile ) + profile_cat_icons( profile ), | 618 | + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ), |
627 | profile.url, | 619 | profile.url, |
628 | :class => 'profile_link url', | 620 | :class => 'profile_link url', |
629 | :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, | 621 | :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, |
@@ -1195,7 +1187,7 @@ module ApplicationHelper | @@ -1195,7 +1187,7 @@ module ApplicationHelper | ||
1195 | pending_tasks_count = link_to("<i class=\"icon-menu-tasks\"></i><span class=\"task-count\">#{count}</span>", user.tasks_url, :id => 'pending-tasks-count', :title => _("Manage your pending tasks")) | 1187 | pending_tasks_count = link_to("<i class=\"icon-menu-tasks\"></i><span class=\"task-count\">#{count}</span>", user.tasks_url, :id => 'pending-tasks-count', :title => _("Manage your pending tasks")) |
1196 | end | 1188 | end |
1197 | 1189 | ||
1198 | - (_("<span class='welcome'>Welcome,</span> %s") % link_to("<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>", user.public_profile_url, :id => "homepage-link", :title => _('Go to your homepage'))) + | 1190 | + (_("<span class='welcome'>Welcome,</span> %s") % link_to("<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>", user.url, :id => "homepage-link", :title => _('Go to your homepage'))) + |
1199 | render_environment_features(:usermenu) + | 1191 | render_environment_features(:usermenu) + |
1200 | admin_link + | 1192 | admin_link + |
1201 | manage_enterprises + | 1193 | manage_enterprises + |
@@ -1244,7 +1236,7 @@ module ApplicationHelper | @@ -1244,7 +1236,7 @@ module ApplicationHelper | ||
1244 | 1236 | ||
1245 | def task_information(task) | 1237 | def task_information(task) |
1246 | values = {} | 1238 | values = {} |
1247 | - values.merge!({:requestor => link_to(task.requestor.name, task.requestor.public_profile_url)}) if task.requestor | 1239 | + values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor |
1248 | values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject | 1240 | values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject |
1249 | values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject | 1241 | values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject |
1250 | values.merge!(task.information[:variables]) if task.information[:variables] | 1242 | values.merge!(task.information[:variables]) if task.information[:variables] |
app/helpers/comment_helper.rb
@@ -16,7 +16,7 @@ module CommentHelper | @@ -16,7 +16,7 @@ module CommentHelper | ||
16 | content_tag('span', show_date(article.published_at), :class => 'date') + | 16 | content_tag('span', show_date(article.published_at), :class => 'date') + |
17 | content_tag('span', [_(", by %s") % link_to(article.author_name, article.author_url)], :class => 'author') + | 17 | content_tag('span', [_(", by %s") % link_to(article.author_name, article.author_url)], :class => 'author') + |
18 | content_tag('span', comments, :class => 'comments'), | 18 | content_tag('span', comments, :class => 'comments'), |
19 | - :class => 'created-at' | 19 | + :class => 'publishing-info' |
20 | ) | 20 | ) |
21 | end | 21 | end |
22 | title | 22 | title |
app/helpers/content_viewer_helper.rb
@@ -30,7 +30,7 @@ module ContentViewerHelper | @@ -30,7 +30,7 @@ module ContentViewerHelper | ||
30 | date_format + | 30 | date_format + |
31 | content_tag('span', _(", by %s") % (article.author ? link_to(article.author_name, article.author_url) : article.author_name), :class => 'author') + | 31 | content_tag('span', _(", by %s") % (article.author ? link_to(article.author_name, article.author_url) : article.author_name), :class => 'author') + |
32 | content_tag('span', comments, :class => 'comments'), | 32 | content_tag('span', comments, :class => 'comments'), |
33 | - :class => 'created-at' | 33 | + :class => 'publishing-info' |
34 | ) | 34 | ) |
35 | end | 35 | end |
36 | title | 36 | title |
app/helpers/events_helper.rb
@@ -30,7 +30,7 @@ module EventsHelper | @@ -30,7 +30,7 @@ module EventsHelper | ||
30 | # the day itself | 30 | # the day itself |
31 | date, | 31 | date, |
32 | # is there any events in this date? | 32 | # is there any events in this date? |
33 | - events.any? {|event| event.date_range.include?(date)}, | 33 | + events.any? {|event| event.date_range.cover?(date)}, |
34 | # is this date in the current month? | 34 | # is this date in the current month? |
35 | true | 35 | true |
36 | ] | 36 | ] |
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +module TaskHelper | ||
2 | + | ||
3 | + def task_email_template(description, email_templates, task, include_blank=true) | ||
4 | + return '' unless email_templates.present? | ||
5 | + | ||
6 | + content_tag( | ||
7 | + :div, | ||
8 | + labelled_form_field(description, select_tag("tasks[#{task.id}][task][email_template_id]", options_from_collection_for_select(email_templates, :id, :name), :include_blank => include_blank, 'data-url' => url_for(:controller => 'email_templates', :action => 'show_parsed', :profile => profile.identifier))), | ||
9 | + :class => 'template-selection' | ||
10 | + ) | ||
11 | + end | ||
12 | + | ||
13 | +end |
app/models/add_friend.rb
@@ -14,6 +14,9 @@ class AddFriend < Task | @@ -14,6 +14,9 @@ class AddFriend < Task | ||
14 | alias :friend :target | 14 | alias :friend :target |
15 | alias :friend= :target= | 15 | alias :friend= :target= |
16 | 16 | ||
17 | + validates :requestor, :kind_of => { :kind => Person } | ||
18 | + validates :target, :kind_of => { :kind => Person } | ||
19 | + | ||
17 | after_create do |task| | 20 | after_create do |task| |
18 | TaskMailer.invitation_notification(task).deliver unless task.friend | 21 | TaskMailer.invitation_notification(task).deliver unless task.friend |
19 | remove_from_suggestion_list(task) | 22 | remove_from_suggestion_list(task) |
app/models/add_member.rb
@@ -2,6 +2,9 @@ class AddMember < Task | @@ -2,6 +2,9 @@ class AddMember < Task | ||
2 | 2 | ||
3 | validates_presence_of :requestor_id, :target_id | 3 | validates_presence_of :requestor_id, :target_id |
4 | 4 | ||
5 | + validates :requestor, kind_of: {kind: Person} | ||
6 | + validates :target, kind_of: {kind: Organization} | ||
7 | + | ||
5 | alias :person :requestor | 8 | alias :person :requestor |
6 | alias :person= :requestor= | 9 | alias :person= :requestor= |
7 | 10 |
app/models/approve_article.rb
1 | class ApproveArticle < Task | 1 | class ApproveArticle < Task |
2 | validates_presence_of :requestor_id, :target_id | 2 | validates_presence_of :requestor_id, :target_id |
3 | 3 | ||
4 | + validates :requestor, kind_of: {kind: Person} | ||
5 | + validate :allowed_requestor | ||
6 | + | ||
7 | + def allowed_requestor | ||
8 | + if target | ||
9 | + if target.person? && requestor != target | ||
10 | + self.errors.add(:requestor, _('You can not post articles to other users.')) | ||
11 | + end | ||
12 | + if target.organization? && !target.members.include?(requestor) && target.environment.portal_community != target | ||
13 | + self.errors.add(:requestor, _('Only members can post articles on communities.')) | ||
14 | + end | ||
15 | + end | ||
16 | + end | ||
17 | + | ||
4 | def article_title | 18 | def article_title |
5 | article ? article.title : _('(The original text was removed)') | 19 | article ? article.title : _('(The original text was removed)') |
6 | end | 20 | end |
7 | - | 21 | + |
8 | def article | 22 | def article |
9 | Article.find_by_id data[:article_id] | 23 | Article.find_by_id data[:article_id] |
10 | end | 24 | end |
@@ -124,4 +138,9 @@ class ApproveArticle < Task | @@ -124,4 +138,9 @@ class ApproveArticle < Task | ||
124 | message | 138 | message |
125 | end | 139 | end |
126 | 140 | ||
141 | + def request_is_member_of_target | ||
142 | + unless requestor.is_member_of?(target) | ||
143 | + errors.add(:approve_article, N_('Requestor must be a member of target.')) | ||
144 | + end | ||
145 | + end | ||
127 | end | 146 | end |
app/models/article.rb
@@ -133,19 +133,11 @@ class Article < ActiveRecord::Base | @@ -133,19 +133,11 @@ class Article < ActiveRecord::Base | ||
133 | {:include => 'categories_including_virtual', :conditions => { 'categories.id' => category.id }} | 133 | {:include => 'categories_including_virtual', :conditions => { 'categories.id' => category.id }} |
134 | } | 134 | } |
135 | 135 | ||
136 | - #FIXME make this test | ||
137 | - scope :newer_than, lambda { |reference_id| | ||
138 | - {:conditions => ["articles.id > #{reference_id}"]} | ||
139 | - } | ||
140 | - | ||
141 | - #FIXME make this test | ||
142 | - scope :older_than, lambda { |reference_id| | ||
143 | - {:conditions => ["articles.id < #{reference_id}"]} | ||
144 | - } | 136 | + include TimeScopes |
145 | 137 | ||
146 | scope :by_range, lambda { |range| { | 138 | scope :by_range, lambda { |range| { |
147 | :conditions => [ | 139 | :conditions => [ |
148 | - 'published_at BETWEEN :start_date AND :end_date', { :start_date => range.first, :end_date => range.last } | 140 | + 'articles.published_at BETWEEN :start_date AND :end_date', { :start_date => range.first, :end_date => range.last } |
149 | ] | 141 | ] |
150 | }} | 142 | }} |
151 | 143 | ||
@@ -748,8 +740,9 @@ class Article < ActiveRecord::Base | @@ -748,8 +740,9 @@ class Article < ActiveRecord::Base | ||
748 | paragraphs.empty? ? '' : paragraphs.first.to_html | 740 | paragraphs.empty? ? '' : paragraphs.first.to_html |
749 | end | 741 | end |
750 | 742 | ||
751 | - def lead | ||
752 | - abstract.blank? ? first_paragraph.html_safe : abstract.html_safe | 743 | + def lead(length = nil) |
744 | + content = abstract.blank? ? first_paragraph.html_safe : abstract.html_safe | ||
745 | + length.present? ? content.truncate(length) : content | ||
753 | end | 746 | end |
754 | 747 | ||
755 | def short_lead | 748 | def short_lead |
@@ -820,6 +813,10 @@ class Article < ActiveRecord::Base | @@ -820,6 +813,10 @@ class Article < ActiveRecord::Base | ||
820 | "content_viewer/view_page" | 813 | "content_viewer/view_page" |
821 | end | 814 | end |
822 | 815 | ||
816 | + def to_liquid | ||
817 | + HashWithIndifferentAccess.new :name => name, :abstract => abstract, :body => body, :id => id, :parent_id => parent_id, :author => author | ||
818 | + end | ||
819 | + | ||
823 | private | 820 | private |
824 | 821 | ||
825 | def sanitize_tag_list | 822 | def sanitize_tag_list |
app/models/change_password.rb
@@ -18,6 +18,8 @@ class ChangePassword < Task | @@ -18,6 +18,8 @@ class ChangePassword < Task | ||
18 | 18 | ||
19 | validates_presence_of :requestor | 19 | validates_presence_of :requestor |
20 | 20 | ||
21 | + validates :requestor, kind_of: {kind: Person} | ||
22 | + | ||
21 | ################################################### | 23 | ################################################### |
22 | # validations for updating a ChangePassword task | 24 | # validations for updating a ChangePassword task |
23 | 25 |
app/models/comment.rb
@@ -20,16 +20,7 @@ class Comment < ActiveRecord::Base | @@ -20,16 +20,7 @@ class Comment < ActiveRecord::Base | ||
20 | 20 | ||
21 | scope :without_reply, :conditions => ['reply_of_id IS NULL'] | 21 | scope :without_reply, :conditions => ['reply_of_id IS NULL'] |
22 | 22 | ||
23 | - #FIXME make this test | ||
24 | - scope :newer_than, lambda { |reference_id| | ||
25 | - {:conditions => ["comments.id > #{reference_id}"]} | ||
26 | - } | ||
27 | - | ||
28 | - #FIXME make this test | ||
29 | - scope :older_than, lambda { |reference_id| | ||
30 | - {:conditions => ["comments.id < #{reference_id}"]} | ||
31 | - } | ||
32 | - | 23 | + include TimeScopes |
33 | 24 | ||
34 | # unauthenticated authors: | 25 | # unauthenticated authors: |
35 | validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) | 26 | validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) |
app/models/create_community.rb
@@ -3,6 +3,9 @@ class CreateCommunity < Task | @@ -3,6 +3,9 @@ class CreateCommunity < Task | ||
3 | validates_presence_of :requestor_id, :target_id | 3 | validates_presence_of :requestor_id, :target_id |
4 | validates_presence_of :name | 4 | validates_presence_of :name |
5 | 5 | ||
6 | + validates :requestor, kind_of: {kind: Person} | ||
7 | + validates :target, kind_of: {kind: Environment} | ||
8 | + | ||
6 | alias :environment :target | 9 | alias :environment :target |
7 | alias :environment= :target= | 10 | alias :environment= :target= |
8 | 11 |
app/models/create_enterprise.rb
@@ -27,6 +27,8 @@ class CreateEnterprise < Task | @@ -27,6 +27,8 @@ class CreateEnterprise < Task | ||
27 | # checks for actual attributes | 27 | # checks for actual attributes |
28 | validates_presence_of :requestor_id, :target_id | 28 | validates_presence_of :requestor_id, :target_id |
29 | 29 | ||
30 | + validates :requestor, kind_of: {kind: Person} | ||
31 | + | ||
30 | # checks for admins required attributes | 32 | # checks for admins required attributes |
31 | DATA_FIELDS.each do |attribute| | 33 | DATA_FIELDS.each do |attribute| |
32 | validates_presence_of attribute, :if => lambda { |obj| obj.environment.required_enterprise_fields.include?(attribute) } | 34 | validates_presence_of attribute, :if => lambda { |obj| obj.environment.required_enterprise_fields.include?(attribute) } |
app/models/email_activation.rb
1 | class EmailActivation < Task | 1 | class EmailActivation < Task |
2 | 2 | ||
3 | validates_presence_of :requestor_id, :target_id | 3 | validates_presence_of :requestor_id, :target_id |
4 | + | ||
5 | + validates :requestor, kind_of: {kind: Person} | ||
6 | + validates :target, kind_of: {kind: Environment} | ||
7 | + | ||
4 | validate :already_requested, :on => :create | 8 | validate :already_requested, :on => :create |
5 | 9 | ||
6 | alias :environment :target | 10 | alias :environment :target |
7 | alias :person :requestor | 11 | alias :person :requestor |
8 | 12 | ||
9 | def already_requested | 13 | def already_requested |
10 | - if !self.requestor.nil? && self.requestor.user.email_activation_pending? | 14 | + if !self.requestor.nil? && self.requestor.person? && self.requestor.user.email_activation_pending? |
11 | self.errors.add(:base, _('You have already requested activation of your mailbox.')) | 15 | self.errors.add(:base, _('You have already requested activation of your mailbox.')) |
12 | end | 16 | end |
13 | end | 17 | end |
app/models/email_template.rb
@@ -17,6 +17,7 @@ class EmailTemplate < ActiveRecord::Base | @@ -17,6 +17,7 @@ class EmailTemplate < ActiveRecord::Base | ||
17 | def available_types | 17 | def available_types |
18 | HashWithIndifferentAccess.new ({ | 18 | HashWithIndifferentAccess.new ({ |
19 | :task_rejection => {:description => _('Task Rejection')}, | 19 | :task_rejection => {:description => _('Task Rejection')}, |
20 | + :task_acceptance => {:description => _('Task Acceptance')}, | ||
20 | :organization_members => {:description => _('Organization Members')} | 21 | :organization_members => {:description => _('Organization Members')} |
21 | }) | 22 | }) |
22 | end | 23 | end |
app/models/enterprise_activation.rb
@@ -8,6 +8,8 @@ class EnterpriseActivation < Task | @@ -8,6 +8,8 @@ class EnterpriseActivation < Task | ||
8 | 8 | ||
9 | validates_presence_of :enterprise | 9 | validates_presence_of :enterprise |
10 | 10 | ||
11 | + validates :target, kind_of: {kind: Enterprise} | ||
12 | + | ||
11 | def perform | 13 | def perform |
12 | self.enterprise.enable self.requestor | 14 | self.enterprise.enable self.requestor |
13 | end | 15 | end |
app/models/environment.rb
@@ -326,6 +326,9 @@ class Environment < ActiveRecord::Base | @@ -326,6 +326,9 @@ class Environment < ActiveRecord::Base | ||
326 | 326 | ||
327 | settings_items :signup_welcome_screen_body, :type => String | 327 | settings_items :signup_welcome_screen_body, :type => String |
328 | 328 | ||
329 | + #Captcha settings | ||
330 | + settings_items :api_captcha_settings, :type => ActiveSupport::HashWithIndifferentAccess, :default => {} | ||
331 | + | ||
329 | def has_custom_welcome_screen? | 332 | def has_custom_welcome_screen? |
330 | settings[:signup_welcome_screen_body].present? | 333 | settings[:signup_welcome_screen_body].present? |
331 | end | 334 | end |
app/models/event.rb
@@ -99,47 +99,19 @@ class Event < Article | @@ -99,47 +99,19 @@ class Event < Article | ||
99 | start_date..(end_date||start_date) | 99 | start_date..(end_date||start_date) |
100 | end | 100 | end |
101 | 101 | ||
102 | - # FIXME this shouldn't be needed | ||
103 | - include ActionView::Helpers::TagHelper | ||
104 | - include ActionView::Helpers::UrlHelper | ||
105 | - include DatesHelper | 102 | + def first_paragraph |
103 | + paragraphs = Nokogiri::HTML.fragment(self.body).css('p') | ||
104 | + paragraphs.empty? ? '' : paragraphs.first.to_html | ||
105 | + end | ||
106 | 106 | ||
107 | def to_html(options = {}) | 107 | def to_html(options = {}) |
108 | + event = self | ||
109 | + format = options[:format] | ||
108 | 110 | ||
109 | - result = '' | ||
110 | - html = ::Builder::XmlMarkup.new(:target => result) | ||
111 | - | ||
112 | - html.div(:class => 'event-info' ) { | ||
113 | - html.ul(:class => 'event-data' ) { | ||
114 | - html.li(:class => 'event-dates' ) { | ||
115 | - html.span _('When:') | ||
116 | - html.text! show_period(start_date, end_date) | ||
117 | - } if start_date.present? || end_date.present? | ||
118 | - html.li { | ||
119 | - html.span _('URL:') | ||
120 | - html.a(self.link || "", 'href' => self.link || "") | ||
121 | - } if self.link.present? | ||
122 | - html.li { | ||
123 | - html.span _('Address:') | ||
124 | - html.text! self.address || "" | ||
125 | - } if self.address.present? | ||
126 | - } | ||
127 | - | ||
128 | - # TODO: some good soul, please clean this ugly hack: | ||
129 | - if self.body | ||
130 | - html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description') | ||
131 | - end | ||
132 | - } | ||
133 | - | ||
134 | - if self.body | ||
135 | - if options[:format] == 'short' | ||
136 | - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', display_short_format(self)) | ||
137 | - else | ||
138 | - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.body) | ||
139 | - end | 111 | + proc do |
112 | + render :file => 'content_viewer/event_page', :locals => { :event => event, | ||
113 | + :format => format } | ||
140 | end | 114 | end |
141 | - | ||
142 | - result | ||
143 | end | 115 | end |
144 | 116 | ||
145 | def duration | 117 | def duration |
app/models/image.rb
@@ -23,7 +23,7 @@ class Image < ActiveRecord::Base | @@ -23,7 +23,7 @@ class Image < ActiveRecord::Base | ||
23 | 23 | ||
24 | postgresql_attachment_fu | 24 | postgresql_attachment_fu |
25 | 25 | ||
26 | - attr_accessible :uploaded_data | 26 | + attr_accessible :uploaded_data, :label |
27 | 27 | ||
28 | def current_data | 28 | def current_data |
29 | File.file?(full_filename) ? File.read(full_filename) : nil | 29 | File.file?(full_filename) ? File.read(full_filename) : nil |
app/models/invitation.rb
@@ -6,6 +6,8 @@ class Invitation < Task | @@ -6,6 +6,8 @@ class Invitation < Task | ||
6 | 6 | ||
7 | validates_presence_of :target_id, :if => Proc.new{|invite| invite.friend_email.blank?} | 7 | validates_presence_of :target_id, :if => Proc.new{|invite| invite.friend_email.blank?} |
8 | 8 | ||
9 | + validates :requestor, kind_of: {kind: Person} | ||
10 | + | ||
9 | validates_presence_of :friend_email, :if => Proc.new{|invite| invite.target_id.blank?} | 11 | validates_presence_of :friend_email, :if => Proc.new{|invite| invite.target_id.blank?} |
10 | validates_format_of :friend_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => Proc.new{|invite| invite.target_id.blank?} | 12 | validates_format_of :friend_email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => Proc.new{|invite| invite.target_id.blank?} |
11 | 13 | ||
@@ -34,7 +36,7 @@ class Invitation < Task | @@ -34,7 +36,7 @@ class Invitation < Task | ||
34 | end | 36 | end |
35 | 37 | ||
36 | def not_invite_yourself | 38 | def not_invite_yourself |
37 | - email = friend ? friend.user.email : friend_email | 39 | + email = friend && friend.person? ? friend.user.email : friend_email |
38 | if person && email && person.user.email == email | 40 | if person && email && person.user.email == email |
39 | self.errors.add(:base, _("You can't invite youself")) | 41 | self.errors.add(:base, _("You can't invite youself")) |
40 | end | 42 | end |
@@ -136,7 +138,11 @@ class Invitation < Task | @@ -136,7 +138,11 @@ class Invitation < Task | ||
136 | end | 138 | end |
137 | 139 | ||
138 | def environment | 140 | def environment |
139 | - self.requestor.environment | 141 | + if self.requestor |
142 | + self.requestor.environment | ||
143 | + else | ||
144 | + nil | ||
145 | + end | ||
140 | end | 146 | end |
141 | 147 | ||
142 | end | 148 | end |
app/models/moderate_user_registration.rb
@@ -7,6 +7,8 @@ class ModerateUserRegistration < Task | @@ -7,6 +7,8 @@ class ModerateUserRegistration < Task | ||
7 | 7 | ||
8 | after_create :schedule_spam_checking | 8 | after_create :schedule_spam_checking |
9 | 9 | ||
10 | + validates :target, kind_of: {kind: Environment} | ||
11 | + | ||
10 | alias :environment :target | 12 | alias :environment :target |
11 | alias :environment= :target= | 13 | alias :environment= :target= |
12 | 14 |
app/models/organization.rb
@@ -8,13 +8,30 @@ class Organization < Profile | @@ -8,13 +8,30 @@ class Organization < Profile | ||
8 | :display => %w[compact] | 8 | :display => %w[compact] |
9 | } | 9 | } |
10 | 10 | ||
11 | - scope :visible_for_person, lambda { |person| | ||
12 | - joins('LEFT JOIN "role_assignments" ON "role_assignments"."resource_id" = "profiles"."id" AND "role_assignments"."resource_type" = \'Profile\'') | 11 | + # An Organization is considered visible to a given person if one of the |
12 | +# following conditions are met: | ||
13 | +# 1) The user is an environment administrator. | ||
14 | +# 2) The user is an administrator of the organization. | ||
15 | +# 3) The user is a member of the organization and the organization is | ||
16 | +# visible. | ||
17 | +# 4) The user is not a member of the organization but the organization is | ||
18 | +# visible, public and enabled. | ||
19 | + def self.visible_for_person(person) | ||
20 | + joins('LEFT JOIN "role_assignments" ON ("role_assignments"."resource_id" = "profiles"."id" | ||
21 | + AND "role_assignments"."resource_type" = \'Profile\') OR ( | ||
22 | + "role_assignments"."resource_id" = "profiles"."environment_id" AND | ||
23 | + "role_assignments"."resource_type" = \'Environment\' )') | ||
24 | + .joins('LEFT JOIN "roles" ON "role_assignments"."role_id" = "roles"."id"') | ||
13 | .where( | 25 | .where( |
14 | - ['( ( role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR | ||
15 | - (profiles.public_profile = ?)) AND (profiles.visible = ?)', Profile.name, person.id, true, true] | 26 | + ['( (roles.key = ? OR roles.key = ?) AND role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) |
27 | + OR | ||
28 | + ( ( ( role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR | ||
29 | + ( profiles.public_profile = ? AND profiles.enabled = ? ) ) AND | ||
30 | + ( profiles.visible = ? ) )', | ||
31 | + 'profile_admin', 'environment_administrator', Profile.name, person.id, | ||
32 | + Profile.name, person.id, true, true, true] | ||
16 | ).uniq | 33 | ).uniq |
17 | - } | 34 | + end |
18 | 35 | ||
19 | settings_items :closed, :type => :boolean, :default => false | 36 | settings_items :closed, :type => :boolean, :default => false |
20 | def closed? | 37 | def closed? |
@@ -154,6 +171,12 @@ class Organization < Profile | @@ -154,6 +171,12 @@ class Organization < Profile | ||
154 | ] | 171 | ] |
155 | end | 172 | end |
156 | 173 | ||
174 | + def short_name chars = 40 | ||
175 | + s = self.display_name | ||
176 | + s = super(chars) if s.blank? | ||
177 | + s | ||
178 | + end | ||
179 | + | ||
157 | def notification_emails | 180 | def notification_emails |
158 | emails = [contact_email].select(&:present?) + admins.map(&:email) | 181 | emails = [contact_email].select(&:present?) + admins.map(&:email) |
159 | if emails.empty? | 182 | if emails.empty? |
app/models/person.rb
1 | # A person is the profile of an user holding all relationships with the rest of the system | 1 | # A person is the profile of an user holding all relationships with the rest of the system |
2 | class Person < Profile | 2 | class Person < Profile |
3 | 3 | ||
4 | - attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, | ||
5 | - :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, | ||
6 | - :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, | 4 | + attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, |
5 | + :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, | ||
6 | + :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, | ||
7 | :custom_area_of_study, :professional_activity, :organization_website, :following_articiles | 7 | :custom_area_of_study, :professional_activity, :organization_website, :following_articiles |
8 | 8 | ||
9 | SEARCH_FILTERS = { | 9 | SEARCH_FILTERS = { |
@@ -45,13 +45,17 @@ roles] } | @@ -45,13 +45,17 @@ roles] } | ||
45 | } | 45 | } |
46 | 46 | ||
47 | scope :visible_for_person, lambda { |person| | 47 | scope :visible_for_person, lambda { |person| |
48 | - joins('LEFT JOIN "friendships" ON "friendships"."friend_id" = "profiles"."id"') | ||
49 | - .where( | ||
50 | - ['( ( friendships.person_id = ? ) OR (profiles.public_profile = ?)) AND (profiles.visible = ?)', person.id, true, true] | ||
51 | - ).uniq | 48 | + joins('LEFT JOIN "role_assignments" ON |
49 | + "role_assignments"."resource_id" = "profiles"."environment_id" AND | ||
50 | + "role_assignments"."resource_type" = \'Environment\'') | ||
51 | + .joins('LEFT JOIN "roles" ON "role_assignments"."role_id" = "roles"."id"') | ||
52 | + .joins('LEFT JOIN "friendships" ON "friendships"."friend_id" = "profiles"."id"') | ||
53 | + .where( | ||
54 | + ['( roles.key = ? AND role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR ( | ||
55 | + ( ( friendships.person_id = ? ) OR (profiles.public_profile = ?)) AND (profiles.visible = ?) )', 'environment_administrator', Profile.name, person.id, person.id, true, true] | ||
56 | + ).uniq | ||
52 | } | 57 | } |
53 | 58 | ||
54 | - | ||
55 | def has_permission_with_admin?(permission, resource) | 59 | def has_permission_with_admin?(permission, resource) |
56 | return true if resource.blank? || resource.admins.include?(self) | 60 | return true if resource.blank? || resource.admins.include?(self) |
57 | return true if resource.kind_of?(Profile) && resource.environment.admins.include?(self) | 61 | return true if resource.kind_of?(Profile) && resource.environment.admins.include?(self) |
app/models/product.rb
@@ -51,6 +51,25 @@ class Product < ActiveRecord::Base | @@ -51,6 +51,25 @@ class Product < ActiveRecord::Base | ||
51 | {:joins => :product_category, :conditions => ['categories.path LIKE ?', "%#{category.slug}%"]} if category | 51 | {:joins => :product_category, :conditions => ['categories.path LIKE ?', "%#{category.slug}%"]} if category |
52 | } | 52 | } |
53 | 53 | ||
54 | + scope :visible_for_person, lambda { |person| | ||
55 | + joins('INNER JOIN "profiles" enterprises ON enterprises."id" = "products"."profile_id"') | ||
56 | + .joins('LEFT JOIN "role_assignments" ON ("role_assignments"."resource_id" = enterprises."id" | ||
57 | + AND "role_assignments"."resource_type" = \'Profile\') OR ( | ||
58 | + "role_assignments"."resource_id" = enterprises."environment_id" AND | ||
59 | + "role_assignments"."resource_type" = \'Environment\' )') | ||
60 | + .joins('LEFT JOIN "roles" ON "role_assignments"."role_id" = "roles"."id"') | ||
61 | + .where( | ||
62 | + ['( (roles.key = ? OR roles.key = ?) AND role_assignments.accessor_type = \'Profile\' AND role_assignments.accessor_id = ? ) | ||
63 | + OR | ||
64 | + ( ( ( role_assignments.accessor_type = \'Profile\' AND | ||
65 | + role_assignments.accessor_id = ? ) OR | ||
66 | + ( enterprises.public_profile = ? AND enterprises.enabled = ? ) ) AND | ||
67 | + ( enterprises.visible = ? ) )', | ||
68 | + 'profile_admin', 'environment_administrator', person.id, person.id, | ||
69 | + true, true, true] | ||
70 | + ).uniq | ||
71 | + } | ||
72 | + | ||
54 | after_update :save_image | 73 | after_update :save_image |
55 | 74 | ||
56 | def lat | 75 | def lat |
app/models/profile.rb
@@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
3 | # which by default is the one returned by Environment:default. | 3 | # which by default is the one returned by Environment:default. |
4 | class Profile < ActiveRecord::Base | 4 | class Profile < ActiveRecord::Base |
5 | 5 | ||
6 | - attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time, :redirection_after_login, :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :custom_fields | 6 | + attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time, :redirection_after_login, :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :custom_fields, :administrator_mail_notification |
7 | 7 | ||
8 | # use for internationalizable human type names in search facets | 8 | # use for internationalizable human type names in search facets |
9 | # reimplement on subclasses | 9 | # reimplement on subclasses |
@@ -129,15 +129,49 @@ class Profile < ActiveRecord::Base | @@ -129,15 +129,49 @@ class Profile < ActiveRecord::Base | ||
129 | } | 129 | } |
130 | scope :no_templates, {:conditions => {:is_template => false}} | 130 | scope :no_templates, {:conditions => {:is_template => false}} |
131 | 131 | ||
132 | - #FIXME make this test | ||
133 | - scope :newer_than, lambda { |reference_id| | ||
134 | - {:conditions => ["profiles.id > #{reference_id}"]} | ||
135 | - } | 132 | + # Returns a scoped object to select profiles in a given location or in a radius |
133 | + # distance from the given location center. | ||
134 | + # The parameter can be the `request.params` with the keys: | ||
135 | + # * `country`: Country code string. | ||
136 | + # * `state`: Second-level administrative country subdivisions. | ||
137 | + # * `city`: City full name for center definition, or as set by users. | ||
138 | + # * `lat`: The latitude to define the center of georef search. | ||
139 | + # * `lng`: The longitude to define the center of georef search. | ||
140 | + # * `distance`: Define the search radius in kilometers. | ||
141 | + # NOTE: This method may return an exception object, to inform filter error. | ||
142 | + # When chaining scopes, is hardly recommended you to add this as the last one, | ||
143 | + # if you can't be sure about the provided parameters. | ||
144 | + def self.by_location(params) | ||
145 | + params = params.with_indifferent_access | ||
146 | + if params[:distance].blank? | ||
147 | + where_code = [] | ||
148 | + [ :city, :state, :country ].each do |place| | ||
149 | + unless params[place].blank? | ||
150 | + # ... So we must to find on this named location | ||
151 | + # TODO: convert location attrs to a table collumn | ||
152 | + where_code << "(profiles.data like '%#{place}: #{params[place]}%')" | ||
153 | + end | ||
154 | + end | ||
155 | + self.where where_code.join(' AND ') | ||
156 | + else # Filter in a georef circle | ||
157 | + unless params[:lat].blank? && params[:lng].blank? | ||
158 | + lat, lng = [ params[:lat].to_f, params[:lng].to_f ] | ||
159 | + end | ||
160 | + if !lat | ||
161 | + location = [ params[:city], params[:state], params[:country] ].compact.join(', ') | ||
162 | + if location.blank? | ||
163 | + return Exception.new ( | ||
164 | + _('You must to provide `lat` and `lng`, or `city` and `country` to define the center of the search circle, defined by `distance`.') | ||
165 | + ) | ||
166 | + end | ||
167 | + lat, lng = Noosfero::GeoRef.location_to_georef location | ||
168 | + end | ||
169 | + dist = params[:distance].to_f | ||
170 | + self.where "#{Noosfero::GeoRef.sql_dist lat, lng} <= #{dist}" | ||
171 | + end | ||
172 | + end | ||
136 | 173 | ||
137 | - #FIXME make this test | ||
138 | - scope :older_than, lambda { |reference_id| | ||
139 | - {:conditions => ["profiles.id < #{reference_id}"]} | ||
140 | - } | 174 | + include TimeScopes |
141 | 175 | ||
142 | def members | 176 | def members |
143 | scopes = plugins.dispatch_scopes(:organization_members, self) | 177 | scopes = plugins.dispatch_scopes(:organization_members, self) |
@@ -170,9 +204,9 @@ class Profile < ActiveRecord::Base | @@ -170,9 +204,9 @@ class Profile < ActiveRecord::Base | ||
170 | Profile.column_names.map{|n| [Profile.table_name, n].join('.')}.join(',') | 204 | Profile.column_names.map{|n| [Profile.table_name, n].join('.')}.join(',') |
171 | end | 205 | end |
172 | 206 | ||
173 | - scope :visible, :conditions => { :visible => true } | 207 | + scope :visible, :conditions => { :visible => true, :secret => false } |
174 | scope :disabled, :conditions => { :visible => false } | 208 | scope :disabled, :conditions => { :visible => false } |
175 | - scope :public, :conditions => { :visible => true, :public_profile => true } | 209 | + scope :public, :conditions => { :visible => true, :public_profile => true, :secret => false } |
176 | scope :enabled, :conditions => { :enabled => true } | 210 | scope :enabled, :conditions => { :enabled => true } |
177 | 211 | ||
178 | # Subclasses must override this method | 212 | # Subclasses must override this method |
@@ -219,6 +253,7 @@ class Profile < ActiveRecord::Base | @@ -219,6 +253,7 @@ class Profile < ActiveRecord::Base | ||
219 | settings_items :description | 253 | settings_items :description |
220 | settings_items :fields_privacy, :type => :hash, :default => {} | 254 | settings_items :fields_privacy, :type => :hash, :default => {} |
221 | settings_items :email_suggestions, :type => :boolean, :default => false | 255 | settings_items :email_suggestions, :type => :boolean, :default => false |
256 | + settings_items :administrator_mail_notification, :type => :boolean, :default => true | ||
222 | 257 | ||
223 | validates_length_of :description, :maximum => 550, :allow_nil => true | 258 | validates_length_of :description, :maximum => 550, :allow_nil => true |
224 | 259 | ||
@@ -658,15 +693,15 @@ private :generate_url, :url_options | @@ -658,15 +693,15 @@ private :generate_url, :url_options | ||
658 | after_create :insert_default_article_set | 693 | after_create :insert_default_article_set |
659 | def insert_default_article_set | 694 | def insert_default_article_set |
660 | if template | 695 | if template |
661 | - copy_articles_from template | 696 | + self.save! if copy_articles_from template |
662 | else | 697 | else |
663 | default_set_of_articles.each do |article| | 698 | default_set_of_articles.each do |article| |
664 | article.profile = self | 699 | article.profile = self |
665 | article.advertise = false | 700 | article.advertise = false |
666 | article.save! | 701 | article.save! |
667 | end | 702 | end |
703 | + self.save! | ||
668 | end | 704 | end |
669 | - self.save! | ||
670 | end | 705 | end |
671 | 706 | ||
672 | # Override this method in subclasses of Profile to create a default article | 707 | # Override this method in subclasses of Profile to create a default article |
@@ -687,10 +722,12 @@ private :generate_url, :url_options | @@ -687,10 +722,12 @@ private :generate_url, :url_options | ||
687 | end | 722 | end |
688 | 723 | ||
689 | def copy_articles_from other | 724 | def copy_articles_from other |
725 | + return false if other.top_level_articles.empty? | ||
690 | other.top_level_articles.each do |a| | 726 | other.top_level_articles.each do |a| |
691 | copy_article_tree a | 727 | copy_article_tree a |
692 | end | 728 | end |
693 | self.articles.reload | 729 | self.articles.reload |
730 | + true | ||
694 | end | 731 | end |
695 | 732 | ||
696 | def copy_article_tree(article, parent=nil) | 733 | def copy_article_tree(article, parent=nil) |
@@ -735,6 +772,29 @@ private :generate_url, :url_options | @@ -735,6 +772,29 @@ private :generate_url, :url_options | ||
735 | end | 772 | end |
736 | end | 773 | end |
737 | 774 | ||
775 | + # Adds many people to profile by id's | ||
776 | + def add_members_by_id(people_ids) | ||
777 | + | ||
778 | + unless people_ids.nil? && people_ids.empty? | ||
779 | + | ||
780 | + people = Person.where(id: people_ids) | ||
781 | + people.each do |person| | ||
782 | + | ||
783 | + add_member(person) unless person.is_member_of?(self) | ||
784 | + end | ||
785 | + end | ||
786 | + end | ||
787 | + | ||
788 | + # Adds many people to profile by email's | ||
789 | + def add_members_by_email(people_emails) | ||
790 | + | ||
791 | + people = User.where(email: people_emails) | ||
792 | + people.each do |user| | ||
793 | + | ||
794 | + add_member(user.person) unless user.person.is_member_of?(self) | ||
795 | + end | ||
796 | + end | ||
797 | + | ||
738 | def remove_member(person) | 798 | def remove_member(person) |
739 | self.disaffiliate(person, Profile::Roles.all_roles(environment.id)) | 799 | self.disaffiliate(person, Profile::Roles.all_roles(environment.id)) |
740 | end | 800 | end |
app/models/qualifier.rb
@@ -11,6 +11,12 @@ class Qualifier < ActiveRecord::Base | @@ -11,6 +11,12 @@ class Qualifier < ActiveRecord::Base | ||
11 | has_many :qualifier_certifiers, :dependent => :destroy | 11 | has_many :qualifier_certifiers, :dependent => :destroy |
12 | has_many :certifiers, :through => :qualifier_certifiers | 12 | has_many :certifiers, :through => :qualifier_certifiers |
13 | 13 | ||
14 | + def used_certs | ||
15 | + Certifier.joins('INNER JOIN product_qualifiers' + | ||
16 | + ' ON certifiers.id = product_qualifiers.certifier_id') | ||
17 | + .where(product_qualifiers: {qualifier_id: self.id}) | ||
18 | + end | ||
19 | + | ||
14 | has_many :product_qualifiers, :dependent => :destroy | 20 | has_many :product_qualifiers, :dependent => :destroy |
15 | has_many :products, :through => :product_qualifiers, :source => :product | 21 | has_many :products, :through => :product_qualifiers, :source => :product |
16 | 22 |
app/models/suggest_article.rb
@@ -41,6 +41,7 @@ class SuggestArticle < Task | @@ -41,6 +41,7 @@ class SuggestArticle < Task | ||
41 | return type if type < Article | 41 | return type if type < Article |
42 | end | 42 | end |
43 | TinyMceArticle | 43 | TinyMceArticle |
44 | + (article[:type] || 'TinyMceArticle').constantize | ||
44 | end | 45 | end |
45 | 46 | ||
46 | def perform | 47 | def perform |
@@ -91,4 +92,5 @@ class SuggestArticle < Task | @@ -91,4 +92,5 @@ class SuggestArticle < Task | ||
91 | def after_ham! | 92 | def after_ham! |
92 | self.delay.marked_as_ham | 93 | self.delay.marked_as_ham |
93 | end | 94 | end |
95 | + | ||
94 | end | 96 | end |
app/models/task.rb
@@ -70,14 +70,28 @@ class Task < ActiveRecord::Base | @@ -70,14 +70,28 @@ class Task < ActiveRecord::Base | ||
70 | begin | 70 | begin |
71 | target_msg = task.target_notification_message | 71 | target_msg = task.target_notification_message |
72 | if target_msg && task.target && !task.target.notification_emails.empty? | 72 | if target_msg && task.target && !task.target.notification_emails.empty? |
73 | - TaskMailer.target_notification(task, target_msg).deliver | 73 | + if target_profile_accepts_notification?(task) |
74 | + TaskMailer.target_notification(task, target_msg).deliver | ||
75 | + end | ||
74 | end | 76 | end |
75 | - rescue NotImplementedError => ex | 77 | + rescue Exception => ex |
76 | Rails.logger.info ex.to_s | 78 | Rails.logger.info ex.to_s |
77 | end | 79 | end |
78 | end | 80 | end |
79 | end | 81 | end |
80 | 82 | ||
83 | + def target_profile_accepts_notification?(task) | ||
84 | + if target_is_profile?(task) | ||
85 | + return task.target.administrator_mail_notification | ||
86 | + else | ||
87 | + true | ||
88 | + end | ||
89 | + end | ||
90 | + | ||
91 | + def target_is_profile?(task) | ||
92 | + task.target.kind_of? Profile | ||
93 | + end | ||
94 | + | ||
81 | # this method finished the task. It calls #perform, which must be overriden | 95 | # this method finished the task. It calls #perform, which must be overriden |
82 | # by subclasses. At the end a message (as returned by #finish_message) is | 96 | # by subclasses. At the end a message (as returned by #finish_message) is |
83 | # sent to the requestor with #notify_requestor. | 97 | # sent to the requestor with #notify_requestor. |
@@ -119,6 +133,51 @@ class Task < ActiveRecord::Base | @@ -119,6 +133,51 @@ class Task < ActiveRecord::Base | ||
119 | end | 133 | end |
120 | end | 134 | end |
121 | 135 | ||
136 | + class KindOfValidator < ActiveModel::EachValidator | ||
137 | + def validate_each(record, attribute, value) | ||
138 | + environment = record.environment || Environment.default | ||
139 | + klass = options[:kind] | ||
140 | + group = klass.to_s.downcase.pluralize | ||
141 | + id = attribute.to_s + "_id" | ||
142 | + if environment.respond_to?(group) | ||
143 | + attrb = value || environment.send(group).find_by_id(record.send(id)) | ||
144 | + else | ||
145 | + attrb = value || klass.find_by_id(record.send(id)) | ||
146 | + end | ||
147 | + if attrb.respond_to?(klass.to_s.downcase + "?") | ||
148 | + unless attrb.send(klass.to_s.downcase + "?") | ||
149 | + record.errors[attribute] << (options[:message] || "should be "+ klass.to_s.downcase) | ||
150 | + end | ||
151 | + else | ||
152 | + unless attrb.class == klass | ||
153 | + record.errors[attribute] << (options[:message] || "should be "+ klass.to_s.downcase) | ||
154 | + end | ||
155 | + end | ||
156 | + end | ||
157 | + end | ||
158 | + | ||
159 | + def requestor_is_of_kind(klass, message = nil) | ||
160 | + error_message = message ||= _('Task requestor must be '+klass.to_s.downcase) | ||
161 | + group = klass.to_s.downcase.pluralize | ||
162 | + if environment.respond_to?(group) and requestor_id | ||
163 | + requestor = requestor ||= environment.send(klass.to_s.downcase.pluralize).find_by_id(requestor_id) | ||
164 | + end | ||
165 | + unless requestor.class == klass | ||
166 | + errors.add(error_message) | ||
167 | + end | ||
168 | + end | ||
169 | + | ||
170 | + def target_is_of_kind(klass, message = nil) | ||
171 | + error_message = message ||= _('Task target must be '+klass.to_s.downcase) | ||
172 | + group = klass.to_s.downcase.pluralize | ||
173 | + if environment.respond_to?(group) and target_id | ||
174 | + target = target ||= environment.send(klass.to_s.downcase.pluralize).find_by_id(target_id) | ||
175 | + end | ||
176 | + unless target.class == klass | ||
177 | + errors.add(error_message) | ||
178 | + end | ||
179 | + end | ||
180 | + | ||
122 | def close(status, closed_by) | 181 | def close(status, closed_by) |
123 | self.status = status | 182 | self.status = status |
124 | self.end_date = Time.now | 183 | self.end_date = Time.now |
@@ -256,7 +315,7 @@ class Task < ActiveRecord::Base | @@ -256,7 +315,7 @@ class Task < ActiveRecord::Base | ||
256 | scope :canceled, :conditions => { :status => Task::Status::CANCELLED } | 315 | scope :canceled, :conditions => { :status => Task::Status::CANCELLED } |
257 | scope :closed, :conditions => { :status => [Task::Status::CANCELLED, Task::Status::FINISHED] } | 316 | scope :closed, :conditions => { :status => [Task::Status::CANCELLED, Task::Status::FINISHED] } |
258 | scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } | 317 | scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } |
259 | - scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } | 318 | + scope :of, lambda { |type| conditions = type ? "tasks.type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } |
260 | scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } | 319 | scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } |
261 | scope :like, lambda { |field, value| where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value} | 320 | scope :like, lambda { |field, value| where("LOWER(#{field}) LIKE ?", "%#{value.downcase}%") if value} |
262 | scope :pending_all, lambda { |profile, filter_type, filter_text| | 321 | scope :pending_all, lambda { |profile, filter_type, filter_text| |
@@ -278,6 +337,10 @@ class Task < ActiveRecord::Base | @@ -278,6 +337,10 @@ class Task < ActiveRecord::Base | ||
278 | Task.to(profile).pending.select('distinct type').map { |t| [t.class.name, t.title] } | 337 | Task.to(profile).pending.select('distinct type').map { |t| [t.class.name, t.title] } |
279 | end | 338 | end |
280 | 339 | ||
340 | + def self.closed_types_for(profile) | ||
341 | + Task.to(profile).closed.select('distinct type').map { |t| [t.class.name, t.title] } | ||
342 | + end | ||
343 | + | ||
281 | def opened? | 344 | def opened? |
282 | status == Task::Status::ACTIVE || status == Task::Status::HIDDEN | 345 | status == Task::Status::ACTIVE || status == Task::Status::HIDDEN |
283 | end | 346 | end |
app/models/user.rb
@@ -34,6 +34,14 @@ class User < ActiveRecord::Base | @@ -34,6 +34,14 @@ class User < ActiveRecord::Base | ||
34 | alias_method_chain :human_attribute_name, :customization | 34 | alias_method_chain :human_attribute_name, :customization |
35 | end | 35 | end |
36 | 36 | ||
37 | + def self.build(user_data, person_data, environment) | ||
38 | + user = User.new(user_data) | ||
39 | + user.terms_of_use = environment.terms_of_use | ||
40 | + user.environment = environment | ||
41 | + user.person_data = person_data | ||
42 | + user | ||
43 | + end | ||
44 | + | ||
37 | before_create do |user| | 45 | before_create do |user| |
38 | if user.environment.nil? | 46 | if user.environment.nil? |
39 | user.environment = Environment.default | 47 | user.environment = Environment.default |
@@ -68,7 +76,8 @@ class User < ActiveRecord::Base | @@ -68,7 +76,8 @@ class User < ActiveRecord::Base | ||
68 | 76 | ||
69 | attr_writer :person_data | 77 | attr_writer :person_data |
70 | def person_data | 78 | def person_data |
71 | - @person_data || {} | 79 | + @person_data = {} if @person_data.nil? |
80 | + @person_data | ||
72 | end | 81 | end |
73 | 82 | ||
74 | def email_domain | 83 | def email_domain |
@@ -93,18 +102,20 @@ class User < ActiveRecord::Base | @@ -93,18 +102,20 @@ class User < ActiveRecord::Base | ||
93 | # Virtual attribute for the unencrypted password | 102 | # Virtual attribute for the unencrypted password |
94 | attr_accessor :password, :name | 103 | attr_accessor :password, :name |
95 | 104 | ||
96 | - validates_presence_of :login, :email | ||
97 | - validates_format_of :login, :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?}) | 105 | + validates_presence_of :login |
106 | + validates_presence_of :email | ||
107 | + validates_format_of :login, :message => _('incorrect format'), :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?}) | ||
98 | validates_presence_of :password, :if => :password_required? | 108 | validates_presence_of :password, :if => :password_required? |
99 | validates_presence_of :password_confirmation, :if => :password_required? | 109 | validates_presence_of :password_confirmation, :if => :password_required? |
100 | - validates_length_of :password, :within => 4..40, :if => :password_required? | 110 | + validates_length_of :password, :message => _('length must be within 4 to 40 characters'), :within => 4..40, :if => :password_required? |
101 | validates_confirmation_of :password, :if => :password_required? | 111 | validates_confirmation_of :password, :if => :password_required? |
102 | - validates_length_of :login, :within => 2..40, :if => (lambda {|user| !user.login.blank?}) | ||
103 | - validates_length_of :email, :within => 3..100, :if => (lambda {|user| !user.email.blank?}) | ||
104 | - validates_uniqueness_of :login, :email, :case_sensitive => false, :scope => :environment_id | 112 | + validates_length_of :login, :message => _('length must be within 2 to 40 characters'), :within => 2..40, :if => (lambda {|user| !user.login.blank?}) |
113 | + validates_length_of :email, :message => _('length must be within 3 to 100 characters'),:within => 3..100, :if => (lambda {|user| !user.email.blank?}) | ||
114 | + validates_uniqueness_of :login, :case_sensitive => false, :scope => :environment_id | ||
115 | + validates_uniqueness_of :email, :case_sensitive => false, :scope => :environment_id | ||
105 | before_save :encrypt_password | 116 | before_save :encrypt_password |
106 | before_save :normalize_email, if: proc{ |u| u.email.present? } | 117 | before_save :normalize_email, if: proc{ |u| u.email.present? } |
107 | - validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?}) | 118 | + validates_format_of :email, :message => _('incorrect format'), :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?}) |
108 | 119 | ||
109 | validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('{fn} must be checked in order to signup.').fix_i18n | 120 | validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('{fn} must be checked in order to signup.').fix_i18n |
110 | 121 | ||
@@ -120,16 +131,15 @@ class User < ActiveRecord::Base | @@ -120,16 +131,15 @@ class User < ActiveRecord::Base | ||
120 | self.update_attribute :last_login_at, Time.now | 131 | self.update_attribute :last_login_at, Time.now |
121 | end | 132 | end |
122 | 133 | ||
123 | - #FIXME make this test | ||
124 | def generate_private_token! | 134 | def generate_private_token! |
125 | self.private_token = SecureRandom.hex | 135 | self.private_token = SecureRandom.hex |
126 | self.private_token_generated_at = DateTime.now | 136 | self.private_token_generated_at = DateTime.now |
127 | save(:validate => false) | 137 | save(:validate => false) |
128 | end | 138 | end |
129 | 139 | ||
130 | - #FIXME make this test | 140 | + TOKEN_VALIDITY = 2.weeks |
131 | def private_token_expired? | 141 | def private_token_expired? |
132 | - self.generate_private_token! if self.private_token.nil? || (self.private_token_generated_at + 2.weeks < DateTime.now) | 142 | + self.private_token.nil? || (self.private_token_generated_at + TOKEN_VALIDITY < DateTime.now) |
133 | end | 143 | end |
134 | 144 | ||
135 | # Activates the user in the database. | 145 | # Activates the user in the database. |
@@ -333,6 +343,8 @@ class User < ActiveRecord::Base | @@ -333,6 +343,8 @@ class User < ActiveRecord::Base | ||
333 | 343 | ||
334 | { | 344 | { |
335 | 'login' => self.login, | 345 | 'login' => self.login, |
346 | + 'name' => self.person.name, | ||
347 | + 'email' => self.email, | ||
336 | 'avatar' => self.person.profile_custom_icon(gravatar_default), | 348 | 'avatar' => self.person.profile_custom_icon(gravatar_default), |
337 | 'is_admin' => self.person.is_admin?, | 349 | 'is_admin' => self.person.is_admin?, |
338 | 'since_month' => self.person.created_at.month, | 350 | 'since_month' => self.person.created_at.month, |
@@ -354,19 +366,6 @@ class User < ActiveRecord::Base | @@ -354,19 +366,6 @@ class User < ActiveRecord::Base | ||
354 | @is_password_required = false | 366 | @is_password_required = false |
355 | end | 367 | end |
356 | 368 | ||
357 | - #FIXME make this test | ||
358 | - def generate_private_token! | ||
359 | - self.private_token = SecureRandom.hex | ||
360 | - self.private_token_generated_at = DateTime.now | ||
361 | - save(:validate => false) | ||
362 | - end | ||
363 | - | ||
364 | - #FIXME make this test | ||
365 | - def private_token_expired? | ||
366 | - self.generate_private_token! if self.private_token.nil? || (self.private_token_generated_at + 2.weeks < DateTime.now) | ||
367 | - end | ||
368 | - | ||
369 | - | ||
370 | protected | 369 | protected |
371 | 370 | ||
372 | def normalize_email | 371 | def normalize_email |
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +<h1>EndPoints</h1> | ||
2 | + | ||
3 | +<div style="float: right"> | ||
4 | +<%= s_('api-playground|Try the %s') % link_to('API Playground', '/api/playground') %> | ||
5 | +</div> | ||
6 | + | ||
7 | +<%= endpoints.map do |endpoint| | ||
8 | + app = endpoint.options[:app].to_s | ||
9 | + unless app.blank? | ||
10 | + content_tag(:h2, app.split('::').last.to_s, title: app) + | ||
11 | + (content_tag :ul do | ||
12 | + endpoint.routes.map do |route| | ||
13 | + content_tag :li do | ||
14 | + content_tag(:strong, route.route_method) + ' ' + | ||
15 | + route.route_path.gsub(':version', content_tag(:b, route.route_version)) | ||
16 | + end | ||
17 | + end.join "\n" | ||
18 | + end) | ||
19 | + end | ||
20 | +end.join "\n" %> |
@@ -0,0 +1,36 @@ | @@ -0,0 +1,36 @@ | ||
1 | +<h1>API Playground</h1> | ||
2 | + | ||
3 | +<script> | ||
4 | +var endpoints = <%= | ||
5 | +endpoints.map do |endpoint| | ||
6 | + app = endpoint.options[:app].to_s | ||
7 | + unless app.blank? | ||
8 | + endpoint.routes.map do |route| | ||
9 | + { | ||
10 | + method: route.route_method, | ||
11 | + path: route.route_path.gsub(':version', route.route_version).split('(').first | ||
12 | + } | ||
13 | + end | ||
14 | + end | ||
15 | +end.flatten.compact.sort{|a,b| | ||
16 | + a[:path]=='/api/v1/login' ? -1 : | ||
17 | + b[:path]=='/api/v1/login' ? 1 : | ||
18 | + a[:path] <=> b[:path] | ||
19 | +}.to_json %>; | ||
20 | +</script> | ||
21 | + | ||
22 | +<form id="api-form"> | ||
23 | + <label id="endpoint">Endpoint: | ||
24 | + <select name="endpoint" onchange="playground.selEndpoint()"></select> | ||
25 | + </label> | ||
26 | + <label id="api-token">Token: | ||
27 | + <%= tag :input, size: 32, placeholder: _('Use the login endpoint') %> | ||
28 | + </label> | ||
29 | +</form> | ||
30 | +<div id="playground-ctrl"> | ||
31 | + <button onclick="playground.addFormParam()"><%=_('Add parameter')%></button> | ||
32 | + <button onclick="playground.run()" style="font-weight:bold"> <%=_('Run')%> </button> | ||
33 | +</div> | ||
34 | +<pre id="api-response" class="empty"></pre> | ||
35 | + | ||
36 | +<script src="/javascripts/api-playground.js"></script> |
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +<% if @page.belongs_to_blog? || @page.belongs_to_forum?%> | ||
2 | + <h1 class="title"> | ||
3 | + <% if no_link %> | ||
4 | + <%= h(@page.title) %> | ||
5 | + <% else %> | ||
6 | + <%= link_to(@page.name, @page.url) %> | ||
7 | + <% end %> | ||
8 | + </h1> | ||
9 | + <%= render :partial => "publishing_info" %> | ||
10 | + <% unless @page.abstract.blank? %> | ||
11 | + <div class="preview"> | ||
12 | + <%= @page.lead %> | ||
13 | + </div> | ||
14 | + <% end %> | ||
15 | +<% else %> | ||
16 | + <h1 class="title"> | ||
17 | + <%= h(@page.title) %> | ||
18 | + </h1> | ||
19 | + <%= render :partial => "publishing_info" %> | ||
20 | +<% end %> |
app/views/content_viewer/_article_toolbar.html.erb
@@ -64,7 +64,7 @@ | @@ -64,7 +64,7 @@ | ||
64 | <% end %> | 64 | <% end %> |
65 | <%= link_to(image_tag('/images/icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %> | 65 | <%= link_to(image_tag('/images/icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %> |
66 | <%= @plugins.dispatch(:article_header_extra_contents, @page).collect { |content| instance_exec(&content) }.join("") %> | 66 | <%= @plugins.dispatch(:article_header_extra_contents, @page).collect { |content| instance_exec(&content) }.join("") %> |
67 | - <%= article_title(@page, :no_link => true) %> | 67 | + <%= render :partial => 'article_title', :locals => {:no_link => true} %> |
68 | <%= article_translations(@page) %> | 68 | <%= article_translations(@page) %> |
69 | </div> | 69 | </div> |
70 | </div> | 70 | </div> |
app/views/content_viewer/_display_compact_format.html.erb
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +<span class="publishing-info"> | ||
2 | + <span class="date"> | ||
3 | + <%= show_date(@page.published_at) %> | ||
4 | + </span> | ||
5 | + <span class="author"> | ||
6 | + <%= _(", by %s") % (@page.author ? link_to(@page.author_name, @page.author_url) : @page.author_name) %> | ||
7 | + </span> | ||
8 | +<% unless @no_comments %> | ||
9 | + <span class="comments"> | ||
10 | + <%= (" - %s") % link_to_comments(@page)%> | ||
11 | + </span> | ||
12 | +<% end %> | ||
13 | +</span> | ||
14 | + | ||
15 | +<% if @page.display_hits? || @page.license.present? %> | ||
16 | + <div id='article-sub-header'> | ||
17 | + <% if @page.display_hits? %> | ||
18 | + <div id="article-hits"> | ||
19 | + <%= n_('Viewed one time', 'Viewed %{num} times', @page.hits) % { :num => @page.hits } %> | ||
20 | + </div> | ||
21 | + <% end %> | ||
22 | + | ||
23 | + <% if @page.license.present? %> | ||
24 | + <div id="article-license"> | ||
25 | + <%= _('Licensed under %s') % (@page.license.url.present? ? link_to(@page.license.name, @page.license.url, :target => '_blank') : @page.license.name) %> | ||
26 | + </div> | ||
27 | + <% end %> | ||
28 | + </div> | ||
29 | +<% end %> |
@@ -0,0 +1,41 @@ | @@ -0,0 +1,41 @@ | ||
1 | +<div class="event-card"> | ||
2 | + <div class="event-image"> | ||
3 | + <% if event.image %> | ||
4 | + <%= image_tag(event.image.public_filename(:big)) %> | ||
5 | + <% end %> | ||
6 | + </div> | ||
7 | + <div class="about-event"> | ||
8 | + <% if event.start_date.present? || event.end_date.present? %> | ||
9 | + <span class="event-date"> | ||
10 | + <%= show_period(event.start_date, event.end_date) %> | ||
11 | + </span> | ||
12 | + <% end %> | ||
13 | + <% if event.link.present? %> | ||
14 | + <span class="event-link"> | ||
15 | + <%= link_to event.link, event.link %> | ||
16 | + </span> | ||
17 | + <% end %> | ||
18 | + <% if event.address.present? %> | ||
19 | + <span class="event-address"> | ||
20 | + <span> | ||
21 | + <%= event.address %> | ||
22 | + </span> | ||
23 | + </span> | ||
24 | + <% end %> | ||
25 | + </div> | ||
26 | +</div> | ||
27 | + | ||
28 | +<div class="event-body"> | ||
29 | + <% if format == 'short' %> | ||
30 | + <%= display_short_format event, :comments_link => false, :read_more_link => false %> | ||
31 | + <% else %> | ||
32 | + <% unless event.abstract.blank? %> | ||
33 | + <div class="event-lead"> | ||
34 | + <%= event.article_lead %> | ||
35 | + </div> | ||
36 | + <% end %> | ||
37 | + <div class="event-content"> | ||
38 | + <%= event.body %> | ||
39 | + </div> | ||
40 | + <% end %> | ||
41 | +</div> |
app/views/content_viewer/view_page.html.erb
@@ -24,22 +24,6 @@ | @@ -24,22 +24,6 @@ | ||
24 | <%= render :partial => 'article_toolbar' %> | 24 | <%= render :partial => 'article_toolbar' %> |
25 | </div> | 25 | </div> |
26 | 26 | ||
27 | -<% if @page.display_hits? || @page.license.present? %> | ||
28 | - <div id='article-sub-header'> | ||
29 | - <% if @page.display_hits? %> | ||
30 | - <div id="article-hits"> | ||
31 | - <%= n_('Viewed one time', 'Viewed %{num} times', @page.hits) % { :num => @page.hits } %> | ||
32 | - </div> | ||
33 | - <% end %> | ||
34 | - | ||
35 | - <% if @page.license.present? %> | ||
36 | - <div id="article-license"> | ||
37 | - <%= _('Licensed under %s') % (@page.license.url.present? ? link_to(@page.license.name, @page.license.url, :target => '_blank') : @page.license.name) %> | ||
38 | - </div> | ||
39 | - <% end %> | ||
40 | - </div> | ||
41 | -<% end %> | ||
42 | - | ||
43 | <% if NOOSFERO_CONF['addthis_enabled'] %> | 27 | <% if NOOSFERO_CONF['addthis_enabled'] %> |
44 | <%= render :partial => 'addthis' %> | 28 | <%= render :partial => 'addthis' %> |
45 | <% end %> | 29 | <% end %> |
@@ -47,6 +31,12 @@ | @@ -47,6 +31,12 @@ | ||
47 | <% cache(@page.cache_key(params, user, language)) do %> | 31 | <% cache(@page.cache_key(params, user, language)) do %> |
48 | <div class="<%="article-body article-body-" + @page.css_class_name %>"> | 32 | <div class="<%="article-body article-body-" + @page.css_class_name %>"> |
49 | <% options = @page.image? ? {:gallery_view => true} : {} %> | 33 | <% options = @page.image? ? {:gallery_view => true} : {} %> |
34 | + <% if @page.image.present? && !@page.event? %> | ||
35 | + <div class="article-body-img"> | ||
36 | + <%= image_tag(@page.image.public_filename) %> | ||
37 | + <p><%= @page.image.label%></p> | ||
38 | + </div> | ||
39 | + <% end %> | ||
50 | <%= article_to_html(@page, options) %> | 40 | <%= article_to_html(@page, options) %> |
51 | <br style="clear:both" /> | 41 | <br style="clear:both" /> |
52 | </div> <!-- end class="article-body" --> | 42 | </div> <!-- end class="article-body" --> |
app/views/email_templates/edit.html.erb
app/views/email_templates/new.html.erb
app/views/layouts/application-ng.html.erb
@@ -27,6 +27,7 @@ | @@ -27,6 +27,7 @@ | ||
27 | 27 | ||
28 | <script type="text/javascript"> | 28 | <script type="text/javascript"> |
29 | DEFAULT_LOADING_MESSAGE = <%="'#{ _('loading...') }'" %>; | 29 | DEFAULT_LOADING_MESSAGE = <%="'#{ _('loading...') }'" %>; |
30 | + noosfero.profile = <%= (@profile.identifier if @profile).to_json %> | ||
30 | </script> | 31 | </script> |
31 | 32 | ||
32 | </head> | 33 | </head> |
app/views/manage_products/_categories_autocomplete.html.erb
1 | <%= text_field_tag 'product_category_id', '', :placeholder => _('type a category for the product') %> | 1 | <%= text_field_tag 'product_category_id', '', :placeholder => _('type a category for the product') %> |
2 | 2 | ||
3 | <%= javascript_include_tag '/javascripts/product_categories.js' %> | 3 | <%= javascript_include_tag '/javascripts/product_categories.js' %> |
4 | -<% javascript_tag do %> | 4 | +<%= javascript_tag do %> |
5 | product_categories.autocomplete.search_url = <%= url_for(:controller => :manage_products, :action => :search_categories).to_json %> | 5 | product_categories.autocomplete.search_url = <%= url_for(:controller => :manage_products, :action => :search_categories).to_json %> |
6 | product_categories.autocomplete.select_url = <%= url_for(:controller => :manage_products, :action => :show_category_tree).to_json %> | 6 | product_categories.autocomplete.select_url = <%= url_for(:controller => :manage_products, :action => :show_category_tree).to_json %> |
7 | product_categories.autocomplete.load('#product_category_id') | 7 | product_categories.autocomplete.load('#product_category_id') |
app/views/profile_editor/_moderation.html.erb
1 | <h2><%= _('Moderation options') %></h2> | 1 | <h2><%= _('Moderation options') %></h2> |
2 | <% if profile.community? %> | 2 | <% if profile.community? %> |
3 | <div style='margin-bottom: 1em'> | 3 | <div style='margin-bottom: 1em'> |
4 | + <h4><%= _('Email Configuration:')%></h4> | ||
5 | + </div> | ||
6 | + <div style='margin-bottom: 0.5em'> | ||
7 | + <%= check_box(:profile_data, :administrator_mail_notification, :style => 'float: left') %> | ||
8 | + <div style='margin-left: 30px'> | ||
9 | + <%= _('Send administrator Email for every task (Default: yes)') %> | ||
10 | + </div> | ||
11 | + </div> | ||
12 | + | ||
13 | + <div style='margin-bottom: 1em'> | ||
4 | <h4><%= _('Invitation moderation:')%></h4> | 14 | <h4><%= _('Invitation moderation:')%></h4> |
5 | </div> | 15 | </div> |
6 | <div style='margin-bottom: 0.5em'> | 16 | <div style='margin-bottom: 0.5em'> |
app/views/shared/_change_image.html.erb
1 | - <%= i.file_field( :uploaded_data, { :onchange => 'updateImg(this.value)' } ) %> | ||
2 | - <%= button_to_function(:cancel,_('Cancel'),"jQuery('#change-image-link').show(); jQuery('#change-image').html('')", :id => 'cancel-change-image-link', :style => 'display: none')%> | 1 | +<%= i.file_field( :uploaded_data, { :onchange => 'updateImg(this.value)' } ) %> |
2 | +<%= labelled_form_field(_("Image Label:"), i.text_field(:label)) %> | ||
3 | +<%= button_to_function(:cancel,_('Cancel'),"jQuery('#change-image-link').show(); jQuery('#change-image').html('')", :id => 'cancel-change-image-link', :style => 'display: none')%> |
app/views/shared/not_found.html.erb
1 | <div id='not-found'> | 1 | <div id='not-found'> |
2 | - <h1><%= _('Nonexistent content: %s') % (content_tag('tt', @path)) %></h1> | 2 | + <h1><%= _('There is no such page: %s') % (content_tag('tt', @path)) %></h1> |
3 | <p> | 3 | <p> |
4 | <%= _('.You may have clicked an expired link or mistyped the address.') %> | 4 | <%= _('.You may have clicked an expired link or mistyped the address.') %> |
5 | - <%= _('.This page does not exist. Would you like to: Create a new conteúdoou Locate existing content.') %> | 5 | + <%= _('.This page does not exist. Would you like to: Create a new content or locate existing content.') %> |
6 | <!-- <%= _('If you clicked a link that was in another site, or was given to you by someone else, it would be nice if you tell them that their link is not valid anymore.') %> --> | 6 | <!-- <%= _('If you clicked a link that was in another site, or was given to you by someone else, it would be nice if you tell them that their link is not valid anymore.') %> --> |
7 | </p> | 7 | </p> |
8 | 8 |
app/views/tasks/_approve_article_accept_details.html.erb
1 | +<%= task_email_template(_('Select an acceptance email template:'), @acceptance_email_templates, task) %> | ||
2 | + | ||
1 | <%= render :file => 'shared/tiny_mce' %> | 3 | <%= render :file => 'shared/tiny_mce' %> |
2 | 4 | ||
3 | <%= labelled_form_field(_('Create a link'), f.check_box(:create_link)) %> | 5 | <%= labelled_form_field(_('Create a link'), f.check_box(:create_link)) %> |
app/views/tasks/_suggest_article_accept_details.html.erb
@@ -14,6 +14,5 @@ | @@ -14,6 +14,5 @@ | ||
14 | <%= labelled_form_field(_('Highlight this article'), a.check_box(:highlighted)) %> | 14 | <%= labelled_form_field(_('Highlight this article'), a.check_box(:highlighted)) %> |
15 | 15 | ||
16 | <%= a.hidden_field(:type) %> | 16 | <%= a.hidden_field(:type) %> |
17 | - | ||
18 | <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :f => a, :lead_id => task.id} %> | 17 | <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :f => a, :lead_id => task.id} %> |
19 | <% end %> | 18 | <% end %> |
app/views/tasks/_task_reject_details.html.erb
1 | -<% if @email_templates.present? %> | ||
2 | - <div class="template-selection"> | ||
3 | - <%= labelled_form_field(_('Select a rejection email template:'), select_tag("tasks[#{task.id}][task][email_template_id]", options_from_collection_for_select(@email_templates, :id, :name), :include_blank => true, 'data-url' => url_for(:controller => 'email_templates', :action => 'show_parsed'))) %> | ||
4 | - </div> | ||
5 | -<% end %> | 1 | +<%= task_email_template(_('Select a rejection email template:'), @rejection_email_templates, task) %> |
6 | 2 | ||
7 | <%= labelled_form_field(_('Rejection explanation'), f.text_area(:reject_explanation, :rows => 5)) %> | 3 | <%= labelled_form_field(_('Rejection explanation'), f.text_area(:reject_explanation, :rows => 5)) %> |
app/views/tasks/processed.html.erb
1 | +<%= stylesheet_link_tag 'tasks' %> | ||
2 | + | ||
3 | +<div class="task-processed"> | ||
1 | <h1><%= _("%s's processed tasks") % profile.name %></h1> | 4 | <h1><%= _("%s's processed tasks") % profile.name %></h1> |
2 | 5 | ||
6 | +<div class="task-processed-filter"> | ||
7 | +<% | ||
8 | + type_collection = [[nil, _('All')]] + @task_types | ||
9 | +%> | ||
10 | + <%= form_tag '#', :method => 'get' do %> | ||
11 | + <%= field_set_tag _('Filter'), :class => 'filter_fields' do %> | ||
12 | + <div> | ||
13 | + <%= labelled_select(_('Type of task')+': ', 'filter[type]', :first, :last, @filter[:type], type_collection, {:id => 'filter-type'}) %> | ||
14 | + <%= labelled_select(_('Status:'), 'filter[status]', :last, :first, @filter[:status], [[_('Any'), nil], [_(Task::Status.names[Task::Status::CANCELLED]), 2], [_(Task::Status.names[Task::Status::FINISHED]), 3] ]) %> | ||
15 | + </div> | ||
16 | + | ||
17 | + <div> | ||
18 | + <%= labelled_text_field(_('Text Filter:'), 'filter[text]', @filter[:text]) %> | ||
19 | + </div> | ||
20 | + | ||
21 | + <div> | ||
22 | + <%= labelled_text_field(_('Requestor:'), 'filter[requestor]', @filter[:requestor]) %> | ||
23 | + <%= labelled_text_field(_('Closed by:'), 'filter[closed_by]', @filter[:closed_by]) %> | ||
24 | + </div> | ||
25 | + | ||
26 | + <%= labelled_form_field(_('Creation date'), date_range_field('filter[created_from]', 'filter[created_until]', @filter[:created_from], @filter[:created_until], '%Y-%m-%d', { :change_month => true, :change_year => true, :date_format => 'yy-mm-dd' }, { :size => 14, :from_id => 'filter_created_from', :to_id => 'filter_created_until' })) %> | ||
27 | + <%= labelled_form_field(_('Processed date'), date_range_field('filter[closed_from]', 'filter[closed_until]', @filter[:closed_from], @filter[:closed_until], '%Y-%m-%d', { :change_month => true, :change_year => true, :date_format => 'yy-mm-dd' }, { :size => 14, :from_id => 'filter_closed_from', :to_id => 'filter_closed_until' })) %> | ||
28 | + | ||
29 | + <div class="actions"> | ||
30 | + <%= submit_button(:search, _('Search')) %> | ||
31 | + </div> | ||
32 | + <% end %> | ||
33 | + <% end %> | ||
34 | +</div> | ||
35 | + | ||
3 | <p> | 36 | <p> |
4 | <% if @tasks.empty? %> | 37 | <% if @tasks.empty? %> |
5 | <em><%= _('No processed tasks.') %></em> | 38 | <em><%= _('No processed tasks.') %></em> |
6 | <% else %> | 39 | <% else %> |
7 | - <ul> | 40 | + <ul class="task-list"> |
8 | <% @tasks.each do |item| %> | 41 | <% @tasks.each do |item| %> |
9 | - <li> | ||
10 | - <strong><%= task_information(item) %></strong> <br/> | ||
11 | - <small> | ||
12 | - <%= _('Created:') +' '+ show_date(item.created_at) %> | 42 | + <li class="task status-<%= item.status%>"> |
43 | + <div class="title"> | ||
44 | + <%= task_information(item) %> | ||
45 | + </div> | ||
46 | + <div class="status"> | ||
47 | + <%= _(Task::Status.names[item.status]) %> | ||
48 | + </div> | ||
49 | + <div class="dates"> | ||
50 | + <span class="created"> | ||
51 | + <span class="label"><%= _('Created:') %></span> | ||
52 | + <span class="value"><%= show_date(item.created_at) %></span> | ||
53 | + </span> | ||
13 | — | 54 | — |
14 | - <%= _('Processed:') +' '+ show_date(item.end_date) %> | ||
15 | - </small> | 55 | + <span class="processed"> |
56 | + <span class="label"><%= _('Processed:') %></span> | ||
57 | + <span class="value"><%= show_date(item.end_date) %></span> | ||
58 | + </span> | ||
59 | + </div> | ||
60 | + <% if item.closed_by.present? %> | ||
61 | + <div class="closed-by"> | ||
62 | + <span class="label"><%= _('Closed by:') %></span> | ||
63 | + <span class="value"><%= link_to(item.closed_by.name, item.closed_by.url) %></span> | ||
64 | + </div> | ||
65 | + <% end %> | ||
16 | </li> | 66 | </li> |
17 | <% end %> | 67 | <% end %> |
18 | </ul> | 68 | </ul> |
69 | + <%= pagination_links(@tasks)%> | ||
19 | <% end %> | 70 | <% end %> |
20 | </p> | 71 | </p> |
21 | 72 | ||
22 | <% button_bar do %> | 73 | <% button_bar do %> |
23 | <%= button(:back, _('Back'), :action => 'index') %> | 74 | <%= button(:back, _('Back'), :action => 'index') %> |
24 | <% end %> | 75 | <% end %> |
76 | + | ||
77 | +</div> |
config.ru
@@ -6,6 +6,13 @@ require ::File.expand_path('../config/environment', __FILE__) | @@ -6,6 +6,13 @@ require ::File.expand_path('../config/environment', __FILE__) | ||
6 | #use Rails::Rack::Static | 6 | #use Rails::Rack::Static |
7 | #run ActionController::Dispatcher.new | 7 | #run ActionController::Dispatcher.new |
8 | 8 | ||
9 | +use Rack::Cors do | ||
10 | + allow do | ||
11 | + origins '*' | ||
12 | + resource '/api/*', :headers => :any, :methods => [:get, :post] | ||
13 | + end | ||
14 | +end | ||
15 | + | ||
9 | rails_app = Rack::Builder.new do | 16 | rails_app = Rack::Builder.new do |
10 | run Noosfero::Application | 17 | run Noosfero::Application |
11 | end | 18 | end |
config/application.rb
@@ -135,12 +135,5 @@ module Noosfero | @@ -135,12 +135,5 @@ module Noosfero | ||
135 | 135 | ||
136 | Noosfero::Plugin.setup(config) | 136 | Noosfero::Plugin.setup(config) |
137 | 137 | ||
138 | - config.middleware.use Rack::Cors do | ||
139 | - allow do | ||
140 | - origins '*' | ||
141 | - resource 'api/*', :headers => :any, :methods => [:get, :post] | ||
142 | - end | ||
143 | - end | ||
144 | - | ||
145 | end | 138 | end |
146 | end | 139 | end |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +Rails.application.eager_load! |
config/locales/de.yml
@@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
3 | # contributors: | 3 | # contributors: |
4 | # - Alexander Dreher - http://github.com/alexdreher - Rails 3 update | 4 | # - Alexander Dreher - http://github.com/alexdreher - Rails 3 update |
5 | 5 | ||
6 | -de: | 6 | +de: &de |
7 | date: | 7 | date: |
8 | formats: | 8 | formats: |
9 | default: "%d.%m.%Y" | 9 | default: "%d.%m.%Y" |
@@ -221,3 +221,6 @@ de: | @@ -221,3 +221,6 @@ de: | ||
221 | 221 | ||
222 | full_messages: | 222 | full_messages: |
223 | format: "%{attribute} %{message}" | 223 | format: "%{attribute} %{message}" |
224 | + | ||
225 | +de-DE: | ||
226 | + <<: *de |
config/locales/en-US.yml
@@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
2 | # | 2 | # |
3 | # Use this as the base for the locale file of your language. | 3 | # Use this as the base for the locale file of your language. |
4 | 4 | ||
5 | -"en-US": | 5 | +"en-US": &en-US |
6 | date: | 6 | date: |
7 | formats: | 7 | formats: |
8 | default: "%Y-%m-%d" | 8 | default: "%Y-%m-%d" |
@@ -220,4 +220,7 @@ | @@ -220,4 +220,7 @@ | ||
220 | template: | 220 | template: |
221 | <<: *errors_template | 221 | <<: *errors_template |
222 | full_messages: | 222 | full_messages: |
223 | - format: "%{attribute} %{message}" | ||
224 | \ No newline at end of file | 223 | \ No newline at end of file |
224 | + format: "%{attribute} %{message}" | ||
225 | + | ||
226 | +en: | ||
227 | + <<: *en-US |
config/locales/es.yml
@@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
4 | # - Tsutomu Kuroda - http://github.com/kuroda (t-kuroda@oiax.jp) | 4 | # - Tsutomu Kuroda - http://github.com/kuroda (t-kuroda@oiax.jp) |
5 | # Corrected by Eloy Serra Labán: http://goo.gl/i9Kts, /nQ928, /XfKaX | 5 | # Corrected by Eloy Serra Labán: http://goo.gl/i9Kts, /nQ928, /XfKaX |
6 | 6 | ||
7 | -"es": | 7 | +es: &es |
8 | date: | 8 | date: |
9 | formats: | 9 | formats: |
10 | default: "%d/%m/%Y" | 10 | default: "%d/%m/%Y" |
@@ -223,3 +223,6 @@ | @@ -223,3 +223,6 @@ | ||
223 | 223 | ||
224 | full_messages: | 224 | full_messages: |
225 | format: "%{attribute} %{message}" | 225 | format: "%{attribute} %{message}" |
226 | + | ||
227 | +es_ES: | ||
228 | + <<: *es |
config/locales/fr.yml
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | # - Bruno Michel - http://github.com/nono | 5 | # - Bruno Michel - http://github.com/nono |
6 | # - Tsutomu Kuroda - http://github.com/kuroda (t-kuroda@oiax.jp) | 6 | # - Tsutomu Kuroda - http://github.com/kuroda (t-kuroda@oiax.jp) |
7 | 7 | ||
8 | -fr: | 8 | +fr: &fr |
9 | date: | 9 | date: |
10 | formats: | 10 | formats: |
11 | default: "%d/%m/%Y" | 11 | default: "%d/%m/%Y" |
@@ -222,3 +222,7 @@ fr: | @@ -222,3 +222,7 @@ fr: | ||
222 | <<: *errors_template | 222 | <<: *errors_template |
223 | full_messages: | 223 | full_messages: |
224 | format: "%{attribute} %{message}" | 224 | format: "%{attribute} %{message}" |
225 | + | ||
226 | +fr-FR: | ||
227 | + <<: *fr | ||
228 | + |
config/locales/hr.yml
config/locales/hy.yml
@@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
3 | # FIXME: This is a copy of en-US.yml. Armenian translators, please translate | 3 | # FIXME: This is a copy of en-US.yml. Armenian translators, please translate |
4 | # this into Armenian. | 4 | # this into Armenian. |
5 | 5 | ||
6 | -"hy": | 6 | +hy: &hy |
7 | date: | 7 | date: |
8 | formats: | 8 | formats: |
9 | default: "%Y-%m-%d" | 9 | default: "%Y-%m-%d" |
@@ -222,3 +222,6 @@ | @@ -222,3 +222,6 @@ | ||
222 | <<: *errors_template | 222 | <<: *errors_template |
223 | full_messages: | 223 | full_messages: |
224 | format: "%{attribute} %{message}" | 224 | format: "%{attribute} %{message}" |
225 | + | ||
226 | +hy-AM: | ||
227 | + <<: *hy |
config/locales/it.yml
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | # - Simone Carletti (weppos@weppos.net) | 5 | # - Simone Carletti (weppos@weppos.net) |
6 | # - Davide Guerri (d.guerri@caspur.it) | 6 | # - Davide Guerri (d.guerri@caspur.it) |
7 | 7 | ||
8 | -it: | 8 | +it: &it |
9 | number: | 9 | number: |
10 | format: | 10 | format: |
11 | delimiter: "," | 11 | delimiter: "," |
@@ -234,3 +234,6 @@ it: | @@ -234,3 +234,6 @@ it: | ||
234 | 234 | ||
235 | full_messages: | 235 | full_messages: |
236 | format: "%{attribute} %{message}" | 236 | format: "%{attribute} %{message}" |
237 | + | ||
238 | +it_IT: | ||
239 | + <<: *it |
config/locales/pt-BR.yml
1 | # encoding: UTF-8 | 1 | # encoding: UTF-8 |
2 | # pt-BR translations for Ruby on Rails | 2 | # pt-BR translations for Ruby on Rails |
3 | -"pt-BR": | 3 | + |
4 | +"pt-BR": &pt-BR | ||
4 | # formatos de data e hora | 5 | # formatos de data e hora |
5 | date: | 6 | date: |
6 | formats: | 7 | formats: |
@@ -231,3 +232,6 @@ | @@ -231,3 +232,6 @@ | ||
231 | 232 | ||
232 | full_messages: | 233 | full_messages: |
233 | format: "%{attribute} %{message}" | 234 | format: "%{attribute} %{message}" |
235 | + | ||
236 | +pt: | ||
237 | + <<: *pt-BR |
config/locales/pt.yml
@@ -1,233 +0,0 @@ | @@ -1,233 +0,0 @@ | ||
1 | -# encoding: UTF-8 | ||
2 | -# pt-BR translations for Ruby on Rails | ||
3 | -"pt": | ||
4 | - # formatos de data e hora | ||
5 | - date: | ||
6 | - formats: | ||
7 | - default: "%d/%m/%Y" | ||
8 | - short: "%d de %B" | ||
9 | - long: "%d de %B de %Y" | ||
10 | - | ||
11 | - day_names: | ||
12 | - - Domingo | ||
13 | - - Segunda | ||
14 | - - Terça | ||
15 | - - Quarta | ||
16 | - - Quinta | ||
17 | - - Sexta | ||
18 | - - Sábado | ||
19 | - abbr_day_names: | ||
20 | - - Dom | ||
21 | - - Seg | ||
22 | - - Ter | ||
23 | - - Qua | ||
24 | - - Qui | ||
25 | - - Sex | ||
26 | - - Sáb | ||
27 | - | ||
28 | - month_names: | ||
29 | - - ~ | ||
30 | - - Janeiro | ||
31 | - - Fevereiro | ||
32 | - - Março | ||
33 | - - Abril | ||
34 | - - Maio | ||
35 | - - Junho | ||
36 | - - Julho | ||
37 | - - Agosto | ||
38 | - - Setembro | ||
39 | - - Outubro | ||
40 | - - Novembro | ||
41 | - - Dezembro | ||
42 | - abbr_month_names: | ||
43 | - - ~ | ||
44 | - - Jan | ||
45 | - - Fev | ||
46 | - - Mar | ||
47 | - - Abr | ||
48 | - - Mai | ||
49 | - - Jun | ||
50 | - - Jul | ||
51 | - - Ago | ||
52 | - - Set | ||
53 | - - Out | ||
54 | - - Nov | ||
55 | - - Dez | ||
56 | - order: | ||
57 | - - :day | ||
58 | - - :month | ||
59 | - - :year | ||
60 | - | ||
61 | - time: | ||
62 | - formats: | ||
63 | - default: "%A, %d de %B de %Y, %H:%M h" | ||
64 | - short: "%d/%m, %H:%M h" | ||
65 | - long: "%A, %d de %B de %Y, %H:%M h" | ||
66 | - am: '' | ||
67 | - pm: '' | ||
68 | - | ||
69 | - # Usado no Array.to_sentence | ||
70 | - support: | ||
71 | - array: | ||
72 | - words_connector: ", " | ||
73 | - two_words_connector: " e " | ||
74 | - last_word_connector: " e " | ||
75 | - | ||
76 | - select: | ||
77 | - prompt: "Por favor selecione" | ||
78 | - | ||
79 | - number: | ||
80 | - format: | ||
81 | - separator: ',' | ||
82 | - delimiter: '.' | ||
83 | - precision: 3 | ||
84 | - significant: false | ||
85 | - strip_insignificant_zeros: false | ||
86 | - | ||
87 | - currency: | ||
88 | - format: | ||
89 | - format: '%u %n' | ||
90 | - unit: 'R$' | ||
91 | - separator: ',' | ||
92 | - delimiter: '.' | ||
93 | - precision: 2 | ||
94 | - significant: false | ||
95 | - strip_insignificant_zeros: false | ||
96 | - | ||
97 | - percentage: | ||
98 | - format: | ||
99 | - delimiter: '.' | ||
100 | - | ||
101 | - precision: | ||
102 | - format: | ||
103 | - delimiter: '.' | ||
104 | - | ||
105 | - human: | ||
106 | - format: | ||
107 | - delimiter: '.' | ||
108 | - precision: 2 | ||
109 | - significant: true | ||
110 | - strip_insignificant_zeros: true | ||
111 | - storage_units: | ||
112 | - format: "%n %u" | ||
113 | - units: | ||
114 | - byte: | ||
115 | - one: "Byte" | ||
116 | - other: "Bytes" | ||
117 | - kb: "KB" | ||
118 | - mb: "MB" | ||
119 | - gb: "GB" | ||
120 | - tb: "TB" | ||
121 | - # number_to_human() | ||
122 | - # new in rails 3: please add to other locales | ||
123 | - decimal_units: | ||
124 | - format: "%n %u" | ||
125 | - units: | ||
126 | - unit: "" | ||
127 | - thousand: "mil" | ||
128 | - million: | ||
129 | - one: milhão | ||
130 | - other: milhões | ||
131 | - billion: | ||
132 | - one: bilhão | ||
133 | - other: bilhões | ||
134 | - trillion: | ||
135 | - one: trilhão | ||
136 | - other: trilhões | ||
137 | - quadrillion: | ||
138 | - one: quatrilhão | ||
139 | - other: quatrilhões | ||
140 | - | ||
141 | - # distancia do tempo em palavras | ||
142 | - datetime: | ||
143 | - distance_in_words: | ||
144 | - half_a_minute: 'meio minuto' | ||
145 | - less_than_x_seconds: | ||
146 | - one: 'menos de 1 segundo' | ||
147 | - other: 'menos de %{count} segundos' | ||
148 | - x_seconds: | ||
149 | - one: '1 segundo' | ||
150 | - other: '%{count} segundos' | ||
151 | - less_than_x_minutes: | ||
152 | - one: 'menos de um minuto' | ||
153 | - other: 'menos de %{count} minutos' | ||
154 | - x_minutes: | ||
155 | - one: '1 minuto' | ||
156 | - other: '%{count} minutos' | ||
157 | - about_x_hours: | ||
158 | - one: 'aproximadamente 1 hora' | ||
159 | - other: 'aproximadamente %{count} horas' | ||
160 | - x_days: | ||
161 | - one: '1 dia' | ||
162 | - other: '%{count} dias' | ||
163 | - about_x_months: | ||
164 | - one: 'aproximadamente 1 mês' | ||
165 | - other: 'aproximadamente %{count} meses' | ||
166 | - x_months: | ||
167 | - one: '1 mês' | ||
168 | - other: '%{count} meses' | ||
169 | - about_x_years: | ||
170 | - one: 'aproximadamente 1 ano' | ||
171 | - other: 'aproximadamente %{count} anos' | ||
172 | - over_x_years: | ||
173 | - one: 'mais de 1 ano' | ||
174 | - other: 'mais de %{count} anos' | ||
175 | - almost_x_years: | ||
176 | - one: 'quase 1 ano' | ||
177 | - other: 'quase %{count} anos' | ||
178 | - prompts: | ||
179 | - year: "Ano" | ||
180 | - month: "Mês" | ||
181 | - day: "Dia" | ||
182 | - hour: "Hora" | ||
183 | - minute: "Minuto" | ||
184 | - second: "Segundo" | ||
185 | - | ||
186 | - helpers: | ||
187 | - select: | ||
188 | - prompt: "Por favor selecione" | ||
189 | - | ||
190 | - submit: | ||
191 | - create: 'Criar %{model}' | ||
192 | - update: 'Atualizar %{model}' | ||
193 | - submit: 'Salvar %{model}' | ||
194 | - | ||
195 | - errors: | ||
196 | - format: "%{attribute} %{message}" | ||
197 | - messages: &errors_messages | ||
198 | - inclusion: "não está incluído na lista" | ||
199 | - exclusion: "não está disponível" | ||
200 | - invalid: "não é válido" | ||
201 | - confirmation: "não está de acordo com a confirmação" | ||
202 | - accepted: "deve ser aceito" | ||
203 | - empty: "não pode ficar vazio" | ||
204 | - blank: "não pode ficar em branco" | ||
205 | - too_long: "é muito longo (máximo: %{count} caracteres)" | ||
206 | - too_short: "é muito curto (mínimo: %{count} caracteres)" | ||
207 | - wrong_length: "não possui o tamanho esperado (%{count} caracteres)" | ||
208 | - not_a_number: "não é um número" | ||
209 | - not_an_integer: "não é um número inteiro" | ||
210 | - greater_than: "deve ser maior que %{count}" | ||
211 | - greater_than_or_equal_to: "deve ser maior ou igual a %{count}" | ||
212 | - equal_to: "deve ser igual a %{count}" | ||
213 | - less_than: "deve ser menor que %{count}" | ||
214 | - less_than_or_equal_to: "deve ser menor ou igual a %{count}" | ||
215 | - odd: "deve ser ímpar" | ||
216 | - even: "deve ser par" | ||
217 | - taken: "já está em uso" | ||
218 | - record_invalid: "A validação falhou: %{errors}" | ||
219 | - template: &errors_template | ||
220 | - header: | ||
221 | - one: "Não foi possível gravar %{model}: 1 erro" | ||
222 | - other: "Não foi possível gravar %{model}: %{count} erros." | ||
223 | - body: "Por favor, verifique o(s) seguinte(s) campo(s):" | ||
224 | - | ||
225 | - activerecord: | ||
226 | - errors: | ||
227 | - messages: | ||
228 | - <<: *errors_messages | ||
229 | - template: | ||
230 | - <<: *errors_template | ||
231 | - | ||
232 | - full_messages: | ||
233 | - format: "%{attribute} %{message}" |
config/locales/ru.yml
@@ -10,7 +10,7 @@ | @@ -10,7 +10,7 @@ | ||
10 | # (http://github.com/yaroslav/russian). Следующие данные -- выдержка их него, чтобы | 10 | # (http://github.com/yaroslav/russian). Следующие данные -- выдержка их него, чтобы |
11 | # была возможность минимальной локализации приложения на русский язык. | 11 | # была возможность минимальной локализации приложения на русский язык. |
12 | 12 | ||
13 | -ru: | 13 | +ru: &ru |
14 | date: | 14 | date: |
15 | formats: | 15 | formats: |
16 | default: "%d.%m.%Y" | 16 | default: "%d.%m.%Y" |
@@ -300,3 +300,7 @@ ru: | @@ -300,3 +300,7 @@ ru: | ||
300 | words_connector: ", " | 300 | words_connector: ", " |
301 | two_words_connector: " и " | 301 | two_words_connector: " и " |
302 | last_word_connector: " и " | 302 | last_word_connector: " и " |
303 | + | ||
304 | +ru_RU: | ||
305 | + <<: *ru | ||
306 | + |
config/noosfero.yml.dist
@@ -13,12 +13,39 @@ development: | @@ -13,12 +13,39 @@ development: | ||
13 | exclude_profile_identifier_pattern: index(\..*)?|home(\..*)? | 13 | exclude_profile_identifier_pattern: index(\..*)?|home(\..*)? |
14 | api_recaptcha_site_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-' | 14 | api_recaptcha_site_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-' |
15 | api_recaptcha_private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY' | 15 | api_recaptcha_private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY' |
16 | - api_recaptcha_verify_uri: 'https://www.google.com/recaptcha/api/siteverify' | 16 | + api_recaptcha_verify_uri: 'https://www.google.com/recaptcha/api/siteverify' |
17 | + | ||
18 | +#Google Recaptcha setup | ||
19 | + api_captcha_enabled: true | ||
20 | +#noosfero.com | ||
21 | + api_recaptcha_site_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-' | ||
22 | +#noosfero.com | ||
23 | + api_recaptcha_private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY' | ||
24 | + api_recaptcha_v1_verify_uri: 'https://www.google.com/recaptcha/api/verify' | ||
25 | + api_recaptcha_v2_verify_uri: 'https://www.google.com/recaptcha/api/siteverify' | ||
26 | +# version 1 or 2 | ||
27 | + api_captcha_version: 1 | ||
17 | 28 | ||
18 | test: | 29 | test: |
30 | +#Google Recaptcha setup | ||
31 | + api_captcha_enabled: false | ||
32 | +#noosfero.com | ||
33 | + api_recaptcha_site_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-' | ||
34 | +#noosfero.com | ||
35 | + api_recaptcha_private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY' | ||
36 | + api_recaptcha_v1_verify_uri: 'https://www.google.com/recaptcha/api/verify' | ||
37 | + api_recaptcha_v2_verify_uri: 'https://www.google.com/recaptcha/api/siteverify' | ||
38 | +# version 1 or 2 | ||
39 | + api_captcha_version: 1 | ||
19 | 40 | ||
20 | production: | 41 | production: |
21 | - api_recaptcha_site_key: '6LcLPAcTAAAAAKsd0bxY_TArhD_A7OL19SRCW7_i' | ||
22 | - api_recaptcha_private_key: '6LcLPAcTAAAAAE36SN1M2w1I7Hn8upwXYZ_YQZ5-' | ||
23 | - api_recaptcha_verify_uri: 'https://www.google.com/recaptcha/api/siteverify' | ||
24 | - | ||
25 | \ No newline at end of file | 42 | \ No newline at end of file |
43 | +#Google Recaptcha setup | ||
44 | + api_captcha_enabled: true | ||
45 | +#dialoga | ||
46 | + api_recaptcha_site_key: '6LcLPAcTAAAAAKsd0bxY_TArhD_A7OL19SRCW7_i' | ||
47 | +#dialoga | ||
48 | + api_recaptcha_private_key: '6LcLPAcTAAAAAE36SN1M2w1I7Hn8upwXYZ_YQZ5-' | ||
49 | + api_recaptcha_v1_verify_uri: 'https://www.google.com/recaptcha/api/verify' | ||
50 | + api_recaptcha_v2_verify_uri: 'https://www.google.com/recaptcha/api/siteverify' | ||
51 | + # version 1 or 2 | ||
52 | + api_captcha_version: 1 |
config/routes.rb
@@ -22,6 +22,7 @@ Noosfero::Application.routes.draw do | @@ -22,6 +22,7 @@ Noosfero::Application.routes.draw do | ||
22 | root :to => 'home#index', :constraints => EnvironmentDomainConstraint.new | 22 | root :to => 'home#index', :constraints => EnvironmentDomainConstraint.new |
23 | 23 | ||
24 | match 'site(/:action)', :controller => 'home' | 24 | match 'site(/:action)', :controller => 'home' |
25 | + match 'api(/:action)', :controller => 'api' | ||
25 | 26 | ||
26 | match 'images(/*stuff)' => 'not_found#nothing' | 27 | match 'images(/*stuff)' => 'not_found#nothing' |
27 | match 'stylesheets(/*stuff)' => 'not_found#nothing' | 28 | match 'stylesheets(/*stuff)' => 'not_found#nothing' |
db/migrate/20140407013817_add_private_token_info_to_users.rb
@@ -1,11 +0,0 @@ | @@ -1,11 +0,0 @@ | ||
1 | -class AddPrivateTokenInfoToUsers < ActiveRecord::Migration | ||
2 | - def self.up | ||
3 | - add_column :users, :private_token, :string | ||
4 | - add_column :users, :private_token_generated_at, :datetime | ||
5 | - end | ||
6 | - | ||
7 | - def self.down | ||
8 | - remove_column :users, :private_token | ||
9 | - remove_column :users, :private_token_generated_at | ||
10 | - end | ||
11 | -end |
db/migrate/20150223180807_add_private_token_info_to_users.rb
0 → 100644
@@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
1 | +class AddPrivateTokenInfoToUsers < ActiveRecord::Migration | ||
2 | + def self.up | ||
3 | + add_column :users, :private_token, :string | ||
4 | + add_column :users, :private_token_generated_at, :datetime | ||
5 | + end | ||
6 | + | ||
7 | + def self.down | ||
8 | + remove_column :users, :private_token | ||
9 | + remove_column :users, :private_token_generated_at | ||
10 | + end | ||
11 | +end |
db/migrate/20150712194411_change_task_end_date_from_date_to_date_time.rb
0 → 100644
db/schema.rb
@@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
11 | # | 11 | # |
12 | # It's strongly recommended to check this file into your version control system. | 12 | # It's strongly recommended to check this file into your version control system. |
13 | 13 | ||
14 | -ActiveRecord::Schema.define(:version => 20150602142030) do | 14 | +ActiveRecord::Schema.define(:version => 20150603182105) do |
15 | 15 | ||
16 | create_table "abuse_reports", :force => true do |t| | 16 | create_table "abuse_reports", :force => true do |t| |
17 | t.integer "reporter_id" | 17 | t.integer "reporter_id" |
@@ -377,6 +377,7 @@ ActiveRecord::Schema.define(:version => 20150602142030) do | @@ -377,6 +377,7 @@ ActiveRecord::Schema.define(:version => 20150602142030) do | ||
377 | t.integer "width" | 377 | t.integer "width" |
378 | t.integer "height" | 378 | t.integer "height" |
379 | t.boolean "thumbnails_processed", :default => false | 379 | t.boolean "thumbnails_processed", :default => false |
380 | + t.string "label", :default => "" | ||
380 | end | 381 | end |
381 | 382 | ||
382 | add_index "images", ["parent_id"], :name => "index_images_on_parent_id" | 383 | add_index "images", ["parent_id"], :name => "index_images_on_parent_id" |
debian/changelog
1 | -noosfero (1.2~1) UNRELEASED; urgency=medium | ||
2 | - | ||
3 | - [ Antonio Terceiro ] | ||
4 | - * Temporary version in heavy development | 1 | +noosfero (1.2~rc1) wheezy; urgency=medium |
5 | 2 | ||
6 | [ Joenio Costa ] | 3 | [ Joenio Costa ] |
7 | * Build noosfero-chat package | 4 | * Build noosfero-chat package |
8 | 5 | ||
9 | - -- Joenio Costa <joenio@colivre.coop.br> Mon, 18 May 2015 14:32:21 -0300 | 6 | + [ Antonio Terceiro ] |
7 | + * Noosfero 1.2 RC1 | ||
8 | + | ||
9 | + -- Antonio Terceiro <terceiro@colivre.coop.br> Mon, 13 Jul 2015 15:44:17 -0300 | ||
10 | 10 | ||
11 | noosfero (1.1) wheezy; urgency=low | 11 | noosfero (1.1) wheezy; urgency=low |
12 | 12 |
features/events.feature
@@ -160,6 +160,7 @@ Feature: events | @@ -160,6 +160,7 @@ Feature: events | ||
160 | When I am on /search/events | 160 | When I am on /search/events |
161 | Then I should see "Colivre.net's Events" | 161 | Then I should see "Colivre.net's Events" |
162 | 162 | ||
163 | + | ||
163 | @selenium | 164 | @selenium |
164 | Scenario: published events should be listed in the agenda too | 165 | Scenario: published events should be listed in the agenda too |
165 | Given the following community | 166 | Given the following community |
lib/add_members_job.rb
@@ -4,7 +4,12 @@ class AddMembersJob < Struct.new(:people_ids, :profile_id, :locale) | @@ -4,7 +4,12 @@ class AddMembersJob < Struct.new(:people_ids, :profile_id, :locale) | ||
4 | Noosfero.with_locale(locale) do | 4 | Noosfero.with_locale(locale) do |
5 | 5 | ||
6 | profile = Profile.find(profile_id) | 6 | profile = Profile.find(profile_id) |
7 | - profile.add_members people_ids | 7 | + |
8 | + if people_ids.first =~ /\@/ | ||
9 | + profile.add_members_by_email people_ids | ||
10 | + else | ||
11 | + profile.add_members_by_id people_ids | ||
12 | + end | ||
8 | 13 | ||
9 | end | 14 | end |
10 | end | 15 | end |
lib/noosfero/api/api.rb
1 | require 'grape' | 1 | require 'grape' |
2 | #require 'rack/contrib' | 2 | #require 'rack/contrib' |
3 | - | ||
4 | Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/} | 3 | Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/} |
4 | + | ||
5 | module Noosfero | 5 | module Noosfero |
6 | module API | 6 | module API |
7 | class API < Grape::API | 7 | class API < Grape::API |
@@ -9,32 +9,33 @@ module Noosfero | @@ -9,32 +9,33 @@ module Noosfero | ||
9 | 9 | ||
10 | logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log")) | 10 | logger = Logger.new(File.join(Rails.root, 'log', "#{ENV['RAILS_ENV'] || 'production'}_api.log")) |
11 | logger.formatter = GrapeLogging::Formatters::Default.new | 11 | logger.formatter = GrapeLogging::Formatters::Default.new |
12 | - use RequestLogger, { logger: logger } | 12 | + use GrapeLogging::Middleware::RequestLogger, { logger: logger } |
13 | 13 | ||
14 | - rescue_from :all do |e| | ||
15 | - logger.error e | ||
16 | - end | 14 | + #rescue_from :all do |e| |
15 | + # logger.error e | ||
16 | + #end | ||
17 | 17 | ||
18 | @@NOOSFERO_CONF = nil | 18 | @@NOOSFERO_CONF = nil |
19 | - | ||
20 | def self.NOOSFERO_CONF | 19 | def self.NOOSFERO_CONF |
21 | - if @@NOOSFERO_CONF | 20 | + if @@NOOSFERO_CONF |
22 | @@NOOSFERO_CONF | 21 | @@NOOSFERO_CONF |
23 | else | 22 | else |
24 | file = Rails.root.join('config', 'noosfero.yml') | 23 | file = Rails.root.join('config', 'noosfero.yml') |
25 | @@NOOSFERO_CONF = File.exists?(file) ? YAML.load_file(file)[Rails.env] || {} : {} | 24 | @@NOOSFERO_CONF = File.exists?(file) ? YAML.load_file(file)[Rails.env] || {} : {} |
26 | - end | 25 | + end |
27 | end | 26 | end |
28 | 27 | ||
28 | + before { set_locale } | ||
29 | before { setup_multitenancy } | 29 | before { setup_multitenancy } |
30 | before { detect_stuff_by_domain } | 30 | before { detect_stuff_by_domain } |
31 | + before { filter_disabled_plugins_endpoints } | ||
31 | after { set_session_cookie } | 32 | after { set_session_cookie } |
32 | 33 | ||
33 | version 'v1' | 34 | version 'v1' |
34 | prefix "api" | 35 | prefix "api" |
35 | format :json | 36 | format :json |
36 | content_type :txt, "text/plain" | 37 | content_type :txt, "text/plain" |
37 | - | 38 | + |
38 | helpers APIHelpers | 39 | helpers APIHelpers |
39 | 40 | ||
40 | mount V1::Articles | 41 | mount V1::Articles |
@@ -45,6 +46,9 @@ module Noosfero | @@ -45,6 +46,9 @@ module Noosfero | ||
45 | mount V1::Enterprises | 46 | mount V1::Enterprises |
46 | mount V1::Categories | 47 | mount V1::Categories |
47 | mount V1::Tasks | 48 | mount V1::Tasks |
49 | + mount V1::Tags | ||
50 | + mount V1::Environments | ||
51 | + | ||
48 | mount Session | 52 | mount Session |
49 | 53 | ||
50 | # hook point which allow plugins to add Grape::API extensions to API::API | 54 | # hook point which allow plugins to add Grape::API extensions to API::API |
@@ -57,6 +61,26 @@ module Noosfero | @@ -57,6 +61,26 @@ module Noosfero | ||
57 | end | 61 | end |
58 | end | 62 | end |
59 | end | 63 | end |
64 | + | ||
65 | + def self.endpoint_unavailable?(endpoint, environment) | ||
66 | + api_class = endpoint.options[:app] || endpoint.options[:for] | ||
67 | + if api_class.present? | ||
68 | + klass = api_class.name.deconstantize.constantize | ||
69 | + return klass < Noosfero::Plugin && !environment.plugin_enabled?(klass) | ||
70 | + end | ||
71 | + end | ||
72 | + | ||
73 | + class << self | ||
74 | + def endpoints_with_plugins(environment = nil) | ||
75 | + if environment.present? | ||
76 | + cloned_endpoints = endpoints_without_plugins.dup | ||
77 | + cloned_endpoints.delete_if { |endpoint| endpoint_unavailable?(endpoint, environment) } | ||
78 | + else | ||
79 | + endpoints_without_plugins | ||
80 | + end | ||
81 | + end | ||
82 | + alias_method_chain :endpoints, :plugins | ||
83 | + end | ||
60 | end | 84 | end |
61 | end | 85 | end |
62 | end | 86 | end |
lib/noosfero/api/entities.rb
1 | module Noosfero | 1 | module Noosfero |
2 | module API | 2 | module API |
3 | module Entities | 3 | module Entities |
4 | - | 4 | + |
5 | Entity.format_with :timestamp do |date| | 5 | Entity.format_with :timestamp do |date| |
6 | date.strftime('%Y/%m/%d %H:%M:%S') if date | 6 | date.strftime('%Y/%m/%d %H:%M:%S') if date |
7 | end | 7 | end |
8 | - | 8 | + |
9 | class Image < Entity | 9 | class Image < Entity |
10 | root 'images', 'image' | 10 | root 'images', 'image' |
11 | 11 | ||
12 | expose :url do |image, options| | 12 | expose :url do |image, options| |
13 | image.public_filename | 13 | image.public_filename |
14 | end | 14 | end |
15 | - | 15 | + |
16 | expose :icon_url do |image, options| | 16 | expose :icon_url do |image, options| |
17 | image.public_filename(:icon) | 17 | image.public_filename(:icon) |
18 | end | 18 | end |
19 | - | 19 | + |
20 | expose :minor_url do |image, options| | 20 | expose :minor_url do |image, options| |
21 | image.public_filename(:minor) | 21 | image.public_filename(:minor) |
22 | end | 22 | end |
23 | - | 23 | + |
24 | expose :portrait_url do |image, options| | 24 | expose :portrait_url do |image, options| |
25 | image.public_filename(:portrait) | 25 | image.public_filename(:portrait) |
26 | end | 26 | end |
27 | - | 27 | + |
28 | expose :thumb_url do |image, options| | 28 | expose :thumb_url do |image, options| |
29 | image.public_filename(:thumb) | 29 | image.public_filename(:thumb) |
30 | end | 30 | end |
31 | end | 31 | end |
32 | - | 32 | + |
33 | class Profile < Entity | 33 | class Profile < Entity |
34 | expose :identifier, :name, :id | 34 | expose :identifier, :name, :id |
35 | expose :created_at, :format_with => :timestamp | 35 | expose :created_at, :format_with => :timestamp |
36 | expose :image, :using => Image | 36 | expose :image, :using => Image |
37 | end | 37 | end |
38 | - | 38 | + |
39 | + class UserBasic < Entity | ||
40 | + expose :id | ||
41 | + expose :login | ||
42 | + end | ||
43 | + | ||
39 | class Person < Profile | 44 | class Person < Profile |
40 | root 'people', 'person' | 45 | root 'people', 'person' |
46 | + expose :user, :using => UserBasic | ||
41 | end | 47 | end |
48 | + | ||
42 | class Enterprise < Profile | 49 | class Enterprise < Profile |
43 | root 'enterprises', 'enterprise' | 50 | root 'enterprises', 'enterprise' |
44 | end | 51 | end |
52 | + | ||
45 | class Community < Profile | 53 | class Community < Profile |
46 | root 'communities', 'community' | 54 | root 'communities', 'community' |
47 | expose :description | 55 | expose :description |
48 | end | 56 | end |
49 | - | ||
50 | - class Category < Entity | 57 | + |
58 | + class CategoryBase < Entity | ||
51 | root 'categories', 'category' | 59 | root 'categories', 'category' |
52 | expose :name, :id, :slug | 60 | expose :name, :id, :slug |
61 | + end | ||
62 | + | ||
63 | + class Category < CategoryBase | ||
64 | + root 'categories', 'category' | ||
65 | + expose :full_name do |category, options| | ||
66 | + category.full_name | ||
67 | + end | ||
68 | + expose :parent, :using => CategoryBase, if: { parent: true } | ||
69 | + expose :children, :using => CategoryBase, if: { children: true } | ||
53 | expose :image, :using => Image | 70 | expose :image, :using => Image |
54 | end | 71 | end |
55 | - | 72 | + |
56 | class ArticleBase < Entity | 73 | class ArticleBase < Entity |
57 | root 'articles', 'article' | 74 | root 'articles', 'article' |
58 | expose :id | 75 | expose :id |
@@ -72,12 +89,15 @@ module Noosfero | @@ -72,12 +89,15 @@ module Noosfero | ||
72 | expose :hits | 89 | expose :hits |
73 | expose :start_date | 90 | expose :start_date |
74 | expose :end_date | 91 | expose :end_date |
92 | + expose :tag_list | ||
75 | end | 93 | end |
76 | 94 | ||
77 | class Article < ArticleBase | 95 | class Article < ArticleBase |
78 | root 'articles', 'article' | 96 | root 'articles', 'article' |
79 | expose :parent, :using => ArticleBase | 97 | expose :parent, :using => ArticleBase |
80 | - expose :children, :using => ArticleBase | 98 | + expose :children, using: ArticleBase do |article, options| |
99 | + article.children.limit(Noosfero::API::V1::Articles::MAX_PER_PAGE) | ||
100 | + end | ||
81 | end | 101 | end |
82 | 102 | ||
83 | class Comment < Entity | 103 | class Comment < Entity |
@@ -86,8 +106,8 @@ module Noosfero | @@ -86,8 +106,8 @@ module Noosfero | ||
86 | expose :created_at, :format_with => :timestamp | 106 | expose :created_at, :format_with => :timestamp |
87 | expose :author, :using => Profile | 107 | expose :author, :using => Profile |
88 | end | 108 | end |
89 | - | ||
90 | - | 109 | + |
110 | + | ||
91 | class User < Entity | 111 | class User < Entity |
92 | root 'users', 'user' | 112 | root 'users', 'user' |
93 | expose :id | 113 | expose :id |
@@ -96,14 +116,14 @@ module Noosfero | @@ -96,14 +116,14 @@ module Noosfero | ||
96 | expose :permissions do |user, options| | 116 | expose :permissions do |user, options| |
97 | output = {} | 117 | output = {} |
98 | user.person.role_assignments.map do |role_assigment| | 118 | user.person.role_assignments.map do |role_assigment| |
99 | - if role_assigment.resource.respond_to?(:identifier) | ||
100 | - output[role_assigment.resource.identifier] = role_assigment.role.permissions | 119 | + if role_assigment.resource.respond_to?(:identifier) && !role_assigment.role.nil? |
120 | + output[role_assigment.resource.identifier] = role_assigment.role.permissions | ||
101 | end | 121 | end |
102 | end | 122 | end |
103 | output | 123 | output |
104 | end | 124 | end |
105 | end | 125 | end |
106 | - | 126 | + |
107 | class UserLogin < User | 127 | class UserLogin < User |
108 | expose :private_token | 128 | expose :private_token |
109 | end | 129 | end |
@@ -114,6 +134,16 @@ module Noosfero | @@ -114,6 +134,16 @@ module Noosfero | ||
114 | expose :type | 134 | expose :type |
115 | end | 135 | end |
116 | 136 | ||
137 | + class Environment < Entity | ||
138 | + expose :name | ||
139 | + end | ||
140 | + | ||
141 | + class Tag < Entity | ||
142 | + root 'tags', 'tag' | ||
143 | + expose :name | ||
144 | + end | ||
145 | + | ||
146 | + | ||
117 | end | 147 | end |
118 | end | 148 | end |
119 | end | 149 | end |
lib/noosfero/api/entity.rb
1 | class Noosfero::API::Entity < Grape::Entity | 1 | class Noosfero::API::Entity < Grape::Entity |
2 | 2 | ||
3 | + def initialize(object, options = {}) | ||
4 | + object = nil if object.is_a? Exception | ||
5 | + super object, options | ||
6 | + end | ||
7 | + | ||
8 | + def self.represent(objects, options = {}) | ||
9 | + if options[:has_exception] | ||
10 | + data = super objects, options.merge(is_inner_data: true) | ||
11 | + if objects.is_a? Exception | ||
12 | + data.merge ok: false, error: { | ||
13 | + type: objects.class.name, | ||
14 | + message: objects.message | ||
15 | + } | ||
16 | + else | ||
17 | + data = data.serializable_hash if data.is_a? Noosfero::API::Entity | ||
18 | + data.merge ok: true, error: { type: 'Success', message: '' } | ||
19 | + end | ||
20 | + else | ||
21 | + super objects, options | ||
22 | + end | ||
23 | + end | ||
24 | + | ||
3 | def self.fields_condition(fields) | 25 | def self.fields_condition(fields) |
4 | lambda do |object, options| | 26 | lambda do |object, options| |
5 | return true if options[:fields].blank? | 27 | return true if options[:fields].blank? |
lib/noosfero/api/helpers.rb
1 | -module Noosfero | ||
2 | - module API | ||
3 | - module APIHelpers | 1 | + module Noosfero; |
2 | + | ||
3 | + module API | ||
4 | + module APIHelpers | ||
4 | PRIVATE_TOKEN_PARAM = :private_token | 5 | PRIVATE_TOKEN_PARAM = :private_token |
5 | - ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type] | 6 | + DEFAULT_ALLOWED_PARAMETERS = [:parent_id, :from, :until, :content_type] |
7 | + | ||
8 | + include SanitizeParams | ||
6 | 9 | ||
10 | + def set_locale | ||
11 | + I18n.locale = (params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en') | ||
12 | + end | ||
13 | + | ||
7 | def current_user | 14 | def current_user |
8 | private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s | 15 | private_token = (params[PRIVATE_TOKEN_PARAM] || headers['Private-Token']).to_s |
9 | @current_user ||= User.find_by_private_token(private_token) | 16 | @current_user ||= User.find_by_private_token(private_token) |
@@ -45,14 +52,85 @@ module Noosfero | @@ -45,14 +52,85 @@ module Noosfero | ||
45 | end | 52 | end |
46 | end | 53 | end |
47 | 54 | ||
55 | + ARTICLE_TYPES = ['Article'] + Article.descendants.map{|a| a.to_s} | ||
56 | + TASK_TYPES = ['Task'] + Task.descendants.map{|a| a.to_s} | ||
57 | + | ||
48 | def find_article(articles, id) | 58 | def find_article(articles, id) |
49 | article = articles.find(id) | 59 | article = articles.find(id) |
50 | article.display_to?(current_user.person) ? article : forbidden! | 60 | article.display_to?(current_user.person) ? article : forbidden! |
51 | end | 61 | end |
52 | 62 | ||
53 | - def find_task(tasks, id) | ||
54 | - task = tasks.find(id) | ||
55 | - task.display_to?(current_user.person) ? task : forbidden! | 63 | + def post_article(asset, params) |
64 | + return forbidden! unless current_person.can_post_content?(asset) | ||
65 | + | ||
66 | + klass_type= params[:content_type].nil? ? 'TinyMceArticle' : params[:content_type] | ||
67 | + return forbidden! unless ARTICLE_TYPES.include?(klass_type) | ||
68 | + | ||
69 | + article = klass_type.constantize.new(params[:article]) | ||
70 | + article.last_changed_by = current_person | ||
71 | + article.created_by= current_person | ||
72 | + article.profile = asset | ||
73 | + | ||
74 | + if !article.save | ||
75 | + render_api_errors!(article.errors.full_messages) | ||
76 | + end | ||
77 | + present article, :with => Entities::Article, :fields => params[:fields] | ||
78 | + end | ||
79 | + | ||
80 | + def present_article(asset) | ||
81 | + article = find_article(asset.articles, params[:id]) | ||
82 | + present article, :with => Entities::Article, :fields => params[:fields] | ||
83 | + end | ||
84 | + | ||
85 | + def present_articles(asset) | ||
86 | + articles = find_articles(asset) | ||
87 | + articles = paginate articles | ||
88 | + present articles, :with => Entities::Article, :fields => params[:fields] | ||
89 | + end | ||
90 | + | ||
91 | + def find_articles(asset) | ||
92 | + articles = select_filtered_collection_of(asset, 'articles', params) | ||
93 | + if current_person.present? | ||
94 | + articles = articles.display_filter(current_person, nil) | ||
95 | + else | ||
96 | + articles = articles.published | ||
97 | + end | ||
98 | + if params[:categories_ids] | ||
99 | + articles = articles.joins(:categories).where('category_id in (?)', params[:categories_ids]) | ||
100 | + end | ||
101 | + articles | ||
102 | + end | ||
103 | + | ||
104 | + def find_task(asset, id) | ||
105 | + task = asset.tasks.find(id) | ||
106 | + current_person.has_permission?(task.permission, asset) ? task : forbidden! | ||
107 | + end | ||
108 | + | ||
109 | + def post_task(asset, params) | ||
110 | + klass_type= params[:content_type].nil? ? 'Task' : params[:content_type] | ||
111 | + return forbidden! unless TASK_TYPES.include?(klass_type) | ||
112 | + | ||
113 | + task = klass_type.constantize.new(params[:task]) | ||
114 | + task.requestor_id = current_person.id | ||
115 | + task.target_id = asset.id | ||
116 | + task.target_type = 'Profile' | ||
117 | + | ||
118 | + if !task.save | ||
119 | + render_api_errors!(task.errors.full_messages) | ||
120 | + end | ||
121 | + present task, :with => Entities::Task, :fields => params[:fields] | ||
122 | + end | ||
123 | + | ||
124 | + def present_task(asset) | ||
125 | + task = find_task(asset, params[:id]) | ||
126 | + present task, :with => Entities::Task, :fields => params[:fields] | ||
127 | + end | ||
128 | + | ||
129 | + def present_tasks(asset) | ||
130 | + tasks = select_filtered_collection_of(asset, 'tasks', params) | ||
131 | + tasks = tasks.select {|t| current_person.has_permission?(t.permission, asset)} | ||
132 | + return forbidden! if tasks.empty? && !current_person.has_permission?(:perform_task, asset) | ||
133 | + present tasks, :with => Entities::Task, :fields => params[:fields] | ||
56 | end | 134 | end |
57 | 135 | ||
58 | def make_conditions_with_parameter(params = {}) | 136 | def make_conditions_with_parameter(params = {}) |
@@ -73,19 +151,22 @@ module Noosfero | @@ -73,19 +151,22 @@ module Noosfero | ||
73 | params[:order] || "created_at DESC" | 151 | params[:order] || "created_at DESC" |
74 | end | 152 | end |
75 | 153 | ||
76 | - def select_filtered_collection_of(object, method, params) | ||
77 | - conditions = make_conditions_with_parameter(params) | ||
78 | - order = make_order_with_parameters(params) | ||
79 | - | 154 | + def by_reference(scope, params) |
80 | if params[:reference_id] | 155 | if params[:reference_id] |
81 | - objects = object.send(method).send("#{params.key?(:oldest) ? 'older_than' : 'newer_than'}", params[:reference_id]).where(conditions).limit(limit).order(order) | 156 | + created_at = scope.find(params[:reference_id]).created_at |
157 | + scope.send("#{params.key?(:oldest) ? 'older_than' : 'younger_than'}", created_at) | ||
82 | else | 158 | else |
83 | - objects = object.send(method).where(conditions).limit(limit).order(order) | 159 | + scope |
84 | end | 160 | end |
161 | + end | ||
85 | 162 | ||
86 | - if params[:categories_ids] | ||
87 | - objects = objects.joins(:categories).where('category_id in (?)', params[:categories_ids]) | ||
88 | - end | 163 | + def select_filtered_collection_of(object, method, params) |
164 | + conditions = make_conditions_with_parameter(params) | ||
165 | + order = make_order_with_parameters(params) | ||
166 | + | ||
167 | + objects = object.send(method) | ||
168 | + objects = by_reference(objects, params) | ||
169 | + objects = objects.where(conditions).limit(limit).order(order) | ||
89 | 170 | ||
90 | objects | 171 | objects |
91 | end | 172 | end |
@@ -115,9 +196,9 @@ module Noosfero | @@ -115,9 +196,9 @@ module Noosfero | ||
115 | 196 | ||
116 | def verify_recaptcha_v2(remote_ip, g_recaptcha_response, private_key, api_recaptcha_verify_uri) | 197 | def verify_recaptcha_v2(remote_ip, g_recaptcha_response, private_key, api_recaptcha_verify_uri) |
117 | verify_hash = { | 198 | verify_hash = { |
118 | - "secret" => private_key, | ||
119 | - "remoteip" => remote_ip, | ||
120 | - "response" => g_recaptcha_response | 199 | + "secret" => private_key, |
200 | + "remoteip" => remote_ip, | ||
201 | + "response" => g_recaptcha_response | ||
121 | } | 202 | } |
122 | uri = URI(api_recaptcha_verify_uri) | 203 | uri = URI(api_recaptcha_verify_uri) |
123 | https = Net::HTTP.new(uri.host, uri.port) | 204 | https = Net::HTTP.new(uri.host, uri.port) |
@@ -131,6 +212,10 @@ module Noosfero | @@ -131,6 +212,10 @@ module Noosfero | ||
131 | # error helpers # | 212 | # error helpers # |
132 | ########################################## | 213 | ########################################## |
133 | 214 | ||
215 | + def not_found! | ||
216 | + render_api_error!('404 Not found', 404) | ||
217 | + end | ||
218 | + | ||
134 | def forbidden! | 219 | def forbidden! |
135 | render_api_error!('403 Forbidden', 403) | 220 | render_api_error!('403 Forbidden', 403) |
136 | end | 221 | end |
@@ -188,12 +273,16 @@ module Noosfero | @@ -188,12 +273,16 @@ module Noosfero | ||
188 | end | 273 | end |
189 | end | 274 | end |
190 | 275 | ||
276 | + def filter_disabled_plugins_endpoints | ||
277 | + not_found! if Noosfero::API::API.endpoint_unavailable?(self, @environment) | ||
278 | + end | ||
279 | + | ||
191 | private | 280 | private |
192 | 281 | ||
193 | def parser_params(params) | 282 | def parser_params(params) |
194 | parsed_params = {} | 283 | parsed_params = {} |
195 | params.map do |k,v| | 284 | params.map do |k,v| |
196 | - parsed_params[k.to_sym] = v if ALLOWED_PARAMETERS.include?(k.to_sym) | 285 | + parsed_params[k.to_sym] = v if DEFAULT_ALLOWED_PARAMETERS.include?(k.to_sym) |
197 | end | 286 | end |
198 | parsed_params | 287 | parsed_params |
199 | end | 288 | end |
@@ -212,10 +301,106 @@ module Noosfero | @@ -212,10 +301,106 @@ module Noosfero | ||
212 | def period(from_date, until_date) | 301 | def period(from_date, until_date) |
213 | begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date | 302 | begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date |
214 | end_period = until_date.nil? ? DateTime.now : until_date | 303 | end_period = until_date.nil? ? DateTime.now : until_date |
215 | - | ||
216 | begin_period..end_period | 304 | begin_period..end_period |
217 | end | 305 | end |
218 | 306 | ||
307 | + ########################################## | ||
308 | + # captcha_helpers # | ||
309 | + ########################################## | ||
310 | + | ||
311 | + def test_captcha(remote_ip, params, environment) | ||
312 | + d = environment.api_captcha_settings | ||
313 | + return true unless d[:enabled] == true | ||
314 | + | ||
315 | + if d[:provider] == 'google' | ||
316 | + raise ArgumentError, "Environment api_captcha_settings private_key not defined" if d[:private_key].nil? | ||
317 | + raise ArgumentError, "Environment api_captcha_settings version not defined" unless d[:version] == 1 || d[:version] == 2 | ||
318 | + if d[:version] == 1 | ||
319 | + d[:verify_uri] ||= 'https://www.google.com/recaptcha/api/verify' | ||
320 | + return verify_recaptcha_v1(remote_ip, d[:private_key], d[:verify_uri], params[:recaptcha_challenge_field], params[:recaptcha_response_field]) | ||
321 | + end | ||
322 | + if d[:version] == 2 | ||
323 | + d[:verify_uri] ||= 'https://www.google.com/recaptcha/api/siteverify' | ||
324 | + return verify_recaptcha_v2(remote_ip, d[:private_key], d[:verify_uri], params[:g_recaptcha_response]) | ||
325 | + end | ||
326 | + end | ||
327 | + if d[:provider] == 'serpro' | ||
328 | + raise ArgumentError, "Environment api_captcha_settings verify_uri not defined" if d[:verify_uri].nil? | ||
329 | + return verify_serpro_captcha(d[:serpro_client_id], params[:txtToken_captcha_serpro_gov_br], params[:captcha_text], d[:verify_uri]) | ||
330 | + end | ||
331 | + raise ArgumentError, "Environment api_captcha_settings provider not defined" | ||
332 | + end | ||
333 | + | ||
334 | + def verify_recaptcha_v1(remote_ip, private_key, api_recaptcha_verify_uri, recaptcha_challenge_field, recaptcha_response_field) | ||
335 | + if recaptcha_challenge_field == nil || recaptcha_response_field == nil | ||
336 | + return _('Missing captcha data') | ||
337 | + end | ||
338 | + | ||
339 | + verify_hash = { | ||
340 | + "privatekey" => private_key, | ||
341 | + "remoteip" => remote_ip, | ||
342 | + "challenge" => recaptcha_challenge_field, | ||
343 | + "response" => recaptcha_response_field | ||
344 | + } | ||
345 | + uri = URI(api_recaptcha_verify_uri) | ||
346 | + https = Net::HTTP.new(uri.host, uri.port) | ||
347 | + https.use_ssl = true | ||
348 | + request = Net::HTTP::Post.new(uri.path) | ||
349 | + request.set_form_data(verify_hash) | ||
350 | + begin | ||
351 | + body = https.request(request).body | ||
352 | + rescue Exception => e | ||
353 | + logger.error e | ||
354 | + return _("Google recaptcha error: #{e.message}") | ||
355 | + end | ||
356 | + body = JSON.parse(body) | ||
357 | + body == "true\nsuccess" ? true : body | ||
358 | + end | ||
359 | + | ||
360 | + def verify_recaptcha_v2(remote_ip, private_key, api_recaptcha_verify_uri, g_recaptcha_response) | ||
361 | + if g_recaptcha_response == nil | ||
362 | + return _('Missing captcha data') | ||
363 | + end | ||
364 | + | ||
365 | + verify_hash = { | ||
366 | + "secret" => private_key, | ||
367 | + "remoteip" => remote_ip, | ||
368 | + "response" => g_recaptcha_response | ||
369 | + } | ||
370 | + uri = URI(api_recaptcha_verify_uri) | ||
371 | + https = Net::HTTP.new(uri.host, uri.port) | ||
372 | + https.use_ssl = true | ||
373 | + request = Net::HTTP::Post.new(uri.path) | ||
374 | + request.set_form_data(verify_hash) | ||
375 | + begin | ||
376 | + body = https.request(request).body | ||
377 | + rescue Exception => e | ||
378 | + logger.error e | ||
379 | + return _("Google recaptcha error: #{e.message}") | ||
380 | + end | ||
381 | + captcha_result = JSON.parse(body) | ||
382 | + captcha_result["success"] ? true : captcha_result | ||
383 | + end | ||
384 | + | ||
385 | + def verify_serpro_captcha(client_id, token, captcha_text, verify_uri) | ||
386 | + return _('Missing Serpro Captcha token') if token == nil | ||
387 | + return _('Captcha text has not been filled') if captcha_text == nil | ||
388 | + uri = URI(verify_uri) | ||
389 | + http = Net::HTTP.new(uri.host, uri.port) | ||
390 | + request = Net::HTTP::Post.new(uri.path) | ||
391 | + verify_string = "#{client_id}&#{token}&#{captcha_text}" | ||
392 | + request.body = verify_string | ||
393 | + begin | ||
394 | + body = http.request(request).body | ||
395 | + rescue Exception => e | ||
396 | + logger.error e | ||
397 | + return _("Serpro captcha error: #{e.message}") | ||
398 | + end | ||
399 | + return _("Wrong captcha text, please try again") if body == 0 | ||
400 | + return _("Token not found") if body == 2 | ||
401 | + body == '1' ? true : body | ||
402 | + end | ||
403 | + | ||
219 | end | 404 | end |
220 | end | 405 | end |
221 | end | 406 | end |
lib/noosfero/api/session.rb
@@ -29,26 +29,24 @@ module Noosfero | @@ -29,26 +29,24 @@ module Noosfero | ||
29 | # password (required) - Password | 29 | # password (required) - Password |
30 | # login - login | 30 | # login - login |
31 | # Example Request: | 31 | # Example Request: |
32 | - # POST /register?email=some@mail.com&password=pas&login=some | 32 | + # POST /register?email=some@mail.com&password=pas&password_confirmation=pas&login=some |
33 | params do | 33 | params do |
34 | requires :email, type: String, desc: _("Email") | 34 | requires :email, type: String, desc: _("Email") |
35 | requires :login, type: String, desc: _("Login") | 35 | requires :login, type: String, desc: _("Login") |
36 | requires :password, type: String, desc: _("Password") | 36 | requires :password, type: String, desc: _("Password") |
37 | + requires :password_confirmation, type: String, desc: _("Password confirmation") | ||
37 | end | 38 | end |
38 | post "/register" do | 39 | post "/register" do |
39 | - unique_attributes! User, [:email, :login] | ||
40 | - attrs = attributes_for_keys [:email, :login, :password] | ||
41 | - attrs[:password_confirmation] = attrs[:password] | 40 | + attrs = attributes_for_keys [:email, :login, :password, :password_confirmation] + environment.signup_person_fields |
41 | + remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR']) | ||
42 | 42 | ||
43 | - #Commented for stress tests | ||
44 | - | ||
45 | - # remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR']) | ||
46 | - # private_key = API.NOOSFERO_CONF['api_recaptcha_private_key'] | ||
47 | - # api_recaptcha_verify_uri = API.NOOSFERO_CONF['api_recaptcha_verify_uri'] | ||
48 | - # captcha_result = verify_recaptcha_v2(remote_ip, params['g-recaptcha-response'], private_key, api_recaptcha_verify_uri) | ||
49 | - user = User.new(attrs) | ||
50 | -# if captcha_result["success"] and user.save | ||
51 | - if user.save | 43 | + unless test_captcha(remote_ip, params, environment) == true |
44 | + render_api_error!(_('Please solve the test in order to register.'), 401) | ||
45 | + return | ||
46 | + end | ||
47 | + | ||
48 | + user = User.new(attrs) | ||
49 | + if user.save | ||
52 | user.activate | 50 | user.activate |
53 | user.generate_private_token! | 51 | user.generate_private_token! |
54 | present user, :with => Entities::UserLogin | 52 | present user, :with => Entities::UserLogin |
lib/noosfero/api/v1/articles.rb
@@ -2,12 +2,15 @@ module Noosfero | @@ -2,12 +2,15 @@ module Noosfero | ||
2 | module API | 2 | module API |
3 | module V1 | 3 | module V1 |
4 | class Articles < Grape::API | 4 | class Articles < Grape::API |
5 | - before { authenticate! } | ||
6 | - | 5 | + |
7 | ARTICLE_TYPES = Article.descendants.map{|a| a.to_s} | 6 | ARTICLE_TYPES = Article.descendants.map{|a| a.to_s} |
8 | - | 7 | + |
8 | + MAX_PER_PAGE = 50 | ||
9 | + | ||
9 | resource :articles do | 10 | resource :articles do |
10 | - | 11 | + |
12 | + paginate per_page: MAX_PER_PAGE, max_per_page: MAX_PER_PAGE | ||
13 | + | ||
11 | # Collect articles | 14 | # Collect articles |
12 | # | 15 | # |
13 | # Parameters: | 16 | # Parameters: |
@@ -17,18 +20,44 @@ module Noosfero | @@ -17,18 +20,44 @@ module Noosfero | ||
17 | # | 20 | # |
18 | # Example Request: | 21 | # Example Request: |
19 | # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317 | 22 | # GET host/api/v1/articles?from=2013-04-04-14:41:43&until=2015-04-04-14:41:43&limit=10&private_token=e96fff37c2238fdab074d1dcea8e6317 |
23 | + | ||
20 | get do | 24 | get do |
21 | - articles = select_filtered_collection_of(environment, 'articles', params) | ||
22 | - articles = articles.display_filter(current_person, nil) | ||
23 | - present articles, :with => Entities::Article, :fields => params[:fields] | 25 | + present_articles(environment) |
24 | end | 26 | end |
25 | - | 27 | + |
26 | desc "Return the article id" | 28 | desc "Return the article id" |
27 | get ':id' do | 29 | get ':id' do |
30 | + present_article(environment) | ||
31 | + end | ||
32 | + | ||
33 | + post ':id/report_abuse' do | ||
28 | article = find_article(environment.articles, params[:id]) | 34 | article = find_article(environment.articles, params[:id]) |
29 | - present article, :with => Entities::Article, :fields => params[:fields] | 35 | + profile = article.profile |
36 | + begin | ||
37 | + abuse_report = AbuseReport.new(:reason => params[:report_abuse]) | ||
38 | + if !params[:content_type].blank? | ||
39 | + article = params[:content_type].constantize.find(params[:content_id]) | ||
40 | + abuse_report.content = article_reported_version(article) | ||
41 | + end | ||
42 | + | ||
43 | + current_person.register_report(abuse_report, profile) | ||
44 | + | ||
45 | + if !params[:content_type].blank? | ||
46 | + abuse_report = AbuseReport.find_by_reporter_id_and_abuse_complaint_id(current_person.id, profile.opened_abuse_complaint.id) | ||
47 | + Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article) | ||
48 | + end | ||
49 | + | ||
50 | + { | ||
51 | + :success => true, | ||
52 | + :message => _('Your abuse report was registered. The administrators are reviewing your report.'), | ||
53 | + } | ||
54 | + rescue Exception => exception | ||
55 | + #logger.error(exception.to_s) | ||
56 | + render_api_error!(_('Your report couldn\'t be saved due to some problem. Please contact the administrator.'), 400) | ||
57 | + end | ||
58 | + | ||
30 | end | 59 | end |
31 | - | 60 | + |
32 | desc "Returns the total followers for the article" | 61 | desc "Returns the total followers for the article" |
33 | get ':id/followers' do | 62 | get ':id/followers' do |
34 | article = find_article(environment.articles, params[:id]) | 63 | article = find_article(environment.articles, params[:id]) |
@@ -37,16 +66,22 @@ module Noosfero | @@ -37,16 +66,22 @@ module Noosfero | ||
37 | end | 66 | end |
38 | 67 | ||
39 | desc "Add a follower for the article" | 68 | desc "Add a follower for the article" |
40 | - get ':id/follow' do | 69 | + post ':id/follow' do |
70 | + authenticate! | ||
41 | article = find_article(environment.articles, params[:id]) | 71 | article = find_article(environment.articles, params[:id]) |
42 | - article_follower = ArticleFollower.new | ||
43 | - article_follower.article = article | ||
44 | - article_follower.person = current_person | ||
45 | - article_follower.save! | 72 | + if article.article_followers.exists?(:person_id => current_person.id) |
73 | + {:success => false, :already_follow => true} | ||
74 | + else | ||
75 | + article_follower = ArticleFollower.new | ||
76 | + article_follower.article = article | ||
77 | + article_follower.person = current_person | ||
78 | + article_follower.save! | ||
79 | + {:success => true} | ||
80 | + end | ||
46 | end | 81 | end |
47 | - | ||
48 | 82 | ||
49 | post ':id/vote' do | 83 | post ':id/vote' do |
84 | + authenticate! | ||
50 | value = (params[:value] || 1).to_i | 85 | value = (params[:value] || 1).to_i |
51 | # FIXME verify allowed values | 86 | # FIXME verify allowed values |
52 | render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) | 87 | render_api_error!('Vote value not allowed', 400) unless [-1, 1].include?(value) |
@@ -63,7 +98,7 @@ module Noosfero | @@ -63,7 +98,7 @@ module Noosfero | ||
63 | articles = select_filtered_collection_of(article, 'children', params) | 98 | articles = select_filtered_collection_of(article, 'children', params) |
64 | articles = articles.display_filter(current_person, nil) | 99 | articles = articles.display_filter(current_person, nil) |
65 | 100 | ||
66 | - | 101 | + |
67 | #TODO make tests for this situation | 102 | #TODO make tests for this situation |
68 | if votes_order | 103 | if votes_order |
69 | articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC') | 104 | articles = articles.joins('left join votes on articles.id=votes.voteable_id').group('articles.id').reorder('sum(coalesce(votes.vote, 0)) DESC') |
@@ -72,13 +107,14 @@ module Noosfero | @@ -72,13 +107,14 @@ module Noosfero | ||
72 | Article.hit(articles) | 107 | Article.hit(articles) |
73 | present articles, :with => Entities::Article, :fields => params[:fields] | 108 | present articles, :with => Entities::Article, :fields => params[:fields] |
74 | end | 109 | end |
75 | - | 110 | + |
76 | get ':id/children/:child_id' do | 111 | get ':id/children/:child_id' do |
77 | article = find_article(environment.articles, params[:id]) | 112 | article = find_article(environment.articles, params[:id]) |
78 | present find_article(article.children, params[:child_id]), :with => Entities::Article, :fields => params[:fields] | 113 | present find_article(article.children, params[:child_id]), :with => Entities::Article, :fields => params[:fields] |
79 | end | 114 | end |
80 | 115 | ||
81 | post ':id/children/suggest' do | 116 | post ':id/children/suggest' do |
117 | + authenticate! | ||
82 | parent_article = environment.articles.find(params[:id]) | 118 | parent_article = environment.articles.find(params[:id]) |
83 | 119 | ||
84 | suggest_article = SuggestArticle.new | 120 | suggest_article = SuggestArticle.new |
@@ -96,12 +132,12 @@ module Noosfero | @@ -96,12 +132,12 @@ module Noosfero | ||
96 | # Example Request: | 132 | # Example Request: |
97 | # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body | 133 | # POST api/v1/articles/:id/children?private_token=234298743290432&article[name]=title&article[body]=body |
98 | post ':id/children' do | 134 | post ':id/children' do |
99 | - | 135 | + authenticate! |
100 | parent_article = environment.articles.find(params[:id]) | 136 | parent_article = environment.articles.find(params[:id]) |
101 | return forbidden! unless parent_article.allow_create?(current_person) | 137 | return forbidden! unless parent_article.allow_create?(current_person) |
102 | 138 | ||
103 | klass_type= params[:content_type].nil? ? 'TinyMceArticle' : params[:content_type] | 139 | klass_type= params[:content_type].nil? ? 'TinyMceArticle' : params[:content_type] |
104 | - #FIXME see how to check the article types | 140 | + #FIXME see how to check the article types |
105 | #return forbidden! unless ARTICLE_TYPES.include?(klass_type) | 141 | #return forbidden! unless ARTICLE_TYPES.include?(klass_type) |
106 | 142 | ||
107 | article = klass_type.constantize.new(params[:article]) | 143 | article = klass_type.constantize.new(params[:article]) |
@@ -119,129 +155,31 @@ module Noosfero | @@ -119,129 +155,31 @@ module Noosfero | ||
119 | 155 | ||
120 | end | 156 | end |
121 | 157 | ||
122 | -# Request example: | ||
123 | -# /api/v1/communities/64/articles?from=2013-04-04-14:41:43&until=2015-06-11-14:41:43&limit=10&categories_ids[]=7&categories_ids[]=8&private_token=a97b6a5cae2c4c54e4ae18dde1829a49 | ||
124 | - | ||
125 | - resource :communities do | ||
126 | - segment '/:community_id' do | ||
127 | - resource :articles do | ||
128 | - get do | ||
129 | - community = environment.communities.find(params[:community_id]) | ||
130 | - articles = select_filtered_collection_of(community, 'articles', params) | ||
131 | - articles = articles.display_filter(current_person, community) | ||
132 | - present articles, :with => Entities::Article, :fields => params[:fields] | ||
133 | - end | ||
134 | - | ||
135 | - get ':id' do | ||
136 | - community = environment.communities.find(params[:community_id]) | ||
137 | - article = find_article(community.articles, params[:id]) | ||
138 | - present article, :with => Entities::Article, :fields => params[:fields] | ||
139 | - end | ||
140 | - | ||
141 | - # Example Request: | ||
142 | - # POST api/v1/communites/:community_id/articles?private_token=234298743290432&article[name]=title&article[body]=body | ||
143 | - post do | ||
144 | - community = environment.communities.find(params[:community_id]) | ||
145 | - return forbidden! unless current_person.can_post_content?(community) | ||
146 | - | ||
147 | - klass_type= params[:content_type].nil? ? 'TinyMceArticle' : params[:content_type] | ||
148 | - return forbidden! unless ARTICLE_TYPES.include?(klass_type) | ||
149 | - | ||
150 | - article = klass_type.constantize.new(params[:article]) | ||
151 | - article.last_changed_by = current_person | ||
152 | - article.created_by= current_person | ||
153 | - article.profile = community | ||
154 | - | ||
155 | - if !article.save | ||
156 | - render_api_errors!(article.errors.full_messages) | 158 | + kinds = %w[community person enterprise] |
159 | + kinds.each do |kind| | ||
160 | + resource kind.pluralize.to_sym do | ||
161 | + segment "/:#{kind}_id" do | ||
162 | + resource :articles do | ||
163 | + get do | ||
164 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | ||
165 | + present_articles(profile) | ||
157 | end | 166 | end |
158 | - present article, :with => Entities::Article, :fields => params[:fields] | ||
159 | - end | ||
160 | - | ||
161 | - end | ||
162 | - end | ||
163 | - | ||
164 | - end | ||
165 | - | ||
166 | - resource :people do | ||
167 | - segment '/:person_id' do | ||
168 | - resource :articles do | ||
169 | - get do | ||
170 | - person = environment.people.find(params[:person_id]) | ||
171 | - articles = select_filtered_collection_of(person, 'articles', params) | ||
172 | - articles = articles.display_filter(current_person, person) | ||
173 | - present articles, :with => Entities::Article, :fields => params[:fields] | ||
174 | - end | ||
175 | - | ||
176 | - get ':id' do | ||
177 | - person = environment.people.find(params[:person_id]) | ||
178 | - article = find_article(person.articles, params[:id]) | ||
179 | - present article, :with => Entities::Article, :fields => params[:fields] | ||
180 | - end | ||
181 | - | ||
182 | - post do | ||
183 | - person = environment.people.find(params[:person_id]) | ||
184 | - return forbidden! unless current_person.can_post_content?(person) | ||
185 | - | ||
186 | - klass_type= params[:content_type].nil? ? 'TinyMceArticle' : params[:content_type] | ||
187 | - return forbidden! unless ARTICLE_TYPES.include?(klass_type) | ||
188 | - | ||
189 | - article = klass_type.constantize.new(params[:article]) | ||
190 | - article.last_changed_by = current_person | ||
191 | - article.created_by= current_person | ||
192 | - article.profile = person | ||
193 | - | ||
194 | - if !article.save | ||
195 | - render_api_errors!(article.errors.full_messages) | 167 | + |
168 | + get ':id' do | ||
169 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | ||
170 | + present_article(profile) | ||
196 | end | 171 | end |
197 | - present article, :with => Entities::Article, :fields => params[:fields] | ||
198 | - end | ||
199 | - | ||
200 | - end | ||
201 | - end | ||
202 | - | ||
203 | - end | ||
204 | - | ||
205 | - resource :enterprises do | ||
206 | - segment '/:enterprise_id' do | ||
207 | - resource :articles do | ||
208 | - get do | ||
209 | - enterprise = environment.enterprises.find(params[:enterprise_id]) | ||
210 | - articles = select_filtered_collection_of(enterprise, 'articles', params) | ||
211 | - articles = articles.display_filter(current_person, enterprise) | ||
212 | - present articles, :with => Entities::Article, :fields => params[:fields] | ||
213 | - end | ||
214 | - | ||
215 | - get ':id' do | ||
216 | - enterprise = environment.enterprises.find(params[:enterprise_id]) | ||
217 | - article = find_article(enterprise.articles, params[:id]) | ||
218 | - present article, :with => Entities::Article, :fields => params[:fields] | ||
219 | - end | ||
220 | - | ||
221 | - post do | ||
222 | - enterprise = environment.enterprises.find(params[:enterprise_id]) | ||
223 | - return forbidden! unless current_person.can_post_content?(enterprise) | ||
224 | - | ||
225 | - klass_type= params[:content_type].nil? ? 'TinyMceArticle' : params[:content_type] | ||
226 | - return forbidden! unless ARTICLE_TYPES.include?(klass_type) | ||
227 | - | ||
228 | - article = klass_type.constantize.new(params[:article]) | ||
229 | - article.last_changed_by = current_person | ||
230 | - article.created_by= current_person | ||
231 | - article.profile = enterprise | ||
232 | - | ||
233 | - if !article.save | ||
234 | - render_api_errors!(article.errors.full_messages) | 172 | + |
173 | + # Example Request: | ||
174 | + # POST api/v1/{people,communities,enterprises}/:asset_id/articles?private_token=234298743290432&article[name]=title&article[body]=body | ||
175 | + post do | ||
176 | + profile = environment.send(kind.pluralize).find(params["#{kind}_id"]) | ||
177 | + post_article(profile, params) | ||
235 | end | 178 | end |
236 | - present article, :with => Entities::Article, :fields => params[:fields] | ||
237 | end | 179 | end |
238 | - | ||
239 | end | 180 | end |
240 | end | 181 | end |
241 | - | ||
242 | end | 182 | end |
243 | - | ||
244 | - | ||
245 | end | 183 | end |
246 | end | 184 | end |
247 | end | 185 | end |
lib/noosfero/api/v1/categories.rb
@@ -3,22 +3,25 @@ module Noosfero | @@ -3,22 +3,25 @@ module Noosfero | ||
3 | module V1 | 3 | module V1 |
4 | class Categories < Grape::API | 4 | class Categories < Grape::API |
5 | before { authenticate! } | 5 | before { authenticate! } |
6 | - | 6 | + |
7 | resource :categories do | 7 | resource :categories do |
8 | - | 8 | + |
9 | get do | 9 | get do |
10 | type = params[:category_type] | 10 | type = params[:category_type] |
11 | - categories = type.nil? ? environment.categories : environment.categories.find(:all, :conditions => {:type => type}) | ||
12 | - present categories, :with => Entities::Category | 11 | + include_parent = params[:include_parent] == 'true' |
12 | + include_children = params[:include_children] == 'true' | ||
13 | + | ||
14 | + categories = type.nil? ? environment.categories : environment.categories.where(:type => type) | ||
15 | + present categories, :with => Entities::Category, parent: include_parent, children: include_children | ||
13 | end | 16 | end |
14 | - | ||
15 | - desc "Return the category by id" | 17 | + |
18 | + desc "Return the category by id" | ||
16 | get ':id' do | 19 | get ':id' do |
17 | - present environment.categories.find(params[:id]), :with => Entities::Category | 20 | + present environment.categories.find(params[:id]), :with => Entities::Category, parent: true, children: true |
18 | end | 21 | end |
19 | - | 22 | + |
20 | end | 23 | end |
21 | - | 24 | + |
22 | end | 25 | end |
23 | end | 26 | end |
24 | end | 27 | end |
lib/noosfero/api/v1/comments.rb
@@ -3,7 +3,7 @@ module Noosfero | @@ -3,7 +3,7 @@ module Noosfero | ||
3 | module V1 | 3 | module V1 |
4 | class Comments < Grape::API | 4 | class Comments < Grape::API |
5 | before { authenticate! } | 5 | before { authenticate! } |
6 | - | 6 | + |
7 | resource :articles do | 7 | resource :articles do |
8 | # Collect comments from articles | 8 | # Collect comments from articles |
9 | # | 9 | # |
@@ -15,32 +15,26 @@ module Noosfero | @@ -15,32 +15,26 @@ module Noosfero | ||
15 | # Example Request: | 15 | # Example Request: |
16 | # GET /articles/12/comments?oldest&limit=10&reference_id=23 | 16 | # GET /articles/12/comments?oldest&limit=10&reference_id=23 |
17 | get ":id/comments" do | 17 | get ":id/comments" do |
18 | - | ||
19 | - conditions = make_conditions_with_parameter(params) | ||
20 | article = find_article(environment.articles, params[:id]) | 18 | article = find_article(environment.articles, params[:id]) |
21 | - | ||
22 | - if params[:reference_id] | ||
23 | - comments = article.comments.send("#{params.key?(:oldest) ? 'older_than' : 'newer_than'}", params[:reference_id]).reorder("created_at DESC").find(:all, :conditions => conditions, :limit => limit) | ||
24 | - else | ||
25 | - comments = article.comments.reorder("created_at DESC").find(:all, :conditions => conditions, :limit => limit) | ||
26 | - end | 19 | + comments = select_filtered_collection_of(article, :comments, params) |
20 | + | ||
27 | present comments, :with => Entities::Comment | 21 | present comments, :with => Entities::Comment |
28 | - | ||
29 | end | 22 | end |
30 | - | 23 | + |
31 | get ":id/comments/:comment_id" do | 24 | get ":id/comments/:comment_id" do |
32 | article = find_article(environment.articles, params[:id]) | 25 | article = find_article(environment.articles, params[:id]) |
33 | present article.comments.find(params[:comment_id]), :with => Entities::Comment | 26 | present article.comments.find(params[:comment_id]), :with => Entities::Comment |
34 | end | 27 | end |
35 | - | 28 | + |
36 | # Example Request: | 29 | # Example Request: |
37 | - # POST api/v1/articles/12/comments?private_toke=234298743290432&body=new comment | 30 | + # POST api/v1/articles/12/comments?private_token=2298743290432&body=new comment&title=New |
38 | post ":id/comments" do | 31 | post ":id/comments" do |
39 | article = find_article(environment.articles, params[:id]) | 32 | article = find_article(environment.articles, params[:id]) |
40 | - present article.comments.create(:author => current_person, :body => params[:body]), :with => Entities::Comment | 33 | + options = params.select { |key,v| !['id','private_token'].include?(key) }.merge(:author => current_person) |
34 | + present article.comments.create(options), :with => Entities::Comment | ||
41 | end | 35 | end |
42 | end | 36 | end |
43 | - | 37 | + |
44 | end | 38 | end |
45 | end | 39 | end |
46 | end | 40 | end |
lib/noosfero/api/v1/communities.rb
@@ -3,9 +3,9 @@ module Noosfero | @@ -3,9 +3,9 @@ module Noosfero | ||
3 | module V1 | 3 | module V1 |
4 | class Communities < Grape::API | 4 | class Communities < Grape::API |
5 | before { authenticate! } | 5 | before { authenticate! } |
6 | - | 6 | + |
7 | resource :communities do | 7 | resource :communities do |
8 | - | 8 | + |
9 | # Collect comments from articles | 9 | # Collect comments from articles |
10 | # | 10 | # |
11 | # Parameters: | 11 | # Parameters: |
@@ -19,10 +19,11 @@ module Noosfero | @@ -19,10 +19,11 @@ module Noosfero | ||
19 | get do | 19 | get do |
20 | communities = select_filtered_collection_of(environment, 'communities', params) | 20 | communities = select_filtered_collection_of(environment, 'communities', params) |
21 | communities = communities.visible_for_person(current_person) | 21 | communities = communities.visible_for_person(current_person) |
22 | + communities = communities.by_location(params) # Must be the last. May return Exception obj. | ||
22 | present communities, :with => Entities::Community | 23 | present communities, :with => Entities::Community |
23 | end | 24 | end |
24 | - | ||
25 | - | 25 | + |
26 | + | ||
26 | # Example Request: | 27 | # Example Request: |
27 | # POST api/v1/communties?private_token=234298743290432&community[name]=some_name | 28 | # POST api/v1/communties?private_token=234298743290432&community[name]=some_name |
28 | post do | 29 | post do |
@@ -32,40 +33,40 @@ module Noosfero | @@ -32,40 +33,40 @@ module Noosfero | ||
32 | rescue | 33 | rescue |
33 | community = Community.new(params[:community]) | 34 | community = Community.new(params[:community]) |
34 | end | 35 | end |
35 | - | 36 | + |
36 | if !community.save | 37 | if !community.save |
37 | render_api_errors!(community.errors.full_messages) | 38 | render_api_errors!(community.errors.full_messages) |
38 | end | 39 | end |
39 | - | 40 | + |
40 | present community, :with => Entities::Community | 41 | present community, :with => Entities::Community |
41 | end | 42 | end |
42 | - | 43 | + |
43 | get ':id' do | 44 | get ':id' do |
44 | - community = environment.communities.visible.find_by_id(params[:id]) | 45 | + community = environment.communities.visible_for_person(current_person).find_by_id(params[:id]) |
45 | present community, :with => Entities::Community | 46 | present community, :with => Entities::Community |
46 | end | 47 | end |
47 | - | 48 | + |
48 | end | 49 | end |
49 | - | 50 | + |
50 | resource :people do | 51 | resource :people do |
51 | - | 52 | + |
52 | segment '/:person_id' do | 53 | segment '/:person_id' do |
53 | - | 54 | + |
54 | resource :communities do | 55 | resource :communities do |
55 | - | 56 | + |
56 | get do | 57 | get do |
57 | person = environment.people.find(params[:person_id]) | 58 | person = environment.people.find(params[:person_id]) |
58 | communities = select_filtered_collection_of(person, 'communities', params) | 59 | communities = select_filtered_collection_of(person, 'communities', params) |
59 | communities = communities.visible | 60 | communities = communities.visible |
60 | present communities, :with => Entities::Community | 61 | present communities, :with => Entities::Community |
61 | end | 62 | end |
62 | - | 63 | + |
63 | end | 64 | end |
64 | - | 65 | + |
65 | end | 66 | end |
66 | - | 67 | + |
67 | end | 68 | end |
68 | - | 69 | + |
69 | end | 70 | end |
70 | end | 71 | end |
71 | end | 72 | end |
lib/noosfero/api/v1/enterprises.rb
@@ -3,15 +3,16 @@ module Noosfero | @@ -3,15 +3,16 @@ module Noosfero | ||
3 | module V1 | 3 | module V1 |
4 | class Enterprises < Grape::API | 4 | class Enterprises < Grape::API |
5 | before { authenticate! } | 5 | before { authenticate! } |
6 | - | 6 | + |
7 | resource :enterprises do | 7 | resource :enterprises do |
8 | - | ||
9 | - # Collect comments from articles | 8 | + |
9 | + # Collect enterprises from environment | ||
10 | # | 10 | # |
11 | # Parameters: | 11 | # Parameters: |
12 | # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created | 12 | # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created |
13 | # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected | 13 | # oldest - Collect the oldest comments from reference_id comment. If nothing is passed the newest comments are collected |
14 | # limit - amount of comments returned. The default value is 20 | 14 | # limit - amount of comments returned. The default value is 20 |
15 | + # georef params - read `Profile.by_location` for more information. | ||
15 | # | 16 | # |
16 | # Example Request: | 17 | # Example Request: |
17 | # GET /enterprises?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10 | 18 | # GET /enterprises?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10 |
@@ -19,37 +20,38 @@ module Noosfero | @@ -19,37 +20,38 @@ module Noosfero | ||
19 | get do | 20 | get do |
20 | enterprises = select_filtered_collection_of(environment, 'enterprises', params) | 21 | enterprises = select_filtered_collection_of(environment, 'enterprises', params) |
21 | enterprises = enterprises.visible_for_person(current_person) | 22 | enterprises = enterprises.visible_for_person(current_person) |
23 | + enterprises = enterprises.by_location(params) # Must be the last. May return Exception obj. | ||
22 | present enterprises, :with => Entities::Enterprise | 24 | present enterprises, :with => Entities::Enterprise |
23 | end | 25 | end |
24 | - | 26 | + |
25 | desc "Return one enterprise by id" | 27 | desc "Return one enterprise by id" |
26 | get ':id' do | 28 | get ':id' do |
27 | - enterprise = environment.enterprises.visible.find_by_id(params[:id]) | 29 | + enterprise = environment.enterprises.visible_for_person(current_person).find_by_id(params[:id]) |
28 | present enterprise, :with => Entities::Enterprise | 30 | present enterprise, :with => Entities::Enterprise |
29 | end | 31 | end |
30 | - | 32 | + |
31 | end | 33 | end |
32 | - | 34 | + |
33 | resource :people do | 35 | resource :people do |
34 | - | 36 | + |
35 | segment '/:person_id' do | 37 | segment '/:person_id' do |
36 | - | 38 | + |
37 | resource :enterprises do | 39 | resource :enterprises do |
38 | - | 40 | + |
39 | get do | 41 | get do |
40 | person = environment.people.find(params[:person_id]) | 42 | person = environment.people.find(params[:person_id]) |
41 | enterprises = select_filtered_collection_of(person, 'enterprises', params) | 43 | enterprises = select_filtered_collection_of(person, 'enterprises', params) |
42 | - enterprises = enterprises.visible | 44 | + enterprises = enterprises.visible.by_location(params) |
43 | present enterprises, :with => Entities::Enterprise | 45 | present enterprises, :with => Entities::Enterprise |
44 | end | 46 | end |
45 | - | 47 | + |
46 | end | 48 | end |
47 | - | 49 | + |
48 | end | 50 | end |
49 | - | 51 | + |
50 | end | 52 | end |
51 | - | ||
52 | - | 53 | + |
54 | + | ||
53 | end | 55 | end |
54 | end | 56 | end |
55 | end | 57 | end |
@@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
1 | +module Noosfero | ||
2 | + module API | ||
3 | + module V1 | ||
4 | + class Environments < Grape::API | ||
5 | + | ||
6 | + resource :environment do | ||
7 | + | ||
8 | + desc "Return the person information" | ||
9 | + get '/signup_person_fields' do | ||
10 | + present environment.signup_person_fields | ||
11 | + end | ||
12 | + | ||
13 | + end | ||
14 | + | ||
15 | + end | ||
16 | + end | ||
17 | + end | ||
18 | +end |
lib/noosfero/api/v1/people.rb
@@ -3,10 +3,22 @@ module Noosfero | @@ -3,10 +3,22 @@ module Noosfero | ||
3 | module V1 | 3 | module V1 |
4 | class People < Grape::API | 4 | class People < Grape::API |
5 | before { authenticate! } | 5 | before { authenticate! } |
6 | - | 6 | + |
7 | + desc 'API Root' | ||
8 | + | ||
7 | resource :people do | 9 | resource :people do |
8 | - | ||
9 | - # Collect comments from articles | 10 | + |
11 | + # -- A note about privacy -- | ||
12 | + # We wold find people by location, but we must test if the related | ||
13 | + # fields are public. We can't do it now, with SQL, while the location | ||
14 | + # data and the fields_privacy are a serialized settings. | ||
15 | + # We must build a new table for profile data, where we can set meta-data | ||
16 | + # like: | ||
17 | + # | id | profile_id | key | value | privacy_level | source | | ||
18 | + # | 1 | 99 | city | Salvador | friends | user | | ||
19 | + # | 2 | 99 | lng | -38.521 | me only | automatic | | ||
20 | + | ||
21 | + # Collect people from environment | ||
10 | # | 22 | # |
11 | # Parameters: | 23 | # Parameters: |
12 | # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created | 24 | # from - date where the search will begin. If nothing is passed the default date will be the date of the first article created |
@@ -16,26 +28,68 @@ module Noosfero | @@ -16,26 +28,68 @@ module Noosfero | ||
16 | # Example Request: | 28 | # Example Request: |
17 | # GET /people?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10 | 29 | # GET /people?from=2013-04-04-14:41:43&until=2014-04-04-14:41:43&limit=10 |
18 | # GET /people?reference_id=10&limit=10&oldest | 30 | # GET /people?reference_id=10&limit=10&oldest |
31 | + | ||
32 | + desc "Find environment's people" | ||
19 | get do | 33 | get do |
20 | people = select_filtered_collection_of(environment, 'people', params) | 34 | people = select_filtered_collection_of(environment, 'people', params) |
21 | people = people.visible_for_person(current_person) | 35 | people = people.visible_for_person(current_person) |
22 | present people, :with => Entities::Person | 36 | present people, :with => Entities::Person |
23 | end | 37 | end |
24 | - | 38 | + |
39 | + desc "Return the logged user information" | ||
40 | + get "/me" do | ||
41 | + present current_person, :with => Entities::Person | ||
42 | + end | ||
43 | + | ||
25 | desc "Return the person information" | 44 | desc "Return the person information" |
26 | get ':id' do | 45 | get ':id' do |
27 | - person = environment.people.visible.find_by_id(params[:id]) | 46 | + person = environment.people.visible_for_person(current_person).find_by_id(params[:id]) |
47 | + return not_found! if person.blank? | ||
28 | present person, :with => Entities::Person | 48 | present person, :with => Entities::Person |
29 | end | 49 | end |
30 | - | 50 | + |
51 | + # Example Request: | ||
52 | + # POST api/v1/people?person[login]=some_login&person[password]=some_password&person[name]=Jack | ||
53 | + desc "Create person" | ||
54 | + post do | ||
55 | + user_data = {} | ||
56 | + user_data[:login] = params[:person].delete(:login) || params[:person][:identifier] | ||
57 | + user_data[:email] = params[:person].delete(:email) | ||
58 | + user_data[:password] = params[:person].delete(:password) | ||
59 | + user_data[:password_confirmation] = params[:person].delete(:password_confirmation) | ||
60 | + user = User.build(user_data, params[:person], environment) | ||
61 | + begin | ||
62 | + user.signup! | ||
63 | + rescue ActiveRecord::RecordInvalid | ||
64 | + render_api_errors!(user.errors.full_messages) | ||
65 | + end | ||
66 | + | ||
67 | + present user.person, :with => Entities::Person | ||
68 | + end | ||
69 | + | ||
31 | desc "Return the person friends" | 70 | desc "Return the person friends" |
32 | get ':id/friends' do | 71 | get ':id/friends' do |
33 | - friends = current_person.friends.visible | 72 | + person = environment.people.visible_for_person(current_person).find_by_id(params[:id]) |
73 | + return not_found! if person.blank? | ||
74 | + friends = person.friends.visible | ||
34 | present friends, :with => Entities::Person | 75 | present friends, :with => Entities::Person |
35 | end | 76 | end |
36 | - | 77 | + |
78 | + desc "Return the person permissions on other profiles" | ||
79 | + get ":id/permissions" do | ||
80 | + person = environment.people.find(params[:id]) | ||
81 | + return not_found! if person.blank? | ||
82 | + return forbidden! unless current_person == person || environment.admins.include?(current_person) | ||
83 | + | ||
84 | + output = {} | ||
85 | + person.role_assignments.map do |role_assigment| | ||
86 | + if role_assigment.resource.respond_to?(:identifier) | ||
87 | + output[role_assigment.resource.identifier] = role_assigment.role.permissions | ||
88 | + end | ||
89 | + end | ||
90 | + present output | ||
91 | + end | ||
37 | end | 92 | end |
38 | - | ||
39 | end | 93 | end |
40 | end | 94 | end |
41 | end | 95 | end |