Commit 2b942cdfed60a2df7dcc906b18ab495c55d2666f

Authored by Ábner Silva de Oliveira
2 parents ef5a4c0a e5ecf4b1

merge with noosfero api branch

Showing 190 changed files with 10340 additions and 8390 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 190 files displayed.

.gitlab-ci.yml 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +before_script:
  2 + - mkdir -p tmp/pids log
  3 + - script/noosfero-plugins disableall
  4 + - bundle check || bundle install
  5 +# database
  6 + - cp config/database.yml.gitlab-ci config/database.yml
  7 + - createdb gitlab_ci_test || true
  8 + - bundle exec rake db:schema:load
  9 + - bundle exec rake db:migrate
  10 +
  11 +units:
  12 + script: 'bundle exec rake test:units'
  13 +functionals:
  14 + script: 'bundle exec rake test:functionals'
  15 +integration:
  16 + script: 'bundle exec rake test:integration'
  17 +cucumber:
  18 + script: 'bundle exec rake cucumber'
  19 +selenium:
  20 + script: 'bundle exec rake selenium'
  21 +plugins:
  22 + script: 'bundle exec rake test:noosfero_plugins'
  23 +
... ...
.travis.yml 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +language: ruby
  2 +rvm:
  3 +# for 2.2 support we need to upgrade the pg gem
  4 + - 2.1.6
  5 +
  6 +before_install:
  7 +# dependencies
  8 + - sudo apt-get update
  9 + - sudo apt-get -y install po4a iso-codes tango-icon-theme pidgin-data openjdk-6-jre curl wget
  10 + - sudo apt-get -y install libmagickwand-dev libpq-dev libreadline-dev libsqlite3-dev libxslt1-dev
  11 +# selenium support
  12 + - export DISPLAY=:99.0
  13 + - sh -e /etc/init.d/xvfb start
  14 +
  15 +before_script:
  16 + - mkdir -p tmp/pids log
  17 + - script/noosfero-plugins disableall
  18 + - bundle check || bundle install
  19 +# database
  20 + - cp config/database.yml.travis config/database.yml
  21 + - psql -c 'create database myapp_test;' -U postgres
  22 + - bundle exec rake db:schema:load
  23 + - bundle exec rake db:migrate
  24 +
  25 +env:
  26 + - TASK=test:units
  27 + - TASK=test:functionals
  28 + - TASK=test:integration
  29 + - TASK=cucumber
  30 + - TASK=selenium
  31 + - TASK=test:noosfero_plugins
  32 +
  33 +script:
  34 + - bundle exec rake $TASK
  35 +
... ...
AUTHORS.md
... ... @@ -6,131 +6,132 @@ noosfero, that's not a problem).
6 6 Developers
7 7 ==========
8 8  
9   -Ábner Silva de Oliveira <abner.oliveira@serpro.gov.br>
10 9 Alan Freihof Tygel <alantygel@gmail.com>
11   -alcampelo <alcampelo@alcampelo.(none)>
12   -Alessandro Palmeira <alessandro.palmeira@gmail.com>
13 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 12 Alessandro Palmeira + Caio Salgado <alessandro.palmeira@gmail.com>
15 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 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 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 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 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 24 Alessandro Palmeira + Diego Araújo + João M. M. da Silva <alessandro.palmeira@gmail.com>
28 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 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 30 Alessandro Palmeira + Eduardo Morais <alessandro.palmeira@gmail.com>
34 31 Alessandro Palmeira + Guilherme Rojas <alessandro.palmeira@gmail.com>
35 32 Alessandro Palmeira + Jefferson Fernandes <alessandro.palmeira@gmail.com>
36   -Alessandro Palmeira + João M. M. da Silva <alessandro.palmeira@gmail.com>
37 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 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 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 39 Alessandro Palmeira + Rafael Manzo <alessandro.palmeira@gmail.com>
43   -analosnak <analosnak@gmail.com>
  40 +Alessandro Palmeira <alessandro.palmeira@gmail.com>
44 41 Ana Losnak <analosnak@gmail.com>
45 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 46 Antonio Terceiro + Carlos Morais <terceiro@colivre.coop.br>
47 47 Antonio Terceiro + Paulo Meirelles <terceiro@colivre.coop.br>
48 48 Antonio Terceiro <terceiro@colivre.coop.br>
49 49 Arthur Del Esposte <arthurmde@gmail.com>
50 50 Arthur Del Esposte <arthurmde@yahoo.com.br>
  51 +Athos Ribeiro <athoscribeiro@gmail.com>
51 52 Aurelio A. Heckert <aurelio@colivre.coop.br>
  53 +Braulio Bhavamitra <braulio@eita.org.br>
52 54 Braulio Bhavamitra <brauliobo@gmail.com>
53 55 Bráulio Bhavamitra <brauliobo@gmail.com>
54   -Braulio Bhavamitra <braulio@eita.org.br>
55   -Caio <caio.csalgado@gmail.com>
56 56 Caio + Diego + Pedro + João <caio.csalgado@gmail.com>
  57 +Caio <caio.csalgado@gmail.com>
57 58 Caio Formiga <caio.formiga@gmail.com>
58   -Caio, Pedro <caio.csalgado@gmail.com>
  59 +Caio SBA <caio@colivre.coop.br>
59 60 Caio Salgado + Alessandro Palmeira <caio.csalgado@gmail.com>
60   -Caio Salgado <caio.csalgado@gmail.com>
61 61 Caio Salgado + Carlos Morais + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
62 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 63 Caio Salgado + Diego Araújo + Jefferson Fernandes <caio.csalgado@gmail.com>
66 64 Caio Salgado + Diego Araújo + João M. M. da Silva <caio.csalgado@gmail.com>
67 65 Caio Salgado + Diego Araújo + Pedro Leal <caio.csalgado@gmail.com>
68 66 Caio Salgado + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
69 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 70 Caio Salgado + Jefferson Fernandes <caio.csalgado@gmail.com>
71 71 Caio Salgado + Jefferson Fernandes <jeffs.fernandes@gmail.com>
72 72 Caio Salgado + Rafael Manzo <caio.csalgado@gmail.com>
  73 +Caio Salgado + Renan Teruo + Jefferson Fernandes <jeffs.fernandes@gmail.com>
73 74 Caio Salgado + Renan Teruo <caio.csalgado@gmail.com>
74 75 Caio Salgado + Renan Teruo <caio.salgado@gmail.com>
75   -Caio Salgado + Renan Teruo + Jefferson Fernandes <jeffs.fernandes@gmail.com>
76 76 Caio Salgado + Renan Teruo <renanteruoc@gmail.com>
77   -Caio SBA <caio@colivre.coop.br>
  77 +Caio Salgado <caio.csalgado@gmail.com>
78 78 Caio Tiago Oliveira <caiotiago@colivre.coop.br>
  79 +Caio, Pedro <caio.csalgado@gmail.com>
79 80 Carlos Andre de Souza <carlos.andre.souza@msn.com>
80   -Carlos Morais <carlos88morais@gmail.com>
81 81 Carlos Morais + Diego Araújo <diegoamc90@gmail.com>
82 82 Carlos Morais + Eduardo Morais <carlos88morais@gmail.com>
83 83 Carlos Morais + Paulo Meirelles <carlos88morais@gmail.com>
84 84 Carlos Morais + Pedro Leal <carlos88morais@gmail.com>
85   -Daniel Alves + Diego Araújo <danpaulalves@gmail.com>
86   -Daniel Alves + Diego Araújo <diegoamc90@gmail.com>
  85 +Carlos Morais <carlos88morais@gmail.com>
87 86 Daniel Alves + Diego Araújo + Guilherme Rojas <danpaulalves@gmail.com>
88 87 Daniel Alves + Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com>
89 88 Daniel Alves + Diego Araújo + Guilherme Rojas <guilhermehrojas@gmail.com>
  89 +Daniel Alves + Diego Araújo <danpaulalves@gmail.com>
  90 +Daniel Alves + Diego Araújo <diegoamc90@gmail.com>
90 91 Daniel Alves + Guilherme Rojas <danpaulalves@gmail.com>
91 92 Daniel Alves + Rafael Manzo <rr.manzo@gmail.com>
92   -Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>
93 93 Daniel Bucher <daniel.bucher88@gmail.com>
94 94 Daniel Cunha <daniel@colivre.coop.br>
95   -daniel <dtygel@eita.org.br>
  95 +Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>
96 96 David Carlos <ddavidcarlos1392@gmail.com>
97   -diegoamc <diegoamc90@gmail.com>
98   -Diego Araújo + Alessandro Palmeira <diegoamc90@gmail.com>
  97 +Diego + Jefferson <diegoamc90@gmail.com>
  98 +Diego + Renan <renanteruoc@gmail.com>
  99 +Diego Araujo + Caio Salgado <diegoamc90@gmail.com>
  100 +Diego Araujo + Jefferson Fernandes <jeffs.fernandes@gmail.com>
  101 +Diego Araujo + Rafael Manzo <diegoamc90@gmail.com>
  102 +Diego Araujo + Rodrigo Souto + Rafael Manzo <rr.manzo@gmail.com>
99 103 Diego Araújo + Alessandro Palmeira + João M. M. da Silva <diegoamc90@gmail.com>
100 104 Diego Araújo + Alessandro Palmeira + Rafael Manzo <rr.manzo@gmail.com>
101   -Diego Araujo + Caio Salgado <diegoamc90@gmail.com>
  105 +Diego Araújo + Alessandro Palmeira <diegoamc90@gmail.com>
102 106 Diego Araújo + Daniel Alves + Rafael Manzo <rr.manzo@gmail.com>
103   -Diego Araújo <diegoamc90@gmail.com>
104 107 Diego Araújo + Eduardo Morais + Paulo Meirelles <diegoamc90@gmail.com>
105 108 Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com>
106 109 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 110 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 111 Diego Araújo + João M. M. da Silva + João Machini <diegoamc90@gmail.com>
113 112 Diego Araújo + João M. M. da Silva + Pedro Leal <diegoamc90@gmail.com>
  113 +Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com>
  114 +Diego Araújo + João Machini <diegoamc90@gmail.com>
  115 +Diego Araújo + João Machini <digoamc90@gmail.com>
114 116 Diego Araújo + Paulo Meirelles <diegoamc90@gmail.com>
115 117 Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
116   -Diego Araujo + Rafael Manzo <diegoamc90@gmail.com>
117 118 Diego Araújo + Rafael Manzo <diegoamc90@gmail.com>
118 119 Diego Araújo + Renan Teruo + Alessandro Palmeira <diegoamc90@gmail.com>
119 120 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>
  121 +Diego Araújo <diegoamc90@gmail.com>
122 122 Diego Martinez <diegoamc90@gmail.com>
123   -Diego + Renan <renanteruoc@gmail.com>
124   -dtygel <dtygel@gmail.com>
125 123 DylanGuedes <djmgguedes@gmail.com>
126 124 Eduardo Passos <eduardo@risa.localdomain.localhost>
127 125 Eduardo Passos <eduardosteps@gmail.com>
128 126 Eduardo Tourinho Edington <eduardo.edington@serpro.gov.br>
  127 +Eduardo Vital <vitaldu@gmail.com>
129 128 Evandro Jr <evandrojr@gmail.com>
130 129 Evandro Junior <evandrojr@gmail.com>
131   -Fabio Teixeira <fabio1079@gmail.com>
  130 +Evandro Magalhaes Leite Junior <evandro.leite@serpro.gov.br>
132 131 FAMMA TV NOTICIAS MEDIOS DE CO <revistafammatvmusic.oficial@gmail.com>
  132 +Fabio Teixeira <fabio1079@gmail.com>
133 133 Fernanda Lopes <nanda.listas+psl@gmail.com>
  134 +Filipe Ribeiro <firibeiro77@live.com>
134 135 Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br>
135 136 Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)>
136 137 Francisco Marcelo de Araújo Lima Júnior <francisco.lima-junior@serpro.gov.br>
... ... @@ -144,25 +145,29 @@ Hugo Melo &lt;hugo@riseup.net&gt;
144 145 Isaac Canan <isaac@intelletto.com.br>
145 146 Italo Valcy <italo@dcc.ufba.br>
146 147 Jefferson Fernandes + Diego Araujo + Rafael Manzo <jeffs.fernandes@gmail.com>
147   -Jefferson Fernandes + Joao M. M. da Silva <jeffs.fernandes@gmail.com>
148 148 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>
  149 +Jefferson Fernandes + Joao M. M. da Silva <jeffs.fernandes@gmail.com>
  150 +Joao M. M. Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>
  151 +Joao M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>
  152 +Joao M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>
  153 +Joenio Costa <joenio@colivre.coop.br>
  154 +Jose Pedro <1jpsneto@gmail.com>
  155 +Josef Spillner <josef.spillner@tu-dresden.de>
  156 +João M. M. Silva + Caio Salgado <jaodsilv@linux.ime.usp.br>
  157 +João M. M. Silva + Diego Araújo <jaodsilv@linux.ime.usp.br>
  158 +João M. M. Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br>
  159 +João M. M. Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br>
  160 +João M. M. Silva + Renan Teruo <jaodsilv@linux.ime.usp.br>
153 161 João M. M. da Silva + Alessandro Palmeira + Diego Araújo + Caio Salgado <jaodsilv@linux.ime.usp.br>
154 162 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 163 João M. M. da Silva + Alessandro Palmeira + João Machini <jaodsilv@linux.ime.usp.br>
  164 +João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>
158 165 João M. M. da Silva + Caio Salgado + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>
159 166 João M. M. da Silva + Caio Salgado <jaodsilv@linux.ime.usp.br>
160 167 João M. M. da Silva + Carlos Morais <jaodsilv@linux.ime.usp.br>
  168 +João M. M. da Silva + Diego Araújo + Pedro Leal <jaodsilv@linux.ime.usp.br>
161 169 João M. M. da Silva + Diego Araújo <diegoamc90@gmail.com>
162 170 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 171 João M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>
167 172 João M. M. da Silva + João M. Miranda <jaodsilv@linux.ime.usp.br>
168 173 João M. M. da Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br>
... ... @@ -170,46 +175,44 @@ João M. M. da Silva + Pedro Leal &lt;jaodsilv@linux.ime.usp.br&gt;
170 175 João M. M. da Silva + Rafael Manzo + Diego Araújo <jaodsilv@linux.ime.usp.br>
171 176 João M. M. da Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br>
172 177 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>
  178 +João M. M. da Silva <jaodsilv@linux.ime.usp.br>
  179 +João Marco Maciel da Silva + Rafael Manzo + Renan Teruo <jaodsilv@linux.ime.usp.br>
  180 +João da Silva + Eduardo Morais + Rafael Manzo <rr.manzo@gmail.com>
  181 +João da Silva <jaodsilv@linux.ime.usp.br>
182 182 Junior Silva <junior@bajor.localhost.localdomain>
183 183 Junior Silva <junior@sedeantigo.colivre.coop.br>
184 184 Junior Silva <juniorsilva1001@gmail.com>
185 185 Junior Silva <juniorsilva7@juniorsilva-Aspire-5750Z.(none)>
186 186 Junior Silva <juniorsilva@colivre.coop.br>
187   -juniorsilva <juniorsilva@QonoS.localhost.localdomain>
  187 +Jérôme Jutteau <j.jutteau@gmail.com>
188 188 Keilla Menezes <keilla@colivre.coop.br>
189 189 Larissa Reis <larissa@colivre.coop.br>
190 190 Larissa Reis <reiss.larissa@gmail.com>
191 191 Leandro Alves <leandrosustenido@gmail.com>
192   -Leandro Nunes dos Santos <81665687568@serpro-1541727.Home>
193 192 Leandro Nunes dos Santos <81665687568@serpro-1541727.(none)>
194   -Leandro Nunes dos Santos <leandronunes@gmail.com>
  193 +Leandro Nunes dos Santos <81665687568@serpro-1541727.Home>
195 194 Leandro Nunes dos Santos <leandro.santos@serpro.gov.br>
  195 +Leandro Nunes dos Santos <leandronunes@gmail.com>
196 196 LinguÁgil 2010 <linguagil.bahia@gmail.com>
197 197 Lucas Kanashiro <kanashiro.duarte@gmail.com>
198 198 Lucas Melo <lucas@colivre.coop.br>
199 199 Lucas Melo <lucaspradomelo@gmail.com>
200 200 Luciano <lucianopcbr@gmail.com>
  201 +Luciano Prestes Cavacanti <lucianopcbr@gmail.com>
201 202 Luciano Prestes Cavalcanti <lucianopcbr@gmail.com>
202 203 Luis David Aguilar Carlos <ludwig9003@gmail.com>
203 204 Luiz Fernando de Freitas Matos <luiz@luizff.matos@gmail.com>
  205 +M for Momo <mo@rtnp.org>
  206 +Marcelo Júnior <maljunior@gmail.com>
204 207 Marcos <marcos.rpj2@gmail.com>
205 208 Marcos Ramos <ms.ramos@outlook.com>
206 209 Martín Olivera <molivera@solar.org.ar>
207 210 Maurilio Atila <cabelotaina@gmail.com>
208   -M for Momo <mo@rtnp.org>
209 211 Michal Čihař <michal@cihar.com>
  212 +Michel Felipe <mfelipeof@gmail.com>
210 213 Moises Machado <moises@colivre.coop.br>
211   -Naíla Alves <naila@colivre.coop.br>
212 214 Nanda Lopes <nanda.listas+psl@gmail.com>
  215 +Naíla Alves <naila@colivre.coop.br>
213 216 Niemand Jedermann <predatorix@web.de>
214 217 Parley Martins <parleypachecomartins@gmail.com>
215 218 Paulo Meirelles + Alessandro Palmeira + João M. M. da Silva <paulo@softwarelivre.org>
... ... @@ -217,8 +220,8 @@ Paulo Meirelles + Alessandro Palmeira &lt;paulo@softwarelivre.org&gt;
217 220 Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org>
218 221 Paulo Meirelles + Diego Araújo <paulo@softwarelivre.org>
219 222 Paulo Meirelles + João M. M. da Silva <paulo@softwarelivre.org>
220   -Paulo Meirelles <paulo@softwarelivre.org>
221 223 Paulo Meirelles + Rafael Manzo <paulo@softwarelivre.org>
  224 +Paulo Meirelles <paulo@softwarelivre.org>
222 225 Rafael Gomes <rafaelgomes@techfree.com.br>
223 226 Rafael Manzo + Alessandro Palmeira <rr.manzo@gmail.com>
224 227 Rafael Manzo + Daniel Alves <danpaulalves@gmail.com>
... ... @@ -233,37 +236,46 @@ Rafael Reggiani Manzo + Diego Araújo &lt;rr.manzo@gmail.com&gt;
233 236 Rafael Reggiani Manzo + João M. M. da Silva <rr.manzo@gmail.com>
234 237 Rafael Reggiani Manzo <rr.manzo@gmail.com>
235 238 Raphaël Rousseau <raph@r4f.org>
236   -Raquel Lira <raquel.lira@gmail.com>
237 239 Raquel <rcordioli@gmail.com>
  240 +Raquel Lira <raquel.lira@gmail.com>
238 241 Renan Teruo + Caio Salgado <renanteruoc@gmail.com>
239   -Renan Teruoc + Diego Araujo <renanteruoc@gmail.com>
240 242 Renan Teruo + Diego Araujo <renanteruoc@gmail.com>
241 243 Renan Teruo + Diego Araújo <renanteruoc@gmail.com>
242 244 Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com>
243 245 Renan Teruo + Rafael Manzo <renanteruoc@gmail.com>
  246 +Renan Teruoc + Diego Araujo <renanteruoc@gmail.com>
244 247 Rodrigo Souto + Ana Losnak + Daniel Bucher + Caio Almeida + Leandro Nunes + Daniela Feitosa + Mariel Zasso <noosfero-br@listas.softwarelivre.org>
245 248 Rodrigo Souto <rodrigo@colivre.coop.br>
246 249 Ronny Kursawe <kursawe.ronny@googlemail.com>
247   -root <root@debian.sdr.serpro>
248 250 Samuel R. C. Vale <srcvale@holoscopio.com>
  251 +TWS <tablettws@gmail.com>
249 252 Tallys Martins <tallysmartins@gmail.com>
250 253 Tallys Martins <tallysmartins@yahoo.com.br>
251   -tallys <tallys@tallys>
252   -tallys <tallys@tallys.(none)>
253 254 Thiago Casotti <thiago.casotti@uol.com.br>
  255 +Thiago Ribeiro <thiagitosouza@hotmail.com>
254 256 Thiago Zoroastro <thiago.zoroastro@bol.com.br>
255 257 Tuux <tuxa@galaxie.eu.org>
256   -TWS <tablettws@gmail.com>
257 258 Valessio Brito <contato@valessiobrito.com.br>
258 259 Valessio Brito <contato@valessiobrito.info>
259 260 Valessio Brito <valessio@gmail.com>
260   -vfcosta <vfcosta@gmail.com>
261 261 Victor Carvalho <victorhugodf.ac@gmail.com>
262 262 Victor Costa <vfcosta@gmail.com>
263 263 Victor Hugo Alves de Carvalho <victorhugodf.ac@gmail.com>
264 264 Vinicius Cubas Brand <viniciuscb@gmail.com>
265 265 Visita <visita@debian.(none)>
266 266 Yann Lugrin <yann.lugrin@liquid-concept.ch>
  267 +alcampelo <alcampelo@alcampelo.(none)>
  268 +analosnak <analosnak@gmail.com>
  269 +daniel <dtygel@eita.org.br>
  270 +diegoamc <diegoamc90@gmail.com>
  271 +dtygel <dtygel@gmail.com>
  272 +juniorsilva <juniorsilva@QonoS.localhost.localdomain>
  273 +root <root@17edebf1ae91>
  274 +root <root@debian.sdr.serpro>
  275 +tallys <tallys@tallys.(none)>
  276 +tallys <tallys@tallys>
  277 +vfcosta <vfcosta@gmail.com>
  278 +Ábner Silva de Oliveira <abner.oliveira@serpro.gov.br>
267 279  
268 280 Ideas, specifications and incentive
269 281 ===================================
... ...
Gemfile
1 1 source "https://rubygems.org"
2   -gem 'rails', '~> 3.2.21'
  2 +gem 'rails', '~> 3.2.22'
3 3 gem 'minitest', '~> 3.2.0'
4 4 gem 'fast_gettext', '~> 0.6.8'
5 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 18 gem 'gettext', '~> 2.2.1', :require => false
19 19 gem 'locale', '~> 2.0.5'
20 20 gem 'whenever', :require => false
21   -gem 'eita-jrails', '>= 0.9.5', :require => 'jrails'
  21 +gem 'eita-jrails', '= 0.9.5', :require => 'jrails'
22 22 gem 'grape', '~> 0.11.0'
23 23 gem 'grape-entity'
24 24 gem 'grape-swagger'
... ... @@ -26,6 +26,10 @@ gem &#39;grape_logging&#39;
26 26 gem 'api-pagination', '~> 4.1.1'
27 27 gem 'rack-cors'
28 28 gem 'rack-contrib'
  29 +#gem 'grape-swagger-rails'
  30 +
  31 +# FIXME list here all actual dependencies (i.e. the ones in debian/control),
  32 +# with their GEM names (not the Debian package names)
29 33  
30 34 # asset pipeline
31 35 gem 'uglifier', '>= 1.0.3'
... ...
Gemfile.lock 0 → 100644
... ... @@ -0,0 +1,277 @@
  1 +GEM
  2 + remote: https://rubygems.org/
  3 + specs:
  4 + RedCloth (4.2.9)
  5 + actionmailer (3.2.22)
  6 + actionpack (= 3.2.22)
  7 + mail (~> 2.5.4)
  8 + actionpack (3.2.22)
  9 + activemodel (= 3.2.22)
  10 + activesupport (= 3.2.22)
  11 + builder (~> 3.0.0)
  12 + erubis (~> 2.7.0)
  13 + journey (~> 1.0.4)
  14 + rack (~> 1.4.5)
  15 + rack-cache (~> 1.2)
  16 + rack-test (~> 0.6.1)
  17 + sprockets (~> 2.2.1)
  18 + activemodel (3.2.22)
  19 + activesupport (= 3.2.22)
  20 + builder (~> 3.0.0)
  21 + activerecord (3.2.22)
  22 + activemodel (= 3.2.22)
  23 + activesupport (= 3.2.22)
  24 + arel (~> 3.0.2)
  25 + tzinfo (~> 0.3.29)
  26 + activeresource (3.2.22)
  27 + activemodel (= 3.2.22)
  28 + activesupport (= 3.2.22)
  29 + activesupport (3.2.22)
  30 + i18n (~> 0.6, >= 0.6.4)
  31 + multi_json (~> 1.0)
  32 + acts-as-taggable-on (3.4.4)
  33 + activerecord (>= 3.2, < 5)
  34 + api-pagination (4.1.1)
  35 + arel (3.0.3)
  36 + axiom-types (0.1.1)
  37 + descendants_tracker (~> 0.0.4)
  38 + ice_nine (~> 0.11.0)
  39 + thread_safe (~> 0.3, >= 0.3.1)
  40 + builder (3.0.4)
  41 + capybara (2.1.0)
  42 + mime-types (>= 1.16)
  43 + nokogiri (>= 1.3.3)
  44 + rack (>= 1.0.0)
  45 + rack-test (>= 0.5.4)
  46 + xpath (~> 2.0)
  47 + childprocess (0.5.6)
  48 + ffi (~> 1.0, >= 1.0.11)
  49 + chronic (0.10.2)
  50 + coercible (1.0.0)
  51 + descendants_tracker (~> 0.0.1)
  52 + columnize (0.9.0)
  53 + cucumber (1.0.6)
  54 + builder (>= 2.1.2)
  55 + diff-lcs (>= 1.1.2)
  56 + gherkin (~> 2.4.18)
  57 + json (>= 1.4.6)
  58 + term-ansicolor (>= 1.0.6)
  59 + cucumber-rails (1.0.6)
  60 + capybara (>= 1.1.1)
  61 + cucumber (>= 1.0.6)
  62 + nokogiri (>= 1.5.0)
  63 + daemons (1.1.9)
  64 + dalli (2.7.4)
  65 + database_cleaner (1.2.0)
  66 + debugger (1.6.8)
  67 + columnize (>= 0.3.1)
  68 + debugger-linecache (~> 1.2.0)
  69 + debugger-ruby_core_source (~> 1.3.5)
  70 + debugger-linecache (1.2.0)
  71 + debugger-ruby_core_source (1.3.8)
  72 + descendants_tracker (0.0.4)
  73 + thread_safe (~> 0.3, >= 0.3.1)
  74 + diff-lcs (1.2.5)
  75 + eita-jrails (0.9.5)
  76 + actionpack (~> 3.2, >= 3.1.0)
  77 + activesupport (~> 3.2, >= 3.0.0)
  78 + equalizer (0.0.11)
  79 + erubis (2.7.0)
  80 + eventmachine (1.0.7)
  81 + exception_notification (4.0.1)
  82 + actionmailer (>= 3.0.4)
  83 + activesupport (>= 3.0.4)
  84 + execjs (2.5.2)
  85 + fast_gettext (0.6.12)
  86 + ffi (1.9.10)
  87 + gettext (2.2.1)
  88 + locale
  89 + gherkin (2.4.21)
  90 + json (>= 1.4.6)
  91 + git-version-bump (0.15.1)
  92 + grape (0.11.0)
  93 + activesupport
  94 + builder
  95 + hashie (>= 2.1.0)
  96 + multi_json (>= 1.3.2)
  97 + multi_xml (>= 0.5.2)
  98 + rack (>= 1.3.0)
  99 + rack-accept
  100 + rack-mount
  101 + virtus (>= 1.0.0)
  102 + grape-entity (0.4.5)
  103 + activesupport
  104 + multi_json (>= 1.3.2)
  105 + grape-swagger (0.10.1)
  106 + grape (>= 0.8.0)
  107 + grape-entity
  108 + grape_logging (1.1.2)
  109 + grape
  110 + hashie (3.4.2)
  111 + hike (1.2.3)
  112 + i18n (0.7.0)
  113 + ice_nine (0.11.1)
  114 + journey (1.0.4)
  115 + json (1.8.3)
  116 + locale (2.0.9)
  117 + magic (0.2.9)
  118 + ffi (>= 0.6.3)
  119 + mail (2.5.4)
  120 + mime-types (~> 1.16)
  121 + treetop (~> 1.4.8)
  122 + metaclass (0.0.4)
  123 + mime-types (1.25.1)
  124 + mini_portile (0.6.2)
  125 + minitest (3.2.0)
  126 + mocha (1.1.0)
  127 + metaclass (~> 0.0.1)
  128 + multi_json (1.11.2)
  129 + multi_xml (0.5.5)
  130 + nokogiri (1.6.6.2)
  131 + mini_portile (~> 0.6.0)
  132 + pg (0.13.2)
  133 + polyglot (0.3.5)
  134 + rack (1.4.7)
  135 + rack-accept (0.4.5)
  136 + rack (>= 0.4)
  137 + rack-cache (1.2)
  138 + rack (>= 0.4)
  139 + rack-contrib (1.3.0)
  140 + git-version-bump (~> 0.15)
  141 + rack (~> 1.4)
  142 + rack-cors (0.4.0)
  143 + rack-mount (0.8.3)
  144 + rack (>= 1.0.0)
  145 + rack-ssl (1.3.4)
  146 + rack
  147 + rack-test (0.6.3)
  148 + rack (>= 1.0)
  149 + rails (3.2.22)
  150 + actionmailer (= 3.2.22)
  151 + actionpack (= 3.2.22)
  152 + activerecord (= 3.2.22)
  153 + activeresource (= 3.2.22)
  154 + activesupport (= 3.2.22)
  155 + bundler (~> 1.0)
  156 + railties (= 3.2.22)
  157 + rails_autolink (1.1.6)
  158 + rails (> 3.1)
  159 + railties (3.2.22)
  160 + actionpack (= 3.2.22)
  161 + activesupport (= 3.2.22)
  162 + rack-ssl (~> 1.3.2)
  163 + rake (>= 0.8.7)
  164 + rdoc (~> 3.4)
  165 + thor (>= 0.14.6, < 2.0)
  166 + rake (10.4.2)
  167 + rdoc (3.12.2)
  168 + json (~> 1.4)
  169 + rest-client (1.6.9)
  170 + mime-types (~> 1.16)
  171 + rmagick (2.13.4)
  172 + rspec (2.14.1)
  173 + rspec-core (~> 2.14.0)
  174 + rspec-expectations (~> 2.14.0)
  175 + rspec-mocks (~> 2.14.0)
  176 + rspec-core (2.14.8)
  177 + rspec-expectations (2.14.5)
  178 + diff-lcs (>= 1.1.3, < 2.0)
  179 + rspec-mocks (2.14.6)
  180 + rspec-rails (2.14.2)
  181 + actionpack (>= 3.0)
  182 + activemodel (>= 3.0)
  183 + activesupport (>= 3.0)
  184 + railties (>= 3.0)
  185 + rspec-core (~> 2.14.0)
  186 + rspec-expectations (~> 2.14.0)
  187 + rspec-mocks (~> 2.14.0)
  188 + ruby-feedparser (0.9.3)
  189 + magic
  190 + rubyzip (1.1.7)
  191 + sass (3.4.15)
  192 + sass-rails (3.2.6)
  193 + railties (~> 3.2.0)
  194 + sass (>= 3.1.10)
  195 + tilt (~> 1.3)
  196 + selenium-webdriver (2.39.0)
  197 + childprocess (>= 0.2.5)
  198 + multi_json (~> 1.0)
  199 + rubyzip (~> 1.0)
  200 + websocket (~> 1.0.4)
  201 + sprockets (2.2.3)
  202 + hike (~> 1.2)
  203 + multi_json (~> 1.0)
  204 + rack (~> 1.0)
  205 + tilt (~> 1.1, != 1.3.0)
  206 + term-ansicolor (1.3.2)
  207 + tins (~> 1.0)
  208 + thin (1.3.1)
  209 + daemons (>= 1.0.9)
  210 + eventmachine (>= 0.12.6)
  211 + rack (>= 1.0.0)
  212 + thor (0.19.1)
  213 + thread_safe (0.3.5)
  214 + tilt (1.4.1)
  215 + tins (1.5.4)
  216 + treetop (1.4.15)
  217 + polyglot
  218 + polyglot (>= 0.3.1)
  219 + tzinfo (0.3.44)
  220 + uglifier (2.7.1)
  221 + execjs (>= 0.3.0)
  222 + json (>= 1.8.0)
  223 + virtus (1.0.5)
  224 + axiom-types (~> 0.1)
  225 + coercible (~> 1.0)
  226 + descendants_tracker (~> 0.0, >= 0.0.3)
  227 + equalizer (~> 0.0, >= 0.0.9)
  228 + websocket (1.0.7)
  229 + whenever (0.9.4)
  230 + chronic (>= 0.6.3)
  231 + will_paginate (3.0.7)
  232 + xpath (2.0.0)
  233 + nokogiri (~> 1.3)
  234 +
  235 +PLATFORMS
  236 + ruby
  237 +
  238 +DEPENDENCIES
  239 + RedCloth (~> 4.2.9)
  240 + acts-as-taggable-on (~> 3.4.2)
  241 + api-pagination (~> 4.1.1)
  242 + capybara (~> 2.1.0)
  243 + cucumber (~> 1.0.6)
  244 + cucumber-rails (~> 1.0.6)
  245 + daemons (~> 1.1.5)
  246 + dalli (~> 2.7.0)
  247 + database_cleaner (~> 1.2.0)
  248 + debugger
  249 + eita-jrails (= 0.9.5)
  250 + exception_notification (~> 4.0.1)
  251 + fast_gettext (~> 0.6.8)
  252 + gettext (~> 2.2.1)
  253 + grape (~> 0.11.0)
  254 + grape-entity
  255 + grape-swagger
  256 + grape_logging
  257 + locale (~> 2.0.5)
  258 + minitest (~> 3.2.0)
  259 + mocha (~> 1.1.0)
  260 + nokogiri (~> 1.6.0)
  261 + pg (~> 0.13.2)
  262 + rack-contrib
  263 + rack-cors
  264 + rails (~> 3.2.22)
  265 + rails_autolink (~> 1.1.5)
  266 + rake
  267 + rest-client (~> 1.6.7)
  268 + rmagick (~> 2.13.1)
  269 + rspec (~> 2.14.0)
  270 + rspec-rails (~> 2.14.1)
  271 + ruby-feedparser (~> 0.7)
  272 + sass-rails
  273 + selenium-webdriver (~> 2.39.0)
  274 + thin (~> 1.3.1)
  275 + uglifier (>= 1.0.3)
  276 + whenever
  277 + will_paginate (~> 3.0.3)
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -94,6 +94,11 @@ class CmsController &lt; MyProfileController
94 94 record_coming
95 95 if request.post?
96 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 102 @article.last_changed_by = user
98 103 if @article.update_attributes(params[:article])
99 104 if !continue
... ... @@ -105,6 +110,11 @@ class CmsController &lt; MyProfileController
105 110 end
106 111 end
107 112 end
  113 +
  114 + unless @article.kind_of?(RssFeed)
  115 + @escaped_body = CGI::escapeHTML(@article.body || '')
  116 + @escaped_abstract = CGI::escapeHTML(@article.abstract || '')
  117 + end
108 118 end
109 119  
110 120 def new
... ...
app/controllers/my_profile/manage_products_controller.rb
... ... @@ -35,7 +35,7 @@ class ManageProductsController &lt; ApplicationController
35 35 end
36 36  
37 37 def categories_for_selection
38   - @category = Category.find(params[:category_id]) if params[:category_id]
  38 + @category = environment.categories.find_by_id params[:category_id]
39 39 @object_name = params[:object_name]
40 40 if @category
41 41 @categories = @category.children
... ... @@ -95,6 +95,20 @@ class ManageProductsController &lt; ApplicationController
95 95 end
96 96 end
97 97  
  98 + def show_category_tree
  99 + @category = environment.categories.find params[:category_id]
  100 + render :partial => 'selected_category_tree'
  101 + end
  102 +
  103 + def search_categories
  104 + @term = params[:term].downcase
  105 + conditions = ['LOWER(name) LIKE ? OR LOWER(name) LIKE ?', "#{@term}%", "% #{@term}%"]
  106 + @categories = ProductCategory.all :conditions => conditions, :limit => 10
  107 + render :json => (@categories.map do |category|
  108 + {:label => category.name, :value => category.id}
  109 + end)
  110 + end
  111 +
98 112 def add_input
99 113 @product = @profile.products.find(params[:id])
100 114 @input = @product.inputs.build
... ...
app/controllers/my_profile/maps_controller.rb
... ... @@ -16,6 +16,7 @@ class MapsController &lt; MyProfileController
16 16  
17 17 Profile.transaction do
18 18 if profile.update_attributes!(params[:profile_data])
  19 + BlockSweeper.expire_blocks profile.blocks.select{ |b| b.class == LocationBlock }
19 20 session[:notice] = _('Address was updated successfully!')
20 21 redirect_to :action => 'edit_location'
21 22 end
... ...
app/controllers/public/account_controller.rb
... ... @@ -98,11 +98,8 @@ class AccountController &lt; ApplicationController
98 98 @block_bot = !!session[:may_be_a_bot]
99 99 @invitation_code = params[:invitation_code]
100 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 102 @terms_of_use = environment.terms_of_use
105   - @user.person_data = params[:profile_data]
106 103 @user.return_to = session[:return_to]
107 104 @person = Person.new(params[:profile_data])
108 105 @person.environment = @user.environment
... ...
app/controllers/public/api_controller.rb 0 → 100644
... ... @@ -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/controllers/public/content_viewer_controller.rb
... ... @@ -15,6 +15,7 @@ class ContentViewerController &lt; ApplicationController
15 15 path = get_path(params[:page], params[:format])
16 16  
17 17 @version = params[:version].to_i
  18 + @npage = params[:npage] || '1'
18 19  
19 20 if path.blank?
20 21 @page = profile.home_page
... ... @@ -131,7 +132,7 @@ class ContentViewerController &lt; ApplicationController
131 132 end
132 133  
133 134 unless @page.display_to?(user)
134   - if !profile.visible? || profile.secret? || (user && user.follows?(profile))
  135 + if !profile.visible? || profile.secret? || (user && user.follows?(profile)) || user.blank?
135 136 render_access_denied
136 137 else #!profile.public?
137 138 private_profile_partial_parameters
... ...
app/helpers/application_helper.rb
... ... @@ -1195,7 +1195,7 @@ module ApplicationHelper
1195 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"))
1196 1196 end
1197 1197  
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'))) +
  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.url, :id => "homepage-link", :title => _('Go to your homepage'))) +
1199 1199 render_environment_features(:usermenu) +
1200 1200 admin_link +
1201 1201 manage_enterprises +
... ... @@ -1226,35 +1226,6 @@ module ApplicationHelper
1226 1226 list.sort.inject(Hash.new(0)){|h,i| h[i] += 1; h }.collect{ |x, n| [n, connector, x].join(" ") }.sort
1227 1227 end
1228 1228  
1229   - #FIXME Use time_ago_in_words instead of this method if you're using Rails 2.2+
1230   - def time_ago_as_sentence(from_time, include_seconds = false)
1231   - to_time = Time.now
1232   - from_time = Time.parse(from_time.to_s)
1233   - from_time = from_time.to_time if from_time.respond_to?(:to_time)
1234   - to_time = to_time.to_time if to_time.respond_to?(:to_time)
1235   - distance_in_minutes = (((to_time - from_time).abs)/60).round
1236   - distance_in_seconds = ((to_time - from_time).abs).round
1237   - case distance_in_minutes
1238   - when 0..1
1239   - return (distance_in_minutes == 0) ? _('less than a minute') : _('1 minute') unless include_seconds
1240   - case distance_in_seconds
1241   - when 0..4 then _('less than 5 seconds')
1242   - when 5..9 then _('less than 10 seconds')
1243   - when 10..19 then _('less than 20 seconds')
1244   - when 20..39 then _('half a minute')
1245   - when 40..59 then _('less than a minute')
1246   - else _('1 minute')
1247   - end
1248   -
1249   - when 2..44 then _('%{distance} minutes ago') % { :distance => distance_in_minutes }
1250   - when 45..89 then _('about 1 hour ago')
1251   - when 90..1439 then _('about %{distance} hours ago') % { :distance => (distance_in_minutes.to_f / 60.0).round }
1252   - when 1440..2879 then _('1 day ago')
1253   - when 2880..10079 then _('%{distance} days ago') % { :distance => (distance_in_minutes / 1440).round }
1254   - else show_time(from_time)
1255   - end
1256   - end
1257   -
1258 1229 def comment_balloon(options = {}, &block)
1259 1230 wrapper = content_tag(:div, capture(&block), :class => 'comment-balloon-content')
1260 1231 (1..8).to_a.reverse.each { |i| wrapper = content_tag(:div, wrapper, :class => "comment-wrapper-#{i}") }
... ... @@ -1273,7 +1244,7 @@ module ApplicationHelper
1273 1244  
1274 1245 def task_information(task)
1275 1246 values = {}
1276   - values.merge!({:requestor => link_to(task.requestor.name, task.requestor.public_profile_url)}) if task.requestor
  1247 + values.merge!({:requestor => link_to(task.requestor.name, task.requestor.url)}) if task.requestor
1277 1248 values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject
1278 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
1279 1250 values.merge!(task.information[:variables]) if task.information[:variables]
... ...
app/helpers/blog_helper.rb
... ... @@ -22,7 +22,9 @@ module BlogHelper
22 22 :param_name => 'npage',
23 23 :previous_label => _('&laquo; Newer posts'),
24 24 :next_label => _('Older posts &raquo;'),
25   - :params => {:action=>"view_page", :page=>articles.first.parent.path.split('/'), :controller=>"content_viewer"}
  25 + :params => {:action=>"view_page",
  26 + :page=>articles.first.parent.path.split('/'),
  27 + :controller=>"content_viewer"}
26 28 }) if articles.present? && conf[:paginate]
27 29 content = []
28 30 artic_len = articles.length
... ... @@ -44,7 +46,7 @@ module BlogHelper
44 46 end
45 47  
46 48 def display_post(article, format = 'full')
47   - no_comments = (format == 'full') ? false : true
  49 + no_comments = (format == 'full' || format == 'compact' ) ? false : true
48 50 title = article_title(article, :no_comments => no_comments)
49 51 method = "display_#{format.split('+')[0]}_format"
50 52 html = send(method, FilePresenter.for(article)).html_safe
... ... @@ -55,8 +57,12 @@ module BlogHelper
55 57 else
56 58 '<div class="post-pic" style="background-image:url('+img+')"></div>'
57 59 end
58   - end.to_s +
59   - title + html
  60 + end.to_s + title + html
  61 + end
  62 +
  63 + def display_compact_format(article)
  64 + render :file => 'content_viewer/_display_compact_format',
  65 + :locals => { :article => article, :format => "compact" }
60 66 end
61 67  
62 68 def display_full_format(article)
... ...
app/helpers/comment_helper.rb
1 1 module CommentHelper
  2 + include DatesHelper
2 3  
3 4 def article_title(article, args = {})
4 5 title = article.title
... ... @@ -15,7 +16,7 @@ module CommentHelper
15 16 content_tag('span', show_date(article.published_at), :class => 'date') +
16 17 content_tag('span', [_(", by %s") % link_to(article.author_name, article.author_url)], :class => 'author') +
17 18 content_tag('span', comments, :class => 'comments'),
18   - :class => 'created-at'
  19 + :class => 'publishing-info'
19 20 )
20 21 end
21 22 title
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -2,6 +2,7 @@ module ContentViewerHelper
2 2  
3 3 include BlogHelper
4 4 include ForumHelper
  5 + include DatesHelper
5 6  
6 7 def display_number_of_comments(n)
7 8 base_str = "<span class='comment-count hide'>#{n}</span>"
... ... @@ -24,16 +25,35 @@ module ContentViewerHelper
24 25 unless args[:no_comments] || !article.accept_comments
25 26 comments = (" - %s") % link_to_comments(article)
26 27 end
  28 + date_format = show_with_right_format_date article
27 29 title << content_tag('span',
28   - content_tag('span', show_date(article.published_at), :class => 'date') +
  30 + date_format +
29 31 content_tag('span', _(", by %s") % (article.author ? link_to(article.author_name, article.author_url) : article.author_name), :class => 'author') +
30 32 content_tag('span', comments, :class => 'comments'),
31   - :class => 'created-at'
  33 + :class => 'publishing-info'
32 34 )
33 35 end
34 36 title
35 37 end
36 38  
  39 + def show_with_right_format_date article
  40 + date_format = article.environment.date_format
  41 + use_numbers = false
  42 + year = true
  43 + left_time = false
  44 + if date_format == 'numbers_with_year'
  45 + use_numbers = true
  46 + elsif date_format == 'numbers'
  47 + use_numbers = true
  48 + year = false
  49 + elsif date_format == 'month_name'
  50 + year = false
  51 + elsif date_format == 'past_time'
  52 + left_time = true
  53 + end
  54 + content_tag('span', show_date(article.published_at, use_numbers , year, left_time), :class => 'date')
  55 + end
  56 +
37 57 def link_to_comments(article, args = {})
38 58 return '' unless article.accept_comments?
39 59 reference_to_article number_of_comments(article), article, 'comments_list'
... ...
app/helpers/dates_helper.rb
... ... @@ -2,6 +2,7 @@ require &#39;noosfero/i18n&#39;
2 2  
3 3 module DatesHelper
4 4  
  5 + include ActionView::Helpers::DateHelper
5 6 def months
6 7 I18n.t('date.month_names')
7 8 end
... ... @@ -15,10 +16,12 @@ module DatesHelper
15 16 end
16 17  
17 18 # formats a date for displaying.
18   - def show_date(date, use_numbers = false, year=true)
  19 + def show_date(date, use_numbers = false, year = true, left_time = false)
19 20 if date && use_numbers
20 21 date_format = year ? _('%{month}/%{day}/%{year}') : _('%{month}/%{day}')
21 22 date_format % { :day => date.day, :month => date.month, :year => date.year }
  23 + elsif date && left_time
  24 + date_format = time_ago_in_words(date)
22 25 elsif date
23 26 date_format = year ? _('%{month_name} %{day}, %{year}') : _('%{month_name} %{day}')
24 27 date_format % { :day => date.day, :month_name => month_name(date.month), :year => date.year }
... ...
app/helpers/events_helper.rb
1 1 module EventsHelper
2 2  
  3 + include DatesHelper
3 4 def list_events(date, events)
4 5 title = _('Events for %s') % show_date_month(date)
5 6 content_tag('h2', title) +
... ...
app/helpers/folder_helper.rb
1   -require 'short_filename'
2   -
3 1 module FolderHelper
4 2  
5   - include ShortFilename
6 3 include ArticleHelper
7 4  
8 5 def list_contents(configure={})
... ... @@ -10,8 +7,8 @@ module FolderHelper
10 7 configure[:list_type] ||= :folder
11 8 if !configure[:contents].blank?
12 9 configure[:contents] = configure[:contents].paginate(
13   - :order => "updated_at DESC",
14   - :per_page => 10,
  10 + :order => "name ASC",
  11 + :per_page => 30,
15 12 :page => params[:npage]
16 13 )
17 14  
... ... @@ -25,49 +22,32 @@ module FolderHelper
25 22 articles.select {|article| article.display_to?(user)}
26 23 end
27 24  
28   - def display_content_in_listing(configure={})
29   - recursive = configure[:recursive] || false
30   - list_type = configure[:list_type] || :folder
31   - level = configure[:level] || 0
32   - content = FilePresenter.for configure[:content]
  25 + def display_content_icon(content_item)
  26 + content = FilePresenter.for content_item
33 27 content_link = if content.image?
34   - link_to('&nbsp;' * (level * 4) +
35   - image_tag(icon_for_article(content)) + short_filename(content.name),
  28 + link_to(
  29 + image_tag(icon_for_article(content, :bigicon)),
36 30 content.url.merge(:view => true)
37 31 )
38 32 else
39   - link_to('&nbsp;' * (level * 4) +
40   - short_filename(content.name),
41   - content.url.merge(:view => true), :class => icon_for_article(content)
  33 + link_to('',
  34 + content.url.merge(:view => true),
  35 + :class => icon_for_article(content, :bigicon)
42 36 )
43 37 end
44   - result = content_tag(
45   - 'tr',
46   - content_tag('td', content_link ) +
47   - content_tag('td', show_date(content.updated_at), :class => 'last-update'),
48   - :class => "#{list_type}-item"
49   - )
50   - if recursive
51   - result + content.children.map {|item|
52   - display_content_in_listing :content=>item, :recursive=>recursive,
53   - :list_type=>list_type, :level=>level+1
54   - }.join("\n")
55   - else
56   - result
57   - end
58 38 end
59 39  
60   - def icon_for_article(article)
  40 + def icon_for_article(article, size = 'icon')
61 41 article = FilePresenter.for article
62   - icon = article.respond_to?(:icon_name) ?
63   - article.icon_name :
64   - article.class.icon_name(article)
65   - if (icon =~ /\//)
66   - icon
  42 + if article.respond_to?(:sized_icon)
  43 + article.sized_icon(size)
67 44 else
68   - klasses = 'icon ' + [icon].flatten.map{|name| 'icon-'+name}.join(' ')
  45 + icon = article.respond_to?(:icon_name) ?
  46 + article.icon_name :
  47 + article.class.icon_name(article)
  48 + klasses = "#{size} " + [icon].flatten.map{|name| "#{size}-"+name}.join(' ')
69 49 if article.kind_of?(UploadedFile) || article.kind_of?(FilePresenter)
70   - klasses += ' icon-upload-file'
  50 + klasses += " #{size}-upload-file"
71 51 end
72 52 klasses
73 53 end
... ...
app/helpers/forum_helper.rb
1 1 module ForumHelper
  2 + include ActionView::Helpers::DateHelper
2 3  
3 4 def cms_label_for_new_children
4 5 _('New discussion topic')
... ... @@ -42,9 +43,9 @@ module ForumHelper
42 43 def last_topic_update(article)
43 44 info = article.info_from_last_update
44 45 if info[:author_url]
45   - time_ago_as_sentence(info[:date]) + ' ' + _('by') + ' ' + link_to(info[:author_name], info[:author_url])
  46 + time_ago_in_words(info[:date]) + ' ' + _('by') + ' ' + link_to(info[:author_name], info[:author_url])
46 47 else
47   - time_ago_as_sentence(info[:date]) + ' ' + _('by') + ' ' + info[:author_name]
  48 + time_ago_in_words(info[:date]) + ' ' + _('by') + ' ' + info[:author_name]
48 49 end
49 50 end
50 51  
... ...
app/helpers/manage_products_helper.rb
... ... @@ -75,9 +75,12 @@ module ManageProductsHelper
75 75 end
76 76  
77 77 def categories_container(categories_selection_html, hierarchy_html = '')
78   - hidden_field_tag('selected_category_id') +
79   - content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') +
80   - content_tag('div', categories_selection_html, :id => 'categories_container_wrapper')
  78 + content_tag 'div',
  79 + render('categories_autocomplete') +
  80 + hidden_field_tag('selected_category_id') +
  81 + content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') +
  82 + content_tag('div', categories_selection_html, :id => 'categories_container_wrapper'),
  83 + :id => 'categories-container'
81 84 end
82 85  
83 86 def select_for_categories(categories, level = 0)
... ...
app/models/article.rb
... ... @@ -28,7 +28,7 @@ class Article &lt; ActiveRecord::Base
28 28 def initialize(*params)
29 29 super
30 30  
31   - if !params.blank? && params.first.has_key?(:profile)
  31 + if !params.blank? && params.first.has_key?(:profile) && !params.first[:profile].blank?
32 32 profile = params.first[:profile]
33 33 self.published = false unless profile.public?
34 34 end
... ... @@ -133,15 +133,7 @@ class Article &lt; ActiveRecord::Base
133 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 138 scope :by_range, lambda { |range| {
147 139 :conditions => [
... ... @@ -748,8 +740,9 @@ class Article &lt; ActiveRecord::Base
748 740 paragraphs.empty? ? '' : paragraphs.first.to_html
749 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 746 end
754 747  
755 748 def short_lead
... ...
app/models/blog.rb
... ... @@ -76,9 +76,12 @@ class Blog &lt; Folder
76 76 end
77 77  
78 78 settings_items :visualization_format, :type => :string, :default => 'full'
79   - validates_inclusion_of :visualization_format, :in => [ 'full', 'short', 'short+pic' ], :if => :visualization_format
  79 + validates_inclusion_of :visualization_format,
  80 + :in => [ 'full', 'short', 'short+pic', 'compact'],
  81 + :if => :visualization_format
80 82  
81   - settings_items :display_posts_in_current_language, :type => :boolean, :default => false
  83 + settings_items :display_posts_in_current_language,
  84 + :type => :boolean, :default => false
82 85  
83 86 alias :display_posts_in_current_language? :display_posts_in_current_language
84 87  
... ...
app/models/comment.rb
... ... @@ -20,16 +20,7 @@ class Comment &lt; ActiveRecord::Base
20 20  
21 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 25 # unauthenticated authors:
35 26 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? })
... ...
app/models/environment.rb
... ... @@ -3,7 +3,17 @@
3 3 # domains.
4 4 class Environment < ActiveRecord::Base
5 5  
6   - attr_accessible :name, :is_default, :signup_welcome_text_subject, :signup_welcome_text_body, :terms_of_use, :message_for_disabled_enterprise, :news_amount_by_folder, :default_language, :languages, :description, :organization_approval_method, :enabled_plugins, :enabled_features, :redirection_after_login, :redirection_after_signup, :contact_email, :theme, :reports_lower_bound, :noreply_email, :signup_welcome_screen_body, :members_whitelist_enabled, :members_whitelist, :highlighted_news_amount, :portal_news_amount
  6 + attr_accessible :name, :is_default, :signup_welcome_text_subject,
  7 + :signup_welcome_text_body, :terms_of_use,
  8 + :message_for_disabled_enterprise, :news_amount_by_folder,
  9 + :default_language, :languages, :description,
  10 + :organization_approval_method, :enabled_plugins,
  11 + :enabled_features, :redirection_after_login,
  12 + :redirection_after_signup, :contact_email, :theme,
  13 + :reports_lower_bound, :noreply_email,
  14 + :signup_welcome_screen_body, :members_whitelist_enabled,
  15 + :members_whitelist, :highlighted_news_amount,
  16 + :portal_news_amount, :date_format
7 17  
8 18 has_many :users
9 19  
... ... @@ -14,6 +24,12 @@ class Environment &lt; ActiveRecord::Base
14 24  
15 25 IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/
16 26  
  27 + validates_inclusion_of :date_format,
  28 + :in => [ 'numbers_with_year', 'numbers',
  29 + 'month_name_with_year', 'month_name',
  30 + 'past_time'],
  31 + :if => :date_format
  32 +
17 33 def self.verify_filename(filename)
18 34 filename += '.txt' if File.extname(filename) =~ IDENTIFY_SCRIPTS
19 35 filename
... ...
app/models/event.rb
... ... @@ -98,47 +98,19 @@ class Event &lt; Article
98 98 start_date..(end_date||start_date)
99 99 end
100 100  
101   - # FIXME this shouldn't be needed
102   - include ActionView::Helpers::TagHelper
103   - include ActionView::Helpers::UrlHelper
104   - include DatesHelper
  101 + def first_paragraph
  102 + paragraphs = Nokogiri::HTML.fragment(self.body).css('p')
  103 + paragraphs.empty? ? '' : paragraphs.first.to_html
  104 + end
105 105  
106 106 def to_html(options = {})
  107 + event = self
  108 + format = options[:format]
107 109  
108   - result = ''
109   - html = ::Builder::XmlMarkup.new(:target => result)
110   -
111   - html.div(:class => 'event-info' ) {
112   - html.ul(:class => 'event-data' ) {
113   - html.li(:class => 'event-dates' ) {
114   - html.span _('When:')
115   - html.text! show_period(start_date, end_date)
116   - } if start_date.present? || end_date.present?
117   - html.li {
118   - html.span _('URL:')
119   - html.a(self.link || "", 'href' => self.link || "")
120   - } if self.link.present?
121   - html.li {
122   - html.span _('Address:')
123   - html.text! self.address || ""
124   - } if self.address.present?
125   - }
126   -
127   - # TODO: some good soul, please clean this ugly hack:
128   - if self.body
129   - html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description')
130   - end
131   - }
132   -
133   - if self.body
134   - if options[:format] == 'short'
135   - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', display_short_format(self))
136   - else
137   - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.body)
138   - end
  110 + proc do
  111 + render :file => 'content_viewer/event_page', :locals => { :event => event,
  112 + :format => format }
139 113 end
140   -
141   - result
142 114 end
143 115  
144 116 def duration
... ...
app/models/image.rb
... ... @@ -23,7 +23,7 @@ class Image &lt; ActiveRecord::Base
23 23  
24 24 postgresql_attachment_fu
25 25  
26   - attr_accessible :uploaded_data
  26 + attr_accessible :uploaded_data, :label
27 27  
28 28 def current_data
29 29 File.file?(full_filename) ? File.read(full_filename) : nil
... ...
app/models/organization.rb
... ... @@ -8,13 +8,30 @@ class Organization &lt; Profile
8 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 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 33 ).uniq
17   - }
  34 + end
18 35  
19 36 settings_items :closed, :type => :boolean, :default => false
20 37 def closed?
... ... @@ -154,6 +171,12 @@ class Organization &lt; Profile
154 171 ]
155 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 180 def notification_emails
158 181 emails = [contact_email].select(&:present?) + admins.map(&:email)
159 182 if emails.empty?
... ...
app/models/person.rb
1 1 # A person is the profile of an user holding all relationships with the rest of the system
2 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 7 :custom_area_of_study, :professional_activity, :organization_website, :following_articiles
8 8  
9 9 SEARCH_FILTERS = {
... ... @@ -45,13 +45,17 @@ roles] }
45 45 }
46 46  
47 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 59 def has_permission_with_admin?(permission, resource)
56 60 return true if resource.blank? || resource.admins.include?(self)
57 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 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 73 after_update :save_image
55 74  
56 75 def lat
... ...
app/models/profile.rb
... ... @@ -128,15 +128,49 @@ class Profile &lt; ActiveRecord::Base
128 128 }
129 129 scope :no_templates, {:conditions => {:is_template => false}}
130 130  
131   - #FIXME make this test
132   - scope :newer_than, lambda { |reference_id|
133   - {:conditions => ["profiles.id > #{reference_id}"]}
134   - }
  131 + # Returns a scoped object to select profiles in a given location or in a radius
  132 + # distance from the given location center.
  133 + # The parameter can be the `request.params` with the keys:
  134 + # * `country`: Country code string.
  135 + # * `state`: Second-level administrative country subdivisions.
  136 + # * `city`: City full name for center definition, or as set by users.
  137 + # * `lat`: The latitude to define the center of georef search.
  138 + # * `lng`: The longitude to define the center of georef search.
  139 + # * `distance`: Define the search radius in kilometers.
  140 + # NOTE: This method may return an exception object, to inform filter error.
  141 + # When chaining scopes, is hardly recommended you to add this as the last one,
  142 + # if you can't be sure about the provided parameters.
  143 + def self.by_location(params)
  144 + params = params.with_indifferent_access
  145 + if params[:distance].blank?
  146 + where_code = []
  147 + [ :city, :state, :country ].each do |place|
  148 + unless params[place].blank?
  149 + # ... So we must to find on this named location
  150 + # TODO: convert location attrs to a table collumn
  151 + where_code << "(profiles.data like '%#{place}: #{params[place]}%')"
  152 + end
  153 + end
  154 + self.where where_code.join(' AND ')
  155 + else # Filter in a georef circle
  156 + unless params[:lat].blank? && params[:lng].blank?
  157 + lat, lng = [ params[:lat].to_f, params[:lng].to_f ]
  158 + end
  159 + if !lat
  160 + location = [ params[:city], params[:state], params[:country] ].compact.join(', ')
  161 + if location.blank?
  162 + return Exception.new (
  163 + _('You must to provide `lat` and `lng`, or `city` and `country` to define the center of the search circle, defined by `distance`.')
  164 + )
  165 + end
  166 + lat, lng = Noosfero::GeoRef.location_to_georef location
  167 + end
  168 + dist = params[:distance].to_f
  169 + self.where "#{Noosfero::GeoRef.sql_dist lat, lng} <= #{dist}"
  170 + end
  171 + end
135 172  
136   - #FIXME make this test
137   - scope :older_than, lambda { |reference_id|
138   - {:conditions => ["profiles.id < #{reference_id}"]}
139   - }
  173 + include TimeScopes
140 174  
141 175 def members
142 176 scopes = plugins.dispatch_scopes(:organization_members, self)
... ... @@ -1006,19 +1040,11 @@ private :generate_url, :url_options
1006 1040 self.save
1007 1041 end
1008 1042  
1009   - def disabled?
1010   - !visible
1011   - end
1012   -
1013 1043 def enable
1014 1044 self.visible = true
1015 1045 self.save
1016 1046 end
1017 1047  
1018   - def enabled?
1019   - visible
1020   - end
1021   -
1022 1048 def control_panel_settings_button
1023 1049 {:title => _('Edit Profile'), :icon => 'edit-profile'}
1024 1050 end
... ...
app/models/qualifier.rb
... ... @@ -11,6 +11,12 @@ class Qualifier &lt; ActiveRecord::Base
11 11 has_many :qualifier_certifiers, :dependent => :destroy
12 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 20 has_many :product_qualifiers, :dependent => :destroy
15 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 41 return type if type < Article
42 42 end
43 43 TinyMceArticle
  44 + (article[:type] || 'TinyMceArticle').constantize
44 45 end
45 46  
46 47 def perform
... ...
app/models/uploaded_file.rb
1   -require 'short_filename'
2   -
3 1 # Article type that handles uploaded files.
4 2 #
5 3 # Limitation: only file metadata are versioned. Only the latest version
... ... @@ -14,8 +12,6 @@ class UploadedFile &lt; Article
14 12  
15 13 track_actions :upload_image, :after_create, :keep_params => ["view_url", "thumbnail_path", "parent.url", "parent.name"], :if => Proc.new { |a| a.published? && a.image? && !a.parent.nil? && a.parent.gallery? }, :custom_target => :parent
16 14  
17   - include ShortFilename
18   -
19 15 def title
20 16 if self.name.present? then self.name else self.filename end
21 17 end
... ... @@ -65,7 +61,7 @@ class UploadedFile &lt; Article
65 61 # :min_size => 2.megabytes
66 62 # :max_size => 5.megabytes
67 63 has_attachment :storage => :file_system,
68   - :thumbnails => { :icon => [24,24], :thumb => '130x130>', :slideshow => '320x240>', :display => '640X480>' },
  64 + :thumbnails => { :icon => [24,24], :bigicon => [50,50], :thumb => '130x130>', :slideshow => '320x240>', :display => '640X480>' },
69 65 :thumbnail_class => Thumbnail,
70 66 :max_size => self.max_size
71 67  
... ...
app/models/user.rb
... ... @@ -34,6 +34,14 @@ class User &lt; ActiveRecord::Base
34 34 alias_method_chain :human_attribute_name, :customization
35 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 45 before_create do |user|
38 46 if user.environment.nil?
39 47 user.environment = Environment.default
... ... @@ -120,17 +128,15 @@ class User &lt; ActiveRecord::Base
120 128 self.update_attribute :last_login_at, Time.now
121 129 end
122 130  
123   - #FIXME make this test
124 131 def generate_private_token!
125 132 self.private_token = SecureRandom.hex
126 133 self.private_token_generated_at = DateTime.now
127 134 save(:validate => false)
128 135 end
129 136  
130   - #FIXME make this test
131   - def private_token_expired?
132   - return true if self.private_token_generated_at.nil?
133   - self.generate_private_token! if self.private_token.nil? || (self.private_token_generated_at + 2.weeks < DateTime.now)
  137 + TOKEN_VALIDITY = 2.weeks
  138 + def private_token_expired?
  139 + self.private_token.nil? || (self.private_token_generated_at + TOKEN_VALIDITY < DateTime.now)
134 140 end
135 141  
136 142 # Activates the user in the database.
... ... @@ -334,6 +340,8 @@ class User &lt; ActiveRecord::Base
334 340  
335 341 {
336 342 'login' => self.login,
  343 + 'name' => self.person.name,
  344 + 'email' => self.email,
337 345 'avatar' => self.person.profile_custom_icon(gravatar_default),
338 346 'is_admin' => self.person.is_admin?,
339 347 'since_month' => self.person.created_at.month,
... ...
app/presenters/image.rb
... ... @@ -4,6 +4,10 @@ class FilePresenter::Image &lt; FilePresenter
4 4 f.image? ? 10 : nil
5 5 end
6 6  
  7 + def sized_icon(size)
  8 + public_filename size
  9 + end
  10 +
7 11 def icon_name
8 12 public_filename :icon
9 13 end
... ...
app/views/admin_panel/_site_info.html.erb
... ... @@ -3,6 +3,21 @@
3 3 <%= labelled_form_field(_('No reply email'), text_field(:environment, :noreply_email)) %>
4 4 <% themes_options = Theme.system_themes.map {|theme| [theme.name, theme.id] }.sort %>
5 5 <%= labelled_form_field(_('Theme'), select(:environment, :theme, options_for_select(themes_options, environment.theme))) %>
  6 +
  7 +<%= labelled_form_field(
  8 + _("Article's date format"),
  9 + select(:environment, :date_format,
  10 + options_for_select([
  11 + [ _('mm/dd/yyyy'), 'numbers_with_year'],
  12 + [ _('mm/dd'), 'numbers'],
  13 + [ _('Month dd, yyyy'), 'month_name_with_year'],
  14 + [ _('Month dd'), 'month_name'],
  15 + [ _('X minutes/hours/days/months/years ago'), 'past_time']
  16 + ], environment.date_format
  17 + )
  18 + )
  19 +) %>
  20 +
6 21 <%= required f.text_field(:reports_lower_bound, :size => 3) %>
7 22 <%= labelled_form_field(_('Default language'), select(:environment, :default_language, environment.locales.invert, { :selected => environment.default_locale, :include_blank => true })) %>
8 23 <%= label_tag :languages, _('Available languages') %>
... ...
app/views/api/index.html.erb 0 → 100644
... ... @@ -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,32 @@
  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| a[:path]=='/api/v1/login' ? -1:1}.to_json %>;
  16 +</script>
  17 +
  18 +<form id="api-form">
  19 + <label id="endpoint">Endpoint:
  20 + <select name="endpoint" onchange="playground.selEndpoint()"></select>
  21 + </label>
  22 + <label id="api-token">Token:
  23 + <%= tag :input, size: 32, placeholder: _('Use the login endpoint') %>
  24 + </label>
  25 +</form>
  26 +<div id="playground-ctrl">
  27 + <button onclick="playground.addFormParam()"><%=_('Add parameter')%></button>
  28 + <button onclick="playground.run()" style="font-weight:bold">&nbsp; <%=_('Run')%> &nbsp;</button>
  29 +</div>
  30 +<pre id="api-response" class="empty"></pre>
  31 +
  32 +<script src="/javascripts/api-playground.js"></script>
... ...
app/views/cms/_blog.html.erb
... ... @@ -67,7 +67,8 @@
67 67 <%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [
68 68 [ _('Full post'), 'full'],
69 69 [ _('First paragraph'), 'short'],
70   - [ _('First paragraph, with post picture'), 'short+pic']
  70 + [ _('First paragraph, with post picture'), 'short+pic'],
  71 + [ _("Title, Image, Lead"), 'compact']
71 72 ])) %>
72 73  
73 74 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Blog.posts_per_page_options)) %>
... ...
app/views/cms/_view_items.html.erb 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +<% @articles.each do |article| article = FilePresenter.for article %>
  2 + <tr title="<%= article.title%>" >
  3 + <td class="article-name">
  4 + <%= link_to_article(article) %>
  5 + </td>
  6 + <% short_description = article.respond_to?(:short_description) ?
  7 + article.short_description :
  8 + article.class.short_description %>
  9 + <td class="article-mime" title=<%= short_description.to_json %>>
  10 + <%= short_description %>
  11 + </td>
  12 + <td class="last-update">
  13 + <%= time_ago_in_words article.updated_at %>
  14 + </td>
  15 + <td class="article-controls">
  16 + <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit, article) %>
  17 + <%= button_without_text :eyes, _('Public view'), article.view_url %>
  18 + <%= display_spread_button(article) unless remove_content_button(:spread, article) %>
  19 + <% if user.can_change_homepage? && !remove_content_button(:home, article) %>
  20 + <% if profile.home_page != article %>
  21 + <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %>
  22 + <% else %>
  23 + <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %>
  24 + <% end %>
  25 + <% end %>
  26 + <%= display_delete_button(article) if !remove_content_button(:delete, article) %>
  27 + </td>
  28 + </tr>
  29 +<% end %>
... ...
app/views/cms/view.html.erb
... ... @@ -37,6 +37,7 @@
37 37 <tr>
38 38 <th><%= _('Name') %></th>
39 39 <th><%= _('Type') %></th>
  40 + <th><%= _('Last update') %></th>
40 41 <th><%= _('Actions') %></th>
41 42 </tr>
42 43  
... ... @@ -54,32 +55,7 @@
54 55 </tr>
55 56 <% end %>
56 57  
57   - <% @articles.each do |article| article = FilePresenter.for article %>
58   - <tr title="<%= article.title%>" >
59   - <td class="article-name">
60   - <%= link_to_article(article) %>
61   - </td>
62   - <% short_description = article.respond_to?(:short_description) ?
63   - article.short_description :
64   - article.class.short_description %>
65   - <td class="article-mime" title=<%= short_description.to_json %>>
66   - <%= short_description %>
67   - </td>
68   - <td class="article-controls">
69   - <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit, article) %>
70   - <%= button_without_text :eyes, _('Public view'), article.view_url %>
71   - <%= display_spread_button(article) unless remove_content_button(:spread, article) %>
72   - <% if user.can_change_homepage? && !remove_content_button(:home, article) %>
73   - <% if profile.home_page != article %>
74   - <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %>
75   - <% else %>
76   - <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %>
77   - <% end %>
78   - <% end %>
79   - <%= display_delete_button(article) if !remove_content_button(:delete, article) %>
80   - </td>
81   - </tr>
82   - <% end %>
  58 + <%= render 'view_items' %>
83 59  
84 60 </table>
85 61  
... ...
app/views/content_viewer/_article_title.html.erb 0 → 100644
... ... @@ -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 64 <% end %>
65 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 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 68 <%= article_translations(@page) %>
69 69 </div>
70 70 </div>
... ...
app/views/content_viewer/_display_compact_format.html.erb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +<% if article.image %>
  2 + <% className = "article-compact-abstract-with-image" %>
  3 + <% if article.image.thumbnails_processed? %>
  4 + <% image_file = article.image.public_filename(:big) %>
  5 + <% else %>
  6 + <% image_file = "/images/icons-app/image-loading-thumb.png" %>
  7 + <% end %>
  8 +<% else %>
  9 + <% className = "article-compact-abstract" %>
  10 +<% end %>
  11 +
  12 +<div>
  13 + <% if article.image %>
  14 + <div class = "article-compact-image">
  15 + <%= image_tag(image_file) %>
  16 + </div>
  17 + <% end %>
  18 + <div class = <%= className %> >
  19 + <%= article.lead(400) %>
  20 + </div>
  21 +</div>
... ...
app/views/content_viewer/_publishing_info.html.erb 0 → 100644
... ... @@ -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/blog_page.html.erb
... ... @@ -8,7 +8,7 @@
8 8 </div>
9 9 </div>
10 10 <hr class="pre-posts"/>
11   -<div class="blog-posts">
  11 +<div class="blog-posts page-<%= @npage %>">
12 12 <% paginate = true %>
13 13 <%=
14 14 posts = @posts
... ...
app/views/content_viewer/event_page.html.erb 0 → 100644
... ... @@ -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/folder.html.erb
1 1 <% unless folder.body.blank? %>
2   - <div>
  2 + <div class="folder-description">
3 3 <%= folder.body %>
4 4 </div>
5   - <hr/>
6 5 <% end %>
7 6  
8 7 <% if folder.children.empty? %>
... ...
app/views/content_viewer/view_page.html.erb
... ... @@ -24,22 +24,6 @@
24 24 <%= render :partial => 'article_toolbar' %>
25 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 27 <% if NOOSFERO_CONF['addthis_enabled'] %>
44 28 <%= render :partial => 'addthis' %>
45 29 <% end %>
... ... @@ -47,6 +31,12 @@
47 31 <% cache(@page.cache_key(params, user, language)) do %>
48 32 <div class="<%="article-body article-body-" + @page.css_class_name %>">
49 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 40 <%= article_to_html(@page, options) %>
51 41 <br style="clear:both" />
52 42 </div> <!-- end class="article-body" -->
... ...
app/views/layouts/application-ng.html.erb
... ... @@ -27,6 +27,7 @@
27 27  
28 28 <script type="text/javascript">
29 29 DEFAULT_LOADING_MESSAGE = <%="'#{ _('loading...') }'" %>;
  30 + noosfero.profile = <%= (@profile.identifier if @profile).to_json %>
30 31 </script>
31 32  
32 33 </head>
... ...
app/views/manage_products/_categories_autocomplete.html.erb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<%= text_field_tag 'product_category_id', '', :placeholder => _('type a category for the product') %>
  2 +
  3 +<%= javascript_include_tag '/javascripts/product_categories.js' %>
  4 +<%= javascript_tag do %>
  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 %>
  7 + product_categories.autocomplete.load('#product_category_id')
  8 +<% end %>
... ...
app/views/manage_products/_selected_category_tree.html.erb 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +<%= categories_container selects_for_all_ancestors(@category), hierarchy_category_navigation(@category, :make_links => true) %>
  2 +
... ...
app/views/manage_products/edit_category.html.erb
... ... @@ -16,7 +16,7 @@
16 16  
17 17 <h3><%= _('Edit category of this product:') %></h3>
18 18  
19   - <%= categories_container(selects_for_all_ancestors(@category), hierarchy_category_navigation(@category, :make_links => true)) %>
  19 + <%= render 'manage_products/selected_category_tree' %>
20 20  
21 21 <div id='categories_selection_actionbar'>
22 22 <%= button(:back, _('Back to product'), :action => 'show', :id => @product) %>
... ...
app/views/person_notifier/mailer/_comment.html.erb
... ... @@ -19,7 +19,7 @@
19 19 <span style="font-size: 12px;"><%= comment.title %></span><br/>
20 20 <% end %>
21 21 <span style="font-size: 10px;"><%= txt2html comment.body %></span><br/>
22   - <span style="font-size: 8px; color: #929292"><%= time_ago_as_sentence(comment.created_at) %></span>
  22 + <span style="font-size: 8px; color: #929292"><%= time_ago_in_words(comment.created_at) %></span>
23 23 <br style="clear: both;" />
24 24  
25 25 <% unless comment.replies.blank? %>
... ...
app/views/person_notifier/mailer/_create_article.html.erb
... ... @@ -6,7 +6,7 @@
6 6 <p>
7 7 <span style="font-size: 14px;"><%= link_to activity.user.short_name(20), activity.user.url %></span>
8 8 <span style="font-size: 14px;"><%= _("has published on community %s") % link_to(activity.target.profile.short_name(20), activity.target.profile.url, :style => "color: #333; font-weight: bold; text-decoration: none;") if activity.target.profile.is_a?(Community) %></span>
9   - <span style="font-size: 10px; color: #929292; float:right;"><%= time_ago_as_sentence(activity.created_at) %></span>
  9 + <span style="font-size: 10px; color: #929292; float:right;"><%= time_ago_in_words(activity.created_at) %></span>
10 10 </p>
11 11 <p>
12 12 <span style="font-size: 14px;"><%= link_to(activity.params['name'], activity.params['url'], :style => "color: #333; font-weight: bold; text-decoration: none;") %></span>
... ...
app/views/person_notifier/mailer/_default_activity.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <td>
6 6 <p>
7 7 <span style="font-size: 14px;"><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></span>
8   - <span style="font-size: 10px; color: #929292; float: right;"><%= time_ago_as_sentence(activity.created_at) %></span>
  8 + <span style="font-size: 10px; color: #929292; float: right;"><%= time_ago_in_words(activity.created_at) %></span>
9 9 </p>
10 10 </td>
11 11 </tr>
... ...
app/views/person_notifier/mailer/_task.html.erb
... ... @@ -12,7 +12,7 @@
12 12 <span style="font-size: 14px">
13 13 <%= task_information(task) %>
14 14 </span>
15   - <span style="font-size: 10px; color: #929292; float: right;"><%= time_ago_as_sentence(task.created_at) %></span>
  15 + <span style="font-size: 10px; color: #929292; float: right;"><%= time_ago_in_words(task.created_at) %></span>
16 16 </div>
17 17 </td>
18 18 </tr>
... ...
app/views/person_notifier/mailer/_upload_image.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <td>
6 6 <p>
7 7 <span style="font-size: 14px;"><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></span>
8   - <span style="font-size: 10px; color: #929292; float:right;"><%= time_ago_as_sentence(activity.created_at) %></span>
  8 + <span style="font-size: 10px; color: #929292; float:right;"><%= time_ago_in_words(activity.created_at) %></span>
9 9 </p>
10 10 </td>
11 11 </tr>
... ...
app/views/profile/_comment.html.erb
... ... @@ -40,7 +40,7 @@
40 40 <%= txt2html comment.body %>
41 41 </div>
42 42 <div class="profile-activity-time">
43   - <%= time_ago_as_sentence(comment.created_at) %>
  43 + <%= time_ago_in_words(comment.created_at) %>
44 44 </div>
45 45 </div>
46 46  
... ...
app/views/profile/_create_article.html.erb
... ... @@ -12,7 +12,7 @@
12 12 <%= image_tag(activity.params['first_image']) unless activity.params['first_image'].blank? %><%= strip_tags(truncate(activity.params['lead'], :length => 1000, :ommision => '...')).gsub(/(\xC2\xA0|\s)+/, ' ').gsub(/^\s+/, '') unless activity.params['lead'].blank? %> <small><%= link_to(_('See more'), activity.params['url']) unless activity.get_lead.blank? %></small>
13 13 </div>
14 14 <%= content_tag(:p, link_to(_('See complete forum'), activity.get_url), :class => 'see-forum') if activity.target.is_a?(Forum) %>
15   - <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p>
  15 + <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p>
16 16 <div class='profile-wall-actions'>
17 17 <%= link_to s_('profile|Comment'), '#', { :class => 'focus-on-comment'} %>
18 18 <%= link_to_function(_('Remove'), 'remove_item_wall(this, \'%s\', \'%s\', \'%s\'); return false ;' % [".profile-activity-item", url_for(:profile => params[:profile], :action => :remove_activity, :activity_id => activity.id, :only_hide => true, :view => params[:view]), _('Are you sure you want to remove this activity and all its replies?')]) if logged_in? && current_person == @profile %>
... ...
app/views/profile/_default_activity.html.erb
... ... @@ -3,7 +3,7 @@
3 3 </div>
4 4 <div class='profile-activity-description'>
5 5 <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p>
6   - <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p>
  6 + <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p>
7 7 <div class='profile-wall-actions'>
8 8 <%= link_to s_('profile|Comment'), '#', { :class => 'focus-on-comment'} %>
9 9 <%= link_to_function(_('Remove'), 'remove_item_wall(this, \'%s\', \'%s\', \'%s\'); return false ;' % [".profile-activity-item", url_for(:profile => params[:profile], :action => :remove_activity, :activity_id => activity.id, :view => params[:view]), _('Are you sure you want to remove this activity and all its replies?')]) if logged_in? && current_person == @profile %>
... ...
app/views/profile/_leave_scrap.html.erb
... ... @@ -3,7 +3,7 @@
3 3 </div>
4 4 <div class='profile-activity-description'>
5 5 <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p>
6   - <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p>
  6 + <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p>
7 7 <div class='profile-wall-actions'>
8 8 <%= link_to_function(_('Remove'), 'remove_item_wall(this, \'%s\', \'%s\', \'%s\'); return false ;' % [".profile-activity-item", url_for(:profile => params[:profile], :action => :remove_activity, :activity_id => activity.id, :view => params[:view]), _('Are you sure you want to remove this activity and all its replies?')]) if logged_in? && current_person == @profile %>
9 9 </div>
... ...
app/views/profile/_profile_scrap.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <div class='profile-activity-description'>
6 6 <p class='profile-activity-sender'><%= link_to scrap.sender.name, scrap.sender.url %></p>
7 7 <p class='profile-activity-text'><%= txt2html scrap.content %></p>
8   - <p class='profile-activity-time'><%= time_ago_as_sentence(scrap.created_at) %></p>
  8 + <p class='profile-activity-time'><%= time_ago_in_words(scrap.created_at) %></p>
9 9 <div class='profile-wall-actions'>
10 10 <% if logged_in? && current_person.follows?(scrap.sender) %>
11 11 <span class='profile-activity-send-reply'>
... ... @@ -22,5 +22,5 @@
22 22 <% end %>
23 23 </ul>
24 24 <%= render :partial => 'profile_scrap_reply_form', :locals => { :scrap => scrap } %>
25   - <hr />
  25 + <hr />
26 26 </li>
... ...
app/views/profile/_profile_scraps.html.erb
... ... @@ -5,7 +5,7 @@
5 5 <div class='profile-activity-description'>
6 6 <p class='profile-activity-sender'><%= link_to scrap.sender.name, scrap.sender.url %></p>
7 7 <p class='profile-activity-text'><%= txt2html scrap.content %></p>
8   - <p class='profile-activity-time'><%= time_ago_as_sentence(scrap.created_at) %></p>
  8 + <p class='profile-activity-time'><%= time_ago_in_words(scrap.created_at) %></p>
9 9 <div class='profile-wall-actions'>
10 10 <% if logged_in? && current_person.follows?(scrap.sender) %>
11 11 <span class='profile-activity-send-reply'>
... ...
app/views/profile/_upload_image.html.erb
... ... @@ -4,7 +4,7 @@
4 4 </div>
5 5 <div class='profile-activity-description'>
6 6 <p class='profile-activity-text'><%= link_to activity.user.name, activity.user.url %> <%= describe activity %></p>
7   - <p class='profile-activity-time'><%= time_ago_as_sentence(activity.created_at) %></p>
  7 + <p class='profile-activity-time'><%= time_ago_in_words(activity.created_at) %></p>
8 8 <div class='profile-wall-actions'>
9 9 <%= link_to_function(_('Remove'), 'remove_item_wall(this, \'%s\', \'%s\', \'%s\'); return false ;' % [".profile-activity-item", url_for(:profile => params[:profile], :action => :remove_activity, :activity_id => activity.id, :view => params[:view]), _('Are you sure you want to remove this activity and all its replies?')]) if logged_in? && current_person == @profile %>
10 10 </div>
... ...
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/_content_item.html.erb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<div id="list-item">
  2 + <div class="item-info">
  3 + <div class="item-icon" >
  4 + <%= display_content_icon(content) %>
  5 + </div>
  6 + <span class="item-description">
  7 + <%= link_to(content.name, content.url) %>
  8 + </span>
  9 + <span class="item-date"><%= _("Published at: #{show_date(content.updated_at)}") %></span>
  10 + </div>
  11 +</div>
... ...
app/views/shared/_lead_and_body.html.erb
... ... @@ -19,17 +19,27 @@
19 19  
20 20 <div class='article-lead' id="article-lead-<%=lead_id.to_s%>">
21 21  
  22 + <% abstract_options = {:style => 'width: 100%; height: 200px;', :class => editor_type} %>
22 23 <% if f %>
23   - <%= labelled_form_field(_(abstract_label), f.text_area(abstract_method, :style => 'width: 100%; height: 200px;', :class => editor_type)) %>
  24 + <%= labelled_form_field(_(abstract_label), f.text_area(abstract_method, abstract_options)) %>
24 25 <% else %>
25   - <%= labelled_form_field(_(abstract_label), text_area(object, abstract_method, :style => 'width: 100%; height: 200px;', :class => editor_type)) %>
  26 + <% if @article.kind_of?(Article) %>
  27 + <%= labelled_form_field(_(abstract_label), text_area_tag("article[abstract]", @escaped_abstract, abstract_options)) %>
  28 + <% else %>
  29 + <%= labelled_form_field(_(abstract_label), text_area(object, abstract_method, abstract_options)) %>
  30 + <% end %>
26 31 <% end %>
27 32 </div>
28 33 <div style="margin-top: 10px;">
  34 + <% body_options = {:style => 'width: 100%; height: 400px;', :class => editor_type} %>
29 35 <% if f %>
30   - <%= labelled_form_field(_(body_label), f.text_area(body_method, :style => 'width: 100%; height: 400px;', :class => editor_type)) %>
  36 + <%= labelled_form_field(_(body_label), f.text_area(body_method, body_options)) %>
31 37 <% else %>
32   - <%= labelled_form_field(_(body_label), text_area(object, body_method, :style => 'width: 100%; height: 400px;', :class => editor_type)) %>
  38 + <% if @article.kind_of?(Article) %>
  39 + <%= labelled_form_field(_(body_label), text_area_tag("article[body]", @escaped_body, body_options)) %>
  40 + <% else %>
  41 + <%= labelled_form_field(_(body_label), text_area(object, body_method, body_options)) %>
  42 + <% end %>
33 43 <% end %>
34 44 </div>
35 45  
... ...
app/views/shared/content_list.html.erb
1   -<table class="<%= list_type %>-content">
2   - <tr>
3   - <th><%= _('Title') %></th>
4   - <th><%= _('Last update') %></th>
5   - </tr>
  1 +<ul class="<%= list_type %>-content">
6 2 <% contents.each do |content| %>
7   - <% if content.display_to?(user) %>
8   - <%= display_content_in_listing :content=>content, :list_type=>list_type, :recursive=>recursive %>
9   - <% end %>
  3 + <li class="<%= list_type %>-item">
  4 + <% if content.display_to?(user) %>
  5 + <%= render :partial => 'shared/content_item', :locals => { :content => content } %>
  6 + <% end %>
  7 + </li>
10 8 <% end %>
11   -</table>
  9 +</ul>
12 10  
13 11 <p><%= pagination_links contents, :param_name => 'npage', :page_links => true %></p>
... ...
app/views/tasks/_abuse_complaint_accept_details.html.erb
... ... @@ -2,7 +2,7 @@
2 2 <% task.abuse_reports.each do |abuse_report| %>
3 3 <div>
4 4 <strong style="word-wrap: break-word; display: block; padding-right: 40px">"<%= abuse_report.reason %>"</strong> <br />
5   - <i><%= _('Reported by %{reporter} %{time}.') % {:reporter => abuse_report.reporter.name, :time => time_ago_as_sentence(abuse_report.created_at) }%></i> <br />
  5 + <i><%= _('Reported by %{reporter} %{time}.') % {:reporter => abuse_report.reporter.name, :time => time_ago_in_words(abuse_report.created_at) }%></i> <br />
6 6 <% if !abuse_report.content.blank? %>
7 7 <button class="display-abuse-report-details" data-report="<%=abuse_report.id%>"><%=_('View details')%></button>
8 8 <div style='display: none' id=<%= 'abuse-report-details-'+abuse_report.id.to_s %> class="abuse-report-details">
... ...
app/views/tasks/_suggest_article_accept_details.html.erb
... ... @@ -14,6 +14,5 @@
14 14 <%= labelled_form_field(_('Highlight this article'), a.check_box(:highlighted)) %>
15 15  
16 16 <%= a.hidden_field(:type) %>
17   -
18 17 <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :f => a, :lead_id => task.id} %>
19 18 <% end %>
... ...
config/application.rb
... ... @@ -138,7 +138,7 @@ module Noosfero
138 138 config.middleware.use Rack::Cors do
139 139 allow do
140 140 origins '*'
141   - resource 'api/*', :headers => :any, :methods => [:get, :post]
  141 + resource '/api/*', :headers => :any, :methods => [:get, :post]
142 142 end
143 143 end
144 144  
... ...
config/database.yml.gitlab-ci 0 → 100644
... ... @@ -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 @@
  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 @@
  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/noosfero_extensions.rb
1 1 require 'noosfero/role_assignment_ext'
2 2 require 'noosfero/action_tracker_ext'
  3 +require 'noosfero/vote_ext'
... ...
config/noosfero.yml.dist
... ... @@ -11,6 +11,9 @@ development:
11 11 max_upload_size: 5MB
12 12 hours_until_user_activation_check: 72
13 13 exclude_profile_identifier_pattern: index(\..*)?|home(\..*)?
  14 + api_recaptcha_site_key: '6LdsWAcTAAAAAChTUUD6yu9fCDhdIZzNd7F53zf-'
  15 + api_recaptcha_private_key: '6LdsWAcTAAAAAB6maB_HalVyCc4asDAxPxloIMvY'
  16 + api_recaptcha_verify_uri: 'https://www.google.com/recaptcha/api/siteverify'
14 17  
15 18 #Google Recaptcha setup
16 19 api_captcha_enabled: true
... ...
config/routes.rb
... ... @@ -22,6 +22,7 @@ Noosfero::Application.routes.draw do
22 22 root :to => 'home#index', :constraints => EnvironmentDomainConstraint.new
23 23  
24 24 match 'site(/:action)', :controller => 'home'
  25 + match 'api(/:action)', :controller => 'api'
25 26  
26 27 match 'images(/*stuff)' => 'not_found#nothing'
27 28 match 'stylesheets(/*stuff)' => 'not_found#nothing'
... ...
db/migrate/20140407013817_add_private_token_info_to_users.rb
... ... @@ -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/20140708123314_index_role_assignments_filtered_fields.rb 0 → 100644
... ... @@ -0,0 +1,15 @@
  1 +class IndexRoleAssignmentsFilteredFields < ActiveRecord::Migration
  2 +
  3 + def self.up
  4 + add_index :role_assignments, [:accessor_id, :accessor_type]
  5 + add_index :role_assignments, [:accessor_id, :accessor_type, :role_id], name: :index_on_role_assigments_accessor_role
  6 + add_index :role_assignments, [:resource_id, :resource_type]
  7 + add_index :role_assignments, [:resource_id, :resource_type, :role_id], name: :index_on_role_assigments_resource_role
  8 + add_index :role_assignments, [:accessor_id, :accessor_type, :resource_id, :resource_type], name: :index_on_role_assigments_accessor_resource_role
  9 + add_index :profiles, [:type]
  10 + add_index :profiles, [:visible]
  11 + add_index :profiles, [:enabled]
  12 + add_index :profiles, [:validated]
  13 + end
  14 +
  15 +end
... ...
db/migrate/20150223180807_add_private_token_info_to_users.rb 0 → 100644
... ... @@ -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/20150423203352_fix_tags_case_differences.rb 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +class FixTagsCaseDifferences < ActiveRecord::Migration
  2 + def up
  3 + tags = ActsAsTaggableOn::Tag.joins('LEFT JOIN tags as b on LOWER(tags.name) = b.name').where('b.id is null')
  4 + tags.find_each do |tag|
  5 + unless ActsAsTaggableOn::Tag.exists?(:name => tag.name.mb_chars.downcase)
  6 + ActsAsTaggableOn::Tag.create(:name => tag.name.mb_chars.downcase)
  7 + end
  8 + end
  9 +
  10 + execute("UPDATE taggings SET tag_id = new.id FROM taggings AS t INNER JOIN tags AS old ON t.tag_id = old.id INNER JOIN tags AS new ON LOWER(old.name) = new.name WHERE old.id != new.id AND taggings.id = t.id")
  11 +
  12 + execute("UPDATE tags SET taggings_count = (SELECT COUNT(*) FROM taggings WHERE taggings.tag_id = tags.id)")
  13 + execute("DELETE FROM tags WHERE taggings_count = 0")
  14 + end
  15 +
  16 + def down
  17 + say 'This migration is irreversible.'
  18 + end
  19 +end
... ...
db/migrate/20150529180110_add_date_format_to_environment.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class AddDateFormatToEnvironment < ActiveRecord::Migration
  2 + def up
  3 + add_column :environments, :date_format, :string, :default => 'month_name_with_year'
  4 + end
  5 +
  6 + def down
  7 + remove_column :environments, :date_format
  8 + end
  9 +end
... ...
db/migrate/20150603182105_add_label_to_image.rb 0 → 100644
... ... @@ -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/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 12 # It's strongly recommended to check this file into your version control system.
13 13  
14   -ActiveRecord::Schema.define(:version => 20150525101430) do
  14 +ActiveRecord::Schema.define(:version => 20150603182105) do
15 15  
16 16 create_table "abuse_reports", :force => true do |t|
17 17 t.integer "reporter_id"
... ... @@ -321,17 +321,18 @@ ActiveRecord::Schema.define(:version =&gt; 20150525101430) do
321 321 t.text "design_data"
322 322 t.text "custom_header"
323 323 t.text "custom_footer"
324   - t.string "theme", :default => "default", :null => false
  324 + t.string "theme", :default => "default", :null => false
325 325 t.text "terms_of_use_acceptance_text"
326 326 t.datetime "created_at"
327 327 t.datetime "updated_at"
328   - t.integer "reports_lower_bound", :default => 0, :null => false
  328 + t.integer "reports_lower_bound", :default => 0, :null => false
329 329 t.string "redirection_after_login", :default => "keep_on_same_page"
330 330 t.text "signup_welcome_text"
331 331 t.string "languages"
332 332 t.string "default_language"
333 333 t.string "noreply_email"
334 334 t.string "redirection_after_signup", :default => "keep_on_same_page"
  335 + t.string "date_format", :default => "month_name_with_year"
335 336 end
336 337  
337 338 create_table "external_feeds", :force => true do |t|
... ... @@ -376,6 +377,7 @@ ActiveRecord::Schema.define(:version =&gt; 20150525101430) do
376 377 t.integer "width"
377 378 t.integer "height"
378 379 t.boolean "thumbnails_processed", :default => false
  380 + t.string "label", :default => ""
379 381 end
380 382  
381 383 add_index "images", ["parent_id"], :name => "index_images_on_parent_id"
... ... @@ -692,6 +694,7 @@ ActiveRecord::Schema.define(:version =&gt; 20150525101430) do
692 694 t.integer "image_id"
693 695 t.boolean "spam", :default => false
694 696 t.integer "responsible_id"
  697 + t.integer "closed_by_id"
695 698 end
696 699  
697 700 add_index "tasks", ["requestor_id"], :name => "index_tasks_on_requestor_id"
... ...
debian/changelog
... ... @@ -6,7 +6,10 @@ noosfero (1.2~1) UNRELEASED; urgency=medium
6 6 [ Joenio Costa ]
7 7 * Build noosfero-chat package
8 8  
9   - -- Joenio Costa <joenio@colivre.coop.br> Mon, 18 May 2015 14:32:21 -0300
  9 + [ root ]
  10 + * Api test
  11 +
  12 + -- root <root@17edebf1ae91> Mon, 06 Jul 2015 14:39:06 +0000
10 13  
11 14 noosfero (1.1) wheezy; urgency=low
12 15  
... ...
features/approve_article.feature
... ... @@ -62,7 +62,8 @@ Feature: approve article
62 62 And I press "Spread this"
63 63 And I follow "Delete"
64 64 And I confirm the browser dialog
65   - When I am logged in as "joaosilva"
  65 + And I follow "Logout"
  66 + And I am logged in as "joaosilva"
66 67 And I go to sample-community's control panel
67 68 And I follow "Process requests"
68 69 And I choose "Reject"
... ...
features/article_visualization.feature 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +Feature: article visualization
  2 + As a user
  3 + I want to change view modes
  4 + In order to see articles in fullscreen or not in fullscreen
  5 +
  6 + Background:
  7 + Given the following users
  8 + | login | name |
  9 + | joaosilva | Joao Silva |
  10 + And "joaosilva" has no articles
  11 + And the following articles
  12 + | owner | name | body |
  13 + | joaosilva | Sample Article | This is an article |
  14 + And I am logged in as "joaosilva"
  15 +
  16 + @selenium
  17 + Scenario: viewing the article in fullscreen by default
  18 + Given I go to /joaosilva/sample-article?fullscreen=1
  19 + Then I should see "Exit full screen"
  20 +
  21 + @selenium
  22 + Scenario: viewing the article not in fullscreen by default
  23 + Given I go to /joaosilva/sample-article
  24 + Then I should see "Full screen"
  25 +
  26 + @selenium
  27 + Scenario: changing the view mode from not in fullscreen to fullscreen
  28 + Given I go to /joaosilva/sample-article
  29 + And I follow "Full screen"
  30 + Then I should see "Exit full screen"
... ...
features/balloon.feature
... ... @@ -41,19 +41,19 @@ Feature: balloon
41 41 @selenium
42 42 Scenario: I should not see trigger if not enabled on page
43 43 Given feature "show_balloon_with_profile_links_when_clicked" is disabled on environment
44   - When I go to /assets/people
  44 + When I go to /search/people
45 45 Then I should not see "Profile links"
46 46  
47 47 @selenium
48 48 Scenario: I should not see trigger by default on page
49 49 Given feature "show_balloon_with_profile_links_when_clicked" is enabled on environment
50   - When I go to /assets/communities
  50 + When I go to /search/communities
51 51 Then I should not see "Members"
52 52  
53 53 @selenium
54 54 Scenario: I should see balloon when clicked on page trigger
55 55 Given feature "show_balloon_with_profile_links_when_clicked" is enabled on environment
56   - And I go to /assets/communities
  56 + And I go to /search/communities
57 57 And display ".community-trigger"
58 58 When I follow "Profile links"
59 59 Then I should see "Members"
... ...
features/browse_enterprises.feature
... ... @@ -13,20 +13,20 @@ Scenario: show all enterprises
13 13 Given the following enterprises
14 14 | identifier | name |
15 15 | shop2 | Fruits Shop |
16   - Given I am on /assets/enterprises
  16 + Given I am on /search/enterprises
17 17 Then I should see "Enterprises"
18 18 And I should see "Shoes Shop"
19 19 And I should see "Fruits Shop"
20 20  
21 21 Scenario: show profile links button
22   - Given I am on /assets/enterprises
  22 + Given I am on /search/enterprises
23 23 Then I should see "Profile links" within "a.enterprise-trigger"
24 24 And I should not see "Members"
25 25 And I should not see "Agenda"
26 26  
27 27 @selenium-fixme
28 28 Scenario: show profile links when clicked
29   - Given I am on /assets/enterprises
  29 + Given I am on /search/enterprises
30 30 When I follow "Profile links"
31 31 Then I should see "Products" within "ul.menu-submenu-list"
32 32 And I should see "Members" within "ul.menu-submenu-list"
... ... @@ -34,7 +34,7 @@ Scenario: show profile links when clicked
34 34  
35 35 @selenium-fixme
36 36 Scenario: go to catalog when click on products link
37   - Given I am on /assets/enterprises
  37 + Given I am on /search/enterprises
38 38 When I follow "Profile links"
39 39 And I follow "Products" and wait
40 40 Then I should be exactly on /catalog/shop1
... ...
features/change_appearance.feature
... ... @@ -14,7 +14,7 @@ Feature: Change appearance
14 14 And I should not see an element ".box-4"
15 15 And I go to joaosilva's control panel
16 16 And I follow "Edit Appearance"
17   - And I follow "Left Top and Right"
  17 + And I follow "Top and Side Bars"
18 18 And I go to joaosilva's control panel
19 19 And I follow "Edit sideboxes"
20 20 And I should see an element ".box-4"
... ...
features/events.feature
... ... @@ -17,7 +17,7 @@ Feature: events
17 17 Then I should see "November 2009" within ".current-month"
18 18  
19 19 Scenario: go to next month in global agenda
20   - Given I am on /assets/events?year=2009&month=11
  20 + Given I am on /search/events?year=2009&month=11
21 21 When I follow "December"
22 22 Then I should see "December 2009" within ".current-month"
23 23  
... ... @@ -27,7 +27,7 @@ Feature: events
27 27 Then I should see "September 2009" within ".current-month"
28 28  
29 29 Scenario: go to previous month in global agenda
30   - Given I am on /assets/events?year=2009&month=11
  30 + Given I am on /search/events?year=2009&month=11
31 31 When I follow "October"
32 32 Then I should see "October 2009" within ".current-month"
33 33  
... ... @@ -43,7 +43,7 @@ Feature: events
43 43  
44 44 Scenario: go to specific day in global agenda
45 45 Given I am on the homepage
46   - When I am on /assets/events?year=2009&month=11&day=12
  46 + When I am on /search/events?year=2009&month=11&day=12
47 47 Then I should see "Events for November, 2009"
48 48  
49 49 Scenario: list events for specific day
... ... @@ -88,7 +88,7 @@ Feature: events
88 88 And the following events
89 89 | owner | name | start_date |
90 90 | josemanuel | Manuel Birthday | 2009-10-24 |
91   - When I am on /assets/events?year=2009&month=10&day=24
  91 + When I am on /search/events?year=2009&month=10&day=24
92 92 Then I should see "Another Conference"
93 93 And I should see "Manuel Birthday"
94 94  
... ... @@ -157,9 +157,10 @@ Feature: events
157 157 Then I should not see "New events" link
158 158  
159 159 Scenario: display environment name in global agenda
160   - When I am on /assets/events
  160 + When I am on /search/events
161 161 Then I should see "Colivre.net's Events"
162 162  
  163 +
163 164 @selenium
164 165 Scenario: published events should be listed in the agenda too
165 166 Given the following community
... ...
features/profile_domain.feature
... ... @@ -21,9 +21,7 @@ Feature: domain for profile
21 21 Scenario: access profile control panel through profile blocks
22 22 Given I am logged in as "joaosilva"
23 23 When I go to joaosilva's homepage
24   - And I follow "Control panel" within ".profile-info-block"
25   - Then I should see "Joao Silva" within "span.control-panel-title"
26   - When I follow "Control panel" within ".profile-image-block"
  24 + And I follow "Control panel" within ".profile-image-block"
27 25 Then I should see "Joao Silva" within "span.control-panel-title"
28 26  
29 27 @selenium
... ...
features/profile_search.feature
... ... @@ -36,7 +36,7 @@ Feature: search inside a profile
36 36 | joaosilva | ProfileSearchBlock |
37 37 When I go to joaosilva's profile
38 38 And I fill in "q" with "bees" within ".profile-search-block"
39   - And I press "Search"
  39 + And I press "Search" within ".profile-search-block"
40 40 Then I should see "bees and butterflies" within ".main-block"
41 41  
42 42 Scenario: not display unpublished articles
... ...
features/register_enterprise.feature
... ... @@ -203,5 +203,5 @@ Feature: register enterprise
203 203  
204 204 Scenario: a user cant see button to register new enterprise if enterprise_registration disabled
205 205 Given feature "enterprise_registration" is disabled on environment
206   - When I am on /assets/enterprises
  206 + When I am on /search/enterprises
207 207 Then I should not see "New enterprise" link
... ...
features/search_enterprises.feature
... ... @@ -56,7 +56,7 @@ Feature: search enterprises
56 56 | owner | name | body | homepage |
57 57 | shop1 | Shoes home | This is the <i>homepage</i> of Shoes shop! It has a very long and pretty vague description, just so we can test wether the system will correctly create an excerpt of this text. We should probably talk about shoes. | true |
58 58 When I search enterprises for "shoes"
59   - And I choose the search filter "Full"
  59 + And I select "Full" from "display"
60 60 Then I should see "This is the homepage of" within ".search-enterprise-description"
61 61 And I should see "about sho..." within ".search-enterprise-description"
62 62  
... ... @@ -66,7 +66,7 @@ Feature: search enterprises
66 66 | identifier | name | description |
67 67 | shop4 | Clothes shop | This <b>clothes</b> shop also sells shoes! This too has a very long and pretty vague description, just so we can test wether the system will correctly create an excerpt of this text. Clothes are a really important part of our lives. |
68 68 When I search enterprises for "clothes"
69   - And I choose the search filter "Full"
  69 + And I select "Full" from "display"
70 70 And I should see "This clothes shop" within ".search-enterprise-description"
71 71 And I should see "really import..." within ".search-enterprise-description"
72 72  
... ...
features/step_definitions/noosfero_steps.rb
... ... @@ -94,7 +94,7 @@ Given /^the following blocks$/ do |table|
94 94 owner.boxes<< Box.new
95 95 owner.boxes.first.blocks << MainBlock.new
96 96 end
97   - box = owner.boxes.where(:position => 3).first
  97 + box = owner.boxes.first
98 98 klass.constantize.create!(item.merge(:box => box))
99 99 end
100 100 end
... ...
features/suggest_article.feature
... ... @@ -14,9 +14,12 @@ Feature: suggest article
14 14  
15 15 @selenium
16 16 Scenario: highlight an article before approval of a suggested article
17   - Given someone suggested the following article to be published
18   - | target | article_name | article_body | name | email |
19   - | sample-community | A suggested article | this is an article about whales | jose | jose@example.org |
  17 + Given I am on Sample Community's blog
  18 + And I follow "Suggest an article"
  19 + And I fill in "Title" with "Suggestion"
  20 + And I fill in "Your name" with "Some Guy"
  21 + And I fill in "Email" with "someguy@somewhere.com"
  22 + And I press "Save"
20 23 When I am logged in as "joaosilva"
21 24 And I go to sample-community's control panel
22 25 And I follow "Process requests"
... ...
lib/noosfero/api/api.rb
1 1 require 'grape'
2 2 #require 'rack/contrib'
3   -Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require_dependency file unless file =~ /api\.rb/}
  3 +Dir["#{Rails.root}/lib/noosfero/api/*.rb"].each {|file| require file unless file =~ /api\.rb/}
4 4  
5 5 module Noosfero
6 6 module API
... ... @@ -12,36 +12,29 @@ module Noosfero
12 12 use GrapeLogging::Middleware::RequestLogger, { logger: logger }
13 13  
14 14 rescue_from :all do |e|
15   - # Many brave warriors have fallen in the battle for fixing the API log
16   - # Please, don't remove these 2 lines until the API log problem has
17   - # been PROPERLY fixed by our savior!!!
18   - # Otherwise we will have no clue of what went wrong in the API
19   - puts "API error during processing: #{$!}"
20   - puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
21   - # Thanks!
22 15 logger.error e
23 16 end
24 17  
25 18 @@NOOSFERO_CONF = nil
26   -
27 19 def self.NOOSFERO_CONF
28   - if @@NOOSFERO_CONF
  20 + if @@NOOSFERO_CONF
29 21 @@NOOSFERO_CONF
30 22 else
31 23 file = Rails.root.join('config', 'noosfero.yml')
32 24 @@NOOSFERO_CONF = File.exists?(file) ? YAML.load_file(file)[Rails.env] || {} : {}
33   - end
  25 + end
34 26 end
35 27  
36 28 before { setup_multitenancy }
37 29 before { detect_stuff_by_domain }
  30 + before { filter_disabled_plugins_endpoints }
38 31 after { set_session_cookie }
39 32  
40 33 version 'v1'
41 34 prefix "api"
42 35 format :json
43 36 content_type :txt, "text/plain"
44   -
  37 +
45 38 helpers APIHelpers
46 39  
47 40 mount V1::Articles
... ... @@ -54,6 +47,7 @@ module Noosfero
54 47 mount V1::Tasks
55 48 mount V1::Tags
56 49 mount V1::Environments
  50 +
57 51 mount Session
58 52  
59 53 # hook point which allow plugins to add Grape::API extensions to API::API
... ... @@ -66,6 +60,26 @@ module Noosfero
66 60 end
67 61 end
68 62 end
  63 +
  64 + def self.endpoint_unavailable?(endpoint, environment)
  65 + api_class = endpoint.options[:app] || endpoint.options[:for]
  66 + if api_class.present?
  67 + klass = api_class.name.deconstantize.constantize
  68 + return klass < Noosfero::Plugin && !environment.plugin_enabled?(klass)
  69 + end
  70 + end
  71 +
  72 + class << self
  73 + def endpoints_with_plugins(environment = nil)
  74 + if environment.present?
  75 + cloned_endpoints = endpoints_without_plugins.dup
  76 + cloned_endpoints.delete_if { |endpoint| endpoint_unavailable?(endpoint, environment) }
  77 + else
  78 + endpoints_without_plugins
  79 + end
  80 + end
  81 + alias_method_chain :endpoints, :plugins
  82 + end
69 83 end
70 84 end
71   -end
72 85 \ No newline at end of file
  86 +end
... ...
lib/noosfero/api/entities.rb
1 1 module Noosfero
2 2 module API
3 3 module Entities
4   -
  4 +
5 5 Entity.format_with :timestamp do |date|
6 6 date.strftime('%Y/%m/%d %H:%M:%S') if date
7 7 end
8   -
  8 +
9 9 class Image < Entity
10 10 root 'images', 'image'
11 11  
12 12 expose :url do |image, options|
13 13 image.public_filename
14 14 end
15   -
  15 +
16 16 expose :icon_url do |image, options|
17 17 image.public_filename(:icon)
18 18 end
19   -
  19 +
20 20 expose :minor_url do |image, options|
21 21 image.public_filename(:minor)
22 22 end
23   -
  23 +
24 24 expose :portrait_url do |image, options|
25 25 image.public_filename(:portrait)
26 26 end
27   -
  27 +
28 28 expose :thumb_url do |image, options|
29 29 image.public_filename(:thumb)
30 30 end
31 31 end
32   -
  32 +
33 33 class Profile < Entity
34 34 expose :identifier, :name, :id
35 35 expose :created_at, :format_with => :timestamp
36 36 expose :image, :using => Image
37 37 end
38   -
  38 +
  39 + class UserBasic < Entity
  40 + expose :id
  41 + expose :login
  42 + end
  43 +
39 44 class Person < Profile
40 45 root 'people', 'person'
  46 + expose :user, :using => UserBasic
41 47 end
  48 +
42 49 class Enterprise < Profile
43 50 root 'enterprises', 'enterprise'
44 51 end
  52 +
45 53 class Community < Profile
46 54 root 'communities', 'community'
47 55 expose :description
48 56 end
49   -
50   - class Category < Entity
  57 +
  58 + class CategoryBase < Entity
  59 + root 'categories', 'category'
  60 + expose :name, :id
  61 + end
  62 +
  63 + class Category < CategoryBase
51 64 root 'categories', 'category'
52   - expose :name, :id, :slug
  65 + expose :slug
  66 + expose :full_name do |category, options|
  67 + category.full_name
  68 + end
  69 + expose :parent, :using => CategoryBase, if: { parent: true }
  70 + expose :children, :using => CategoryBase, if: { children: true }
53 71 expose :image, :using => Image
54 72 end
55   -
  73 +
56 74 class ArticleBase < Entity
57 75 root 'articles', 'article'
58 76 expose :id
... ... @@ -87,8 +105,8 @@ module Noosfero
87 105 expose :created_at, :format_with => :timestamp
88 106 expose :author, :using => Profile
89 107 end
90   -
91   -
  108 +
  109 +
92 110 class User < Entity
93 111 root 'users', 'user'
94 112 expose :id
... ... @@ -98,13 +116,13 @@ module Noosfero
98 116 output = {}
99 117 user.person.role_assignments.map do |role_assigment|
100 118 if role_assigment.resource.respond_to?(:identifier)
101   - output[role_assigment.resource.identifier] = role_assigment.role.permissions
  119 + output[role_assigment.resource.identifier] = role_assigment.role.permissions
102 120 end
103 121 end
104 122 output
105 123 end
106 124 end
107   -
  125 +
108 126 class UserLogin < User
109 127 expose :private_token
110 128 end
... ... @@ -119,6 +137,12 @@ module Noosfero
119 137 expose :name
120 138 end
121 139  
  140 + class Tag < Entity
  141 + root 'tags', 'tag'
  142 + expose :name
  143 + end
  144 +
  145 +
122 146 end
123 147 end
124 148 end
... ...