Commit c54913e745b269468394ba18e92294ba3acb8ade

Authored by Ábner Silva de Oliveira
2 parents 3a45d3f4 973b71d6

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.

.gitlab-ci.yml 0 → 100644
@@ -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 +
@@ -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
.travis.yml 0 → 100644
@@ -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 +
@@ -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 &lt;hugo@riseup.net&gt; @@ -144,25 +149,29 @@ Hugo Melo &lt;hugo@riseup.net&gt;
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 &lt;jaodsilv@linux.ime.usp.br&gt; @@ -170,46 +179,44 @@ João M. M. da Silva + Pedro Leal &lt;jaodsilv@linux.ime.usp.br&gt;
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 &lt;paulo@softwarelivre.org&gt; @@ -217,8 +224,8 @@ Paulo Meirelles + Alessandro Palmeira &lt;paulo@softwarelivre.org&gt;
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 &lt;rr.manzo@gmail.com&gt; @@ -233,37 +240,46 @@ Rafael Reggiani Manzo + Diego Araújo &lt;rr.manzo@gmail.com&gt;
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 ===================================
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 &#39;exception_notification&#39;, &#39;~&gt; 4.0.1&#39; @@ -18,7 +18,7 @@ gem &#39;exception_notification&#39;, &#39;~&gt; 4.0.1&#39;
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 &#39;api-pagination&#39;, &#39;~&gt; 4.1.1&#39; @@ -27,6 +27,13 @@ gem &#39;api-pagination&#39;, &#39;~&gt; 4.1.1&#39;
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 &lt; ActionController::Base @@ -77,8 +77,8 @@ class ApplicationController &lt; 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 &lt; MyProfileController @@ -6,7 +6,7 @@ class CmsController &lt; 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 &lt; MyProfileController @@ -94,6 +94,11 @@ class CmsController &lt; 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 &lt; MyProfileController @@ -21,13 +22,17 @@ class TasksController &lt; 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 &lt; MyProfileController @@ -111,9 +116,9 @@ class TasksController &lt; 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 &lt; MyProfileController @@ -151,7 +156,29 @@ class TasksController &lt; 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 &lt; ApplicationController @@ -98,11 +98,8 @@ class AccountController &lt; 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
app/controllers/public/api_controller.rb 0 → 100644
@@ -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 ]
app/helpers/task_helper.rb 0 → 100644
@@ -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 &lt; Task @@ -14,6 +14,9 @@ class AddFriend &lt; 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 &lt; Task @@ -2,6 +2,9 @@ class AddMember &lt; 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 &lt; Task @@ -124,4 +138,9 @@ class ApproveArticle &lt; 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 &lt; ActiveRecord::Base @@ -133,19 +133,11 @@ class Article &lt; 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 &lt; ActiveRecord::Base @@ -748,8 +740,9 @@ class Article &lt; 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 &lt; ActiveRecord::Base @@ -820,6 +813,10 @@ class Article &lt; 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 &lt; Task @@ -18,6 +18,8 @@ class ChangePassword &lt; 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 &lt; ActiveRecord::Base @@ -20,16 +20,7 @@ class Comment &lt; 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 &lt; Task @@ -3,6 +3,9 @@ class CreateCommunity &lt; 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 &lt; Task @@ -27,6 +27,8 @@ class CreateEnterprise &lt; 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 &lt; ActiveRecord::Base @@ -17,6 +17,7 @@ class EmailTemplate &lt; 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 &lt; Task @@ -8,6 +8,8 @@ class EnterpriseActivation &lt; 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 &lt; ActiveRecord::Base @@ -326,6 +326,9 @@ class Environment &lt; 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 &lt; Article @@ -99,47 +99,19 @@ class Event &lt; 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 &lt; ActiveRecord::Base @@ -23,7 +23,7 @@ class Image &lt; 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 &lt; Task @@ -6,6 +6,8 @@ class Invitation &lt; 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 &lt; Task @@ -34,7 +36,7 @@ class Invitation &lt; 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 &lt; Task @@ -136,7 +138,11 @@ class Invitation &lt; 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 &lt; Task @@ -7,6 +7,8 @@ class ModerateUserRegistration &lt; 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 &lt; Profile @@ -8,13 +8,30 @@ class Organization &lt; 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 &lt; Profile @@ -154,6 +171,12 @@ class Organization &lt; 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 &lt; ActiveRecord::Base @@ -51,6 +51,25 @@ class Product &lt; 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 &lt; ActiveRecord::Base @@ -129,15 +129,49 @@ class Profile &lt; 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 &lt; ActiveRecord::Base @@ -170,9 +204,9 @@ class Profile &lt; 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 &lt; ActiveRecord::Base @@ -219,6 +253,7 @@ class Profile &lt; 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 &lt; ActiveRecord::Base @@ -11,6 +11,12 @@ class Qualifier &lt; 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 &lt; Task @@ -41,6 +41,7 @@ class SuggestArticle &lt; 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 &lt; Task @@ -91,4 +92,5 @@ class SuggestArticle &lt; 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 &lt; ActiveRecord::Base @@ -70,14 +70,28 @@ class Task &lt; 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 &lt; ActiveRecord::Base @@ -119,6 +133,51 @@ class Task &lt; 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 &lt; ActiveRecord::Base @@ -256,7 +315,7 @@ class Task &lt; 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 &lt; ActiveRecord::Base @@ -278,6 +337,10 @@ class Task &lt; 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 &lt; ActiveRecord::Base @@ -34,6 +34,14 @@ class User &lt; 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 &lt; ActiveRecord::Base @@ -68,7 +76,8 @@ class User &lt; 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 &lt; ActiveRecord::Base @@ -93,18 +102,20 @@ class User &lt; 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 &lt; ActiveRecord::Base @@ -120,16 +131,15 @@ class User &lt; 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 &lt; ActiveRecord::Base @@ -333,6 +343,8 @@ class User &lt; 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 &lt; ActiveRecord::Base @@ -354,19 +366,6 @@ class User &lt; 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
app/views/api/index.html.erb 0 → 100644
@@ -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" %>
app/views/api/playground.html.erb 0 → 100644
@@ -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">&nbsp; <%=_('Run')%> &nbsp;</button>
  33 +</div>
  34 +<pre id="api-response" class="empty"></pre>
  35 +
  36 +<script src="/javascripts/api-playground.js"></script>
app/views/content_viewer/_article_title.html.erb 0 → 100644
@@ -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
@@ -16,6 +16,6 @@ @@ -16,6 +16,6 @@
16 </div> 16 </div>
17 <% end %> 17 <% end %>
18 <div class = <%= className %> > 18 <div class = <%= className %> >
19 - <%= article.abstract.truncate(400) %> 19 + <%= article.lead(400) %>
20 </div> 20 </div>
21 </div> 21 </div>
app/views/content_viewer/_publishing_info.html.erb 0 → 100644
@@ -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 %>
app/views/content_viewer/event_page.html.erb 0 → 100644
@@ -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
1 -<h1>Editing Email Template</h1> 1 +<h1><%= _('Editing Email Template') %></h1>
2 2
3 <%= render 'form' %> 3 <%= render 'form' %>
app/views/email_templates/new.html.erb
1 -<h1>New Email Template</h1> 1 +<h1><%= _('New Email Template') %></h1>
2 2
3 <%= render 'form' %> 3 <%= render 'form' %>
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 &nbsp; &#151; &nbsp; 54 &nbsp; &#151; &nbsp;
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>
@@ -6,6 +6,13 @@ require ::File.expand_path(&#39;../config/environment&#39;, __FILE__) @@ -6,6 +6,13 @@ require ::File.expand_path(&#39;../config/environment&#39;, __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
config/database.yml.gitlab-ci 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +test: &TEST
  2 + adapter: postgresql
  3 + database: gitlab_ci_test
  4 + username: gitlab_ci_runner
  5 +development:
  6 + <<: *TEST
config/database.yml.travis 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +# From http://about.travis-ci.org/docs/user/database-setup/
  2 +test:
  3 + adapter: postgresql
  4 + database: myapp_test
  5 + username: postgres
config/environments/staging.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +# inherit from production
  2 +require_relative 'production'
  3 +
  4 +Noosfero::Application.configure do
  5 +
  6 + # expose errors
  7 + config.consider_all_requests_local = true
  8 +
  9 + # ease debug
  10 + config.assets.compress = false
  11 +
  12 +end
  13 +
config/initializers/eager_load.rb 0 → 100644
@@ -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/en.yml 0 → 120000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +en-US.yml
0 \ No newline at end of file 2 \ No newline at end of file
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-FR.yml 0 → 120000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +fr.yml
0 \ No newline at end of file 2 \ No newline at end of file
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
@@ -176,4 +176,4 @@ @@ -176,4 +176,4 @@
176 template: 176 template:
177 <<: *errors_template 177 <<: *errors_template
178 full_messages: 178 full_messages:
179 - format: "%{attribute} %{message}"  
180 \ No newline at end of file 179 \ No newline at end of file
  180 + format: "%{attribute} %{message}"
config/locales/hy-AM.yml 0 → 120000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +hy.yml
0 \ No newline at end of file 2 \ No newline at end of file
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/pt.yml 0 → 120000
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +pt-BR.yml
0 \ No newline at end of file 2 \ No newline at end of file
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/20150603182105_add_label_to_image.rb 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +class AddLabelToImage < ActiveRecord::Migration
  2 + def up
  3 + add_column :images, :label, :string, :default => ""
  4 + end
  5 + def down
  6 + remove_column :images, :label
  7 + end
  8 +end
db/migrate/20150712194411_change_task_end_date_from_date_to_date_time.rb 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +class ChangeTaskEndDateFromDateToDateTime < ActiveRecord::Migration
  2 +
  3 + def up
  4 + change_column :tasks, :end_date, :datetime
  5 + end
  6 +
  7 + def down
  8 + change_column :tasks, :end_date, :date
  9 + end
  10 +end
@@ -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 =&gt; 20150602142030) do @@ -377,6 +377,7 @@ ActiveRecord::Schema.define(:version =&gt; 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 &lt; Struct.new(:people_ids, :profile_id, :locale) @@ -4,7 +4,12 @@ class AddMembersJob &lt; 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
lib/noosfero/api/v1/environments.rb 0 → 100644
@@ -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