Commit e9fea38f770b7c495ce653da719d5394dbbf7b7b

Authored by Rodrigo Souto
2 parents 504bc402 aa0f384c

Merge branch 'master' into rails3-merge

Conflicts:
	app/controllers/my_profile/cms_controller.rb
	app/controllers/public/content_viewer_controller.rb
	app/helpers/application_helper.rb
	app/helpers/article_helper.rb
	app/helpers/catalog_helper.rb
	app/helpers/comment_helper.rb
	app/models/article.rb
	app/models/enterprise.rb
	app/models/members_block.rb
	app/models/person.rb
	app/models/profile.rb
	app/views/blocks/profile_info.html.erb
	app/views/blocks/profile_info_actions/community.html.erb
	config/initializers/dependencies.rb
	config/routes.rb
	db/schema.rb
	debian/control
	lib/noosfero/plugin.rb
	lib/tasks/plugins.rake
	plugins/custom_forms/views/custom_forms_plugin_myprofile/_form.html.erb
	script/sample-products
	test/functional/catalog_controller_test.rb
	test/functional/manage_products_controller_test.rb
	test/functional/map_balloon_controller_test.rb
	test/functional/profile_controller_test.rb
	test/functional/search_controller_test.rb
	test/unit/box_test.rb
	test/unit/cms_helper_test.rb
	test/unit/enterprise_test.rb
	test/unit/environment_test.rb
	test/unit/featured_products_block_test.rb
	test/unit/person_test.rb
	test/unit/product_category_test.rb
	test/unit/product_test.rb
	test/unit/profile_test.rb
	test/unit/user_test.rb
	vendor/plugins/monkey_patches/methods_from_fake_arel/init.rb
Showing 351 changed files with 82949 additions and 72845 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 351 files displayed.

@@ -1,217 +0,0 @@ @@ -1,217 +0,0 @@
1 -If you are not listed here, but should be, please write to the noosfero mailing  
2 -list: http://listas.softwarelivre.org/cgi-bin/mailman/listinfo/noosfero-dev  
3 -(this list requires subscription to post, but since you are an author of  
4 -noosfero, that's not a problem).  
5 -  
6 -Developers  
7 -==========  
8 -  
9 -Alan Freihof Tygel <alantygel@gmail.com>  
10 -Alessandro Palmeira <alessandro.palmeira@gmail.com>  
11 -Alessandro Palmeira + Caio C. Salgado <alessandro.palmeira@gmail.com>  
12 -Alessandro Palmeira + Caio Salgado <alessandro.palmeira@gmail.com>  
13 -Alessandro Palmeira + Caio Salgado <caio.csalgado@gmail.com>  
14 -Alessandro Palmeira + Caio Salgado + Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com>  
15 -Alessandro Palmeira + Carlos Morais <alessandro.palmeira@gmail.com>  
16 -Alessandro Palmeira + Daniel Alves <alessandro.palmeira@gmail.com>  
17 -Alessandro Palmeira + Daniel Alves + Diego Araújo <diegoamc90@gmail.com>  
18 -Alessandro Palmeira + Daniel Alves + Diego Araújo + Guilherme Rojas <danpaulalves@gmail.com>  
19 -Alessandro Palmeira + Diego Araujo <alessandro.palmeira@gmail.com>  
20 -Alessandro Palmeira + Diego Araújo <alessandro.palmeira@gmail.com>  
21 -Alessandro Palmeira + Diego Araujo + Daniela Feitosa <alessandro.palmeira@gmail.com>  
22 -Alessandro Palmeira + Diego Araujo <diegoamc90@gmail.com>  
23 -Alessandro Palmeira + Diego Araújo <diegoamc90@gmail.com>  
24 -Alessandro Palmeira + Diego Araujo + Eduardo Morais <alessandro.palmeira@gmail.com>  
25 -Alessandro Palmeira + Diego Araújo + João M. M. da Silva <alessandro.palmeira@gmail.com>  
26 -Alessandro Palmeira + Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com>  
27 -Alessandro Palmeira + Diego Araujo + João M. M. da Silva + Paulo Meirelles <alessandro.palmeira@gmail.com>  
28 -Alessandro Palmeira + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>  
29 -Alessandro Palmeira + Diego Araújo + Pedro Leal + João M. M. da Silva <diegoamc90@gmail.com>  
30 -Alessandro Palmeira + Diego Araujo + Rafael Manzo <alessandro.palmeira@gmail.com>  
31 -Alessandro Palmeira + Eduardo Morais <alessandro.palmeira@gmail.com>  
32 -Alessandro Palmeira + Guilherme Rojas <alessandro.palmeira@gmail.com>  
33 -Alessandro Palmeira + Jefferson Fernandes <alessandro.palmeira@gmail.com>  
34 -Alessandro Palmeira + João M. M. da Silva <alessandro.palmeira@gmail.com>  
35 -Alessandro Palmeira + Joao M. M. da Silva + Diego Araujo <alessandro.palmeira@gmail.com>  
36 -Alessandro Palmeira + João M. M. da Silva + Renan Teruo <alessandro.palmeira@gmail.com>  
37 -Alessandro Palmeira + João M. M. Silva <alessandro.palmeira@gmail.com>  
38 -Alessandro Palmeira + Paulo Meirelles <alessandro.palmeira@gmail.com>  
39 -Alessandro Palmeira + Paulo Meirelles + João M. M. da Silva <alessandro.palmeira@gmail.com>  
40 -Alessandro Palmeira + Rafael Manzo <alessandro.palmeira@gmail.com>  
41 -Antonio Terceiro + Carlos Morais <terceiro@colivre.coop.br>  
42 -Antonio Terceiro + Paulo Meirelles <terceiro@colivre.coop.br>  
43 -Antonio Terceiro <terceiro@colivre.coop.br>  
44 -Aurelio A. Heckert <aurelio@colivre.coop.br>  
45 -Braulio Bhavamitra <brauliobo@gmail.com>  
46 -Bráulio Bhavamitra <brauliobo@gmail.com>  
47 -Braulio Bhavamitra <braulio@eita.org.br>  
48 -Caio <caio.csalgado@gmail.com>  
49 -Caio + Diego + Pedro + João <caio.csalgado@gmail.com>  
50 -Caio Formiga <caio.formiga@gmail.com>  
51 -Caio, Pedro <caio.csalgado@gmail.com>  
52 -Caio Salgado + Alessandro Palmeira <caio.csalgado@gmail.com>  
53 -Caio Salgado <caio.csalgado@gmail.com>  
54 -Caio Salgado + Carlos Morais + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>  
55 -Caio Salgado + Diego Araujo <caio.csalgado@gmail.com>  
56 -Caio Salgado + Diego Araújo <caio.csalgado@gmail.com>  
57 -Caio Salgado + Diego Araújo <diegoamc90@gmail.com>  
58 -Caio Salgado + Diego Araújo + Jefferson Fernandes <caio.csalgado@gmail.com>  
59 -Caio Salgado + Diego Araújo + João M. M. da Silva <caio.csalgado@gmail.com>  
60 -Caio Salgado + Diego Araújo + Pedro Leal <caio.csalgado@gmail.com>  
61 -Caio Salgado + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>  
62 -Caio Salgado + Diego Araújo + Rafael Manzo <diegoamc90@gmail.com>  
63 -Caio Salgado + Jefferson Fernandes <caio.csalgado@gmail.com>  
64 -Caio Salgado + Jefferson Fernandes <jeffs.fernandes@gmail.com>  
65 -Caio Salgado + Rafael Manzo <caio.csalgado@gmail.com>  
66 -Caio Salgado + Renan Teruo <caio.csalgado@gmail.com>  
67 -Caio Salgado + Renan Teruo <caio.salgado@gmail.com>  
68 -Caio Salgado + Renan Teruo + Jefferson Fernandes <jeffs.fernandes@gmail.com>  
69 -Caio Salgado + Renan Teruo <renanteruoc@gmail.com>  
70 -Caio SBA <caio@colivre.coop.br>  
71 -Carlos Morais <carlos88morais@gmail.com>  
72 -Carlos Morais + Diego Araújo <diegoamc90@gmail.com>  
73 -Carlos Morais + Eduardo Morais <carlos88morais@gmail.com>  
74 -Carlos Morais + Paulo Meirelles <carlos88morais@gmail.com>  
75 -Carlos Morais + Pedro Leal <carlos88morais@gmail.com>  
76 -Daniel Alves + Diego Araújo <danpaulalves@gmail.com>  
77 -Daniel Alves + Diego Araújo <diegoamc90@gmail.com>  
78 -Daniel Alves + Diego Araújo + Guilherme Rojas <danpaulalves@gmail.com>  
79 -Daniel Alves + Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com>  
80 -Daniel Alves + Diego Araújo + Guilherme Rojas <guilhermehrojas@gmail.com>  
81 -Daniel Alves + Guilherme Rojas <danpaulalves@gmail.com>  
82 -Daniel Alves + Rafael Manzo <rr.manzo@gmail.com>  
83 -Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>  
84 -Daniel Cunha <daniel@colivre.coop.br>  
85 -diegoamc <diegoamc90@gmail.com>  
86 -Diego Araújo + Alessandro Palmeira <diegoamc90@gmail.com>  
87 -Diego Araújo + Alessandro Palmeira + João M. M. da Silva <diegoamc90@gmail.com>  
88 -Diego Araújo + Alessandro Palmeira + Rafael Manzo <rr.manzo@gmail.com>  
89 -Diego Araujo + Caio Salgado <diegoamc90@gmail.com>  
90 -Diego Araújo + Daniel Alves + Rafael Manzo <rr.manzo@gmail.com>  
91 -Diego Araújo <diegoamc90@gmail.com>  
92 -Diego Araújo + Eduardo Morais + Paulo Meirelles <diegoamc90@gmail.com>  
93 -Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com>  
94 -Diego Araújo + Jefferson Fernandes <diegoamc90@gmail.com>  
95 -Diego Araujo + Jefferson Fernandes <jeffs.fernandes@gmail.com>  
96 -Diego Araújo + João Machini <diegoamc90@gmail.com>  
97 -Diego Araújo + João Machini <digoamc90@gmail.com>  
98 -Diego Araújo + João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>  
99 -Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com>  
100 -Diego Araújo + João M. M. da Silva + João Machini <diegoamc90@gmail.com>  
101 -Diego Araújo + João M. M. da Silva + Pedro Leal <diegoamc90@gmail.com>  
102 -Diego Araújo + Paulo Meirelles <diegoamc90@gmail.com>  
103 -Diego Araújo + Pedro Leal <diegoamc90@gmail.com>  
104 -Diego Araujo + Rafael Manzo <diegoamc90@gmail.com>  
105 -Diego Araújo + Rafael Manzo <diegoamc90@gmail.com>  
106 -Diego Araújo + Renan Teruo + Alessandro Palmeira <diegoamc90@gmail.com>  
107 -Diego Araújo + Renan Teruo <diegoamc90@gmail.com>  
108 -Diego Araujo + Rodrigo Souto + Rafael Manzo <rr.manzo@gmail.com>  
109 -Diego + Jefferson <diegoamc90@gmail.com>  
110 -Diego Martinez <diegoamc90@gmail.com>  
111 -Diego Martinez <diego@diego-K55A.(none)>  
112 -Diego + Renan <renanteruoc@gmail.com>  
113 -Fernanda Lopes <nanda.listas+psl@gmail.com>  
114 -Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br>  
115 -Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)>  
116 -Grazieno Pellegrino <grazieno@gmail.com>  
117 -Isaac Canan <isaac@intelletto.com.br>  
118 -Italo Valcy <italo@dcc.ufba.br>  
119 -Jefferson Fernandes + Diego Araujo + Rafael Manzo <jeffs.fernandes@gmail.com>  
120 -Jefferson Fernandes + Joao M. M. da Silva <jeffs.fernandes@gmail.com>  
121 -Jefferson Fernandes + Joao M. M. Silva <jeffs.fernandes@gmail.com>  
122 -João da Silva <jaodsilv@linux.ime.usp.br>  
123 -João Marco Maciel da Silva + Rafael Manzo + Renan Teruo <jaodsilv@linux.ime.usp.br>  
124 -João M. M. da Silva + Alessandro Palmeira + Diego Araújo + Caio Salgado <jaodsilv@linux.ime.usp.br>  
125 -João M. M. da Silva + Alessandro Palmeira + Diego Araújo <jaodsilv@linux.ime.usp.br>  
126 -Joao M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>  
127 -João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>  
128 -João M. M. da Silva + Alessandro Palmeira + João Machini <jaodsilv@linux.ime.usp.br>  
129 -João M. M. da Silva + Caio Salgado + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>  
130 -João M. M. da Silva + Caio Salgado <jaodsilv@linux.ime.usp.br>  
131 -João M. M. da Silva + Carlos Morais <jaodsilv@linux.ime.usp.br>  
132 -João M. M. da Silva + Diego Araújo <diegoamc90@gmail.com>  
133 -João M. M. da Silva + Diego Araújo <jaodsilv@linux.ime.usp.br>  
134 -João M. M. da Silva + Diego Araújo + Pedro Leal <jaodsilv@linux.ime.usp.br>  
135 -João M. M. da Silva <jaodsilv@linux.ime.usp.br>  
136 -Joao M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>  
137 -João M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>  
138 -João M. M. da Silva + João M. Miranda <jaodsilv@linux.ime.usp.br>  
139 -João M. M. da Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br>  
140 -João M. M. da Silva + Pedro Leal <jaodsilv@linux.ime.usp.br>  
141 -João M. M. da Silva + Rafael Manzo + Diego Araújo <jaodsilv@linux.ime.usp.br>  
142 -João M. M. da Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br>  
143 -João M. M. da Silva + Renan Teruo <jaodsilv@linux.ime.usp.br>  
144 -João M. M. Silva + Caio Salgado <jaodsilv@linux.ime.usp.br>  
145 -João M. M. Silva + Diego Araújo <jaodsilv@linux.ime.usp.br>  
146 -Joao M. M. Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>  
147 -João M. M. Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br>  
148 -João M. M. Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br>  
149 -João M. M. Silva + Renan Teruo <jaodsilv@linux.ime.usp.br>  
150 -Joenio Costa <joenio@colivre.coop.br>  
151 -Josef Spillner <josef.spillner@tu-dresden.de>  
152 -Junior Silva <juniorsilva1001@gmail.com>  
153 -Junior Silva <juniorsilva7@juniorsilva-Aspire-5750Z.(none)>  
154 -Keilla Menezes <keilla@colivre.coop.br>  
155 -Larissa Reis <larissa@colivre.coop.br>  
156 -Larissa Reis <reiss.larissa@gmail.com>  
157 -Leandro Nunes dos Santos <leandronunes@gmail.com>  
158 -Leandro Nunes dos Santos <leandro.santos@serpro.gov.br>  
159 -LinguÁgil 2010 <linguagil.bahia@gmail.com>  
160 -Lucas Melo <lucas@colivre.coop.br>  
161 -Lucas Melo <lucaspradomelo@gmail.com>  
162 -Luis David Aguilar Carlos <ludwig9003@gmail.com>  
163 -Martín Olivera <molivera@solar.org.ar>  
164 -Moises Machado <moises@colivre.coop.br>  
165 -Naíla Alves <naila@colivre.coop.br>  
166 -Nanda Lopes <nanda.listas+psl@gmail.com>  
167 -Paulo Meirelles + Alessandro Palmeira + João M. M. da Silva <paulo@softwarelivre.org>  
168 -Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org>  
169 -Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org>  
170 -Paulo Meirelles + Diego Araújo <paulo@softwarelivre.org>  
171 -Paulo Meirelles + João M. M. da Silva <paulo@softwarelivre.org>  
172 -Paulo Meirelles <paulo@softwarelivre.org>  
173 -Paulo Meirelles + Rafael Manzo <paulo@softwarelivre.org>  
174 -Rafael Gomes <rafaelgomes@techfree.com.br>  
175 -Rafael Manzo + Alessandro Palmeira <rr.manzo@gmail.com>  
176 -Rafael Manzo + Daniel Alves <danpaulalves@gmail.com>  
177 -Rafael Manzo + Diego Araújo <rr.manzo@gmail.com>  
178 -Rafael Manzo + João M. M. Silva <rr.manzo@gmail.com>  
179 -Rafael Manzo + Paulo Meirelles <rr.manzo@gmail.com>  
180 -Rafael Martins <rmmartins@gmail.com>  
181 -Rafael Reggiani Manzo + Caio Salgado + Jefferson Fernandes <rr.manzo@gmail.com>  
182 -Rafael Reggiani Manzo + Diego Araujo <diegoamc90@gmail.com>  
183 -Rafael Reggiani Manzo + Diego Araujo <rr.manzo@gmail.com>  
184 -Rafael Reggiani Manzo + Diego Araújo <rr.manzo@gmail.com>  
185 -Rafael Reggiani Manzo + João M. M. da Silva <rr.manzo@gmail.com>  
186 -Rafael Reggiani Manzo <rr.manzo@gmail.com>  
187 -Raphaël Rousseau <raph@r4f.org>  
188 -Raquel Lira <raquel.lira@gmail.com>  
189 -Renan Teruo + Caio Salgado <renanteruoc@gmail.com>  
190 -Renan Teruoc + Diego Araujo <renanteruoc@gmail.com>  
191 -Renan Teruo + Diego Araujo <renanteruoc@gmail.com>  
192 -Renan Teruo + Diego Araújo <renanteruoc@gmail.com>  
193 -Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com>  
194 -Renan Teruo + Rafael Manzo <renanteruoc@gmail.com>  
195 -Rodrigo Souto <diguliu@gmail.com>  
196 -Rodrigo Souto <rodrigo@colivre.coop.br>  
197 -Ronny Kursawe <kursawe.ronny@googlemail.com>  
198 -root <root@debian.sdr.serpro>  
199 -Samuel R. C. Vale <srcvale@holoscopio.com>  
200 -Valessio Brito <valessio@gmail.com>  
201 -vfcosta <vfcosta@gmail.com>  
202 -Victor Costa <vfcosta@gmail.com>  
203 -Vinicius Cubas Brand <viniciuscb@gmail.com>  
204 -Visita <visita@debian.(none)>  
205 -Yann Lugrin <yann.lugrin@liquid-concept.ch>  
206 -  
207 -Ideas, specifications and incentive  
208 -===================================  
209 -Daniel Tygel <dtygel@fbes.org.br>  
210 -Guilherme Rocha <guilherme@gf7.com.br>  
211 -Raphael Rousseau <raph@r4f.org>  
212 -Théo Bondolfi <move@cooperation.net>  
213 -Vicente Aguiar <vicenteaguiar@colivre.coop.br>  
214 -  
215 -Arts  
216 -===================================  
217 -Nara Oliveira <narananet@gmail.com>  
AUTHORS.md 0 → 100644
@@ -0,0 +1,214 @@ @@ -0,0 +1,214 @@
  1 +If you are not listed here, but should be, please write to the noosfero mailing list: http://listas.softwarelivre.org/cgi-bin/mailman/listinfo/noosfero-dev (this list requires subscription to post, but since you are an author of noosfero, that's not a problem).
  2 +
  3 +Developers
  4 +==========
  5 +
  6 +Alan Freihof Tygel <alantygel@gmail.com>
  7 +Alessandro Palmeira <alessandro.palmeira@gmail.com>
  8 +Alessandro Palmeira + Caio C. Salgado <alessandro.palmeira@gmail.com>
  9 +Alessandro Palmeira + Caio Salgado <alessandro.palmeira@gmail.com>
  10 +Alessandro Palmeira + Caio Salgado <caio.csalgado@gmail.com>
  11 +Alessandro Palmeira + Caio Salgado + Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com>
  12 +Alessandro Palmeira + Carlos Morais <alessandro.palmeira@gmail.com>
  13 +Alessandro Palmeira + Daniel Alves <alessandro.palmeira@gmail.com>
  14 +Alessandro Palmeira + Daniel Alves + Diego Araújo <diegoamc90@gmail.com>
  15 +Alessandro Palmeira + Daniel Alves + Diego Araújo + Guilherme Rojas <danpaulalves@gmail.com>
  16 +Alessandro Palmeira + Diego Araujo <alessandro.palmeira@gmail.com>
  17 +Alessandro Palmeira + Diego Araújo <alessandro.palmeira@gmail.com>
  18 +Alessandro Palmeira + Diego Araujo + Daniela Feitosa <alessandro.palmeira@gmail.com>
  19 +Alessandro Palmeira + Diego Araujo <diegoamc90@gmail.com>
  20 +Alessandro Palmeira + Diego Araújo <diegoamc90@gmail.com>
  21 +Alessandro Palmeira + Diego Araujo + Eduardo Morais <alessandro.palmeira@gmail.com>
  22 +Alessandro Palmeira + Diego Araújo + João M. M. da Silva <alessandro.palmeira@gmail.com>
  23 +Alessandro Palmeira + Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com>
  24 +Alessandro Palmeira + Diego Araujo + João M. M. da Silva + Paulo Meirelles <alessandro.palmeira@gmail.com>
  25 +Alessandro Palmeira + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
  26 +Alessandro Palmeira + Diego Araújo + Pedro Leal + João M. M. da Silva <diegoamc90@gmail.com>
  27 +Alessandro Palmeira + Diego Araujo + Rafael Manzo <alessandro.palmeira@gmail.com>
  28 +Alessandro Palmeira + Eduardo Morais <alessandro.palmeira@gmail.com>
  29 +Alessandro Palmeira + Guilherme Rojas <alessandro.palmeira@gmail.com>
  30 +Alessandro Palmeira + Jefferson Fernandes <alessandro.palmeira@gmail.com>
  31 +Alessandro Palmeira + João M. M. da Silva <alessandro.palmeira@gmail.com>
  32 +Alessandro Palmeira + Joao M. M. da Silva + Diego Araujo <alessandro.palmeira@gmail.com>
  33 +Alessandro Palmeira + João M. M. da Silva + Renan Teruo <alessandro.palmeira@gmail.com>
  34 +Alessandro Palmeira + João M. M. Silva <alessandro.palmeira@gmail.com>
  35 +Alessandro Palmeira + Paulo Meirelles <alessandro.palmeira@gmail.com>
  36 +Alessandro Palmeira + Paulo Meirelles + João M. M. da Silva <alessandro.palmeira@gmail.com>
  37 +Alessandro Palmeira + Rafael Manzo <alessandro.palmeira@gmail.com>
  38 +Antonio Terceiro + Carlos Morais <terceiro@colivre.coop.br>
  39 +Antonio Terceiro + Paulo Meirelles <terceiro@colivre.coop.br>
  40 +Antonio Terceiro <terceiro@colivre.coop.br>
  41 +Aurelio A. Heckert <aurelio@colivre.coop.br>
  42 +Braulio Bhavamitra <brauliobo@gmail.com>
  43 +Bráulio Bhavamitra <brauliobo@gmail.com>
  44 +Braulio Bhavamitra <braulio@eita.org.br>
  45 +Caio <caio.csalgado@gmail.com>
  46 +Caio + Diego + Pedro + João <caio.csalgado@gmail.com>
  47 +Caio Formiga <caio.formiga@gmail.com>
  48 +Caio, Pedro <caio.csalgado@gmail.com>
  49 +Caio Salgado + Alessandro Palmeira <caio.csalgado@gmail.com>
  50 +Caio Salgado <caio.csalgado@gmail.com>
  51 +Caio Salgado + Carlos Morais + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
  52 +Caio Salgado + Diego Araujo <caio.csalgado@gmail.com>
  53 +Caio Salgado + Diego Araújo <caio.csalgado@gmail.com>
  54 +Caio Salgado + Diego Araújo <diegoamc90@gmail.com>
  55 +Caio Salgado + Diego Araújo + Jefferson Fernandes <caio.csalgado@gmail.com>
  56 +Caio Salgado + Diego Araújo + João M. M. da Silva <caio.csalgado@gmail.com>
  57 +Caio Salgado + Diego Araújo + Pedro Leal <caio.csalgado@gmail.com>
  58 +Caio Salgado + Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
  59 +Caio Salgado + Diego Araújo + Rafael Manzo <diegoamc90@gmail.com>
  60 +Caio Salgado + Jefferson Fernandes <caio.csalgado@gmail.com>
  61 +Caio Salgado + Jefferson Fernandes <jeffs.fernandes@gmail.com>
  62 +Caio Salgado + Rafael Manzo <caio.csalgado@gmail.com>
  63 +Caio Salgado + Renan Teruo <caio.csalgado@gmail.com>
  64 +Caio Salgado + Renan Teruo <caio.salgado@gmail.com>
  65 +Caio Salgado + Renan Teruo + Jefferson Fernandes <jeffs.fernandes@gmail.com>
  66 +Caio Salgado + Renan Teruo <renanteruoc@gmail.com>
  67 +Caio SBA <caio@colivre.coop.br>
  68 +Carlos Morais <carlos88morais@gmail.com>
  69 +Carlos Morais + Diego Araújo <diegoamc90@gmail.com>
  70 +Carlos Morais + Eduardo Morais <carlos88morais@gmail.com>
  71 +Carlos Morais + Paulo Meirelles <carlos88morais@gmail.com>
  72 +Carlos Morais + Pedro Leal <carlos88morais@gmail.com>
  73 +Daniel Alves + Diego Araújo <danpaulalves@gmail.com>
  74 +Daniel Alves + Diego Araújo <diegoamc90@gmail.com>
  75 +Daniel Alves + Diego Araújo + Guilherme Rojas <danpaulalves@gmail.com>
  76 +Daniel Alves + Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com>
  77 +Daniel Alves + Diego Araújo + Guilherme Rojas <guilhermehrojas@gmail.com>
  78 +Daniel Alves + Guilherme Rojas <danpaulalves@gmail.com>
  79 +Daniel Alves + Rafael Manzo <rr.manzo@gmail.com>
  80 +Daniela Soares Feitosa <danielafeitosa@colivre.coop.br>
  81 +Daniel Cunha <daniel@colivre.coop.br>
  82 +diegoamc <diegoamc90@gmail.com>
  83 +Diego Araújo + Alessandro Palmeira <diegoamc90@gmail.com>
  84 +Diego Araújo + Alessandro Palmeira + João M. M. da Silva <diegoamc90@gmail.com>
  85 +Diego Araújo + Alessandro Palmeira + Rafael Manzo <rr.manzo@gmail.com>
  86 +Diego Araujo + Caio Salgado <diegoamc90@gmail.com>
  87 +Diego Araújo + Daniel Alves + Rafael Manzo <rr.manzo@gmail.com>
  88 +Diego Araújo <diegoamc90@gmail.com>
  89 +Diego Araújo + Eduardo Morais + Paulo Meirelles <diegoamc90@gmail.com>
  90 +Diego Araújo + Guilherme Rojas <diegoamc90@gmail.com>
  91 +Diego Araújo + Jefferson Fernandes <diegoamc90@gmail.com>
  92 +Diego Araujo + Jefferson Fernandes <jeffs.fernandes@gmail.com>
  93 +Diego Araújo + João Machini <diegoamc90@gmail.com>
  94 +Diego Araújo + João Machini <digoamc90@gmail.com>
  95 +Diego Araújo + João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>
  96 +Diego Araújo + João M. M. da Silva <diegoamc90@gmail.com>
  97 +Diego Araújo + João M. M. da Silva + João Machini <diegoamc90@gmail.com>
  98 +Diego Araújo + João M. M. da Silva + Pedro Leal <diegoamc90@gmail.com>
  99 +Diego Araújo + Paulo Meirelles <diegoamc90@gmail.com>
  100 +Diego Araújo + Pedro Leal <diegoamc90@gmail.com>
  101 +Diego Araujo + Rafael Manzo <diegoamc90@gmail.com>
  102 +Diego Araújo + Rafael Manzo <diegoamc90@gmail.com>
  103 +Diego Araújo + Renan Teruo + Alessandro Palmeira <diegoamc90@gmail.com>
  104 +Diego Araújo + Renan Teruo <diegoamc90@gmail.com>
  105 +Diego Araujo + Rodrigo Souto + Rafael Manzo <rr.manzo@gmail.com>
  106 +Diego + Jefferson <diegoamc90@gmail.com>
  107 +Diego Martinez <diegoamc90@gmail.com>
  108 +Diego Martinez <diego@diego-K55A.(none)>
  109 +Diego + Renan <renanteruoc@gmail.com>
  110 +Fernanda Lopes <nanda.listas+psl@gmail.com>
  111 +Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br>
  112 +Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)>
  113 +Grazieno Pellegrino <grazieno@gmail.com>
  114 +Isaac Canan <isaac@intelletto.com.br>
  115 +Italo Valcy <italo@dcc.ufba.br>
  116 +Jefferson Fernandes + Diego Araujo + Rafael Manzo <jeffs.fernandes@gmail.com>
  117 +Jefferson Fernandes + Joao M. M. da Silva <jeffs.fernandes@gmail.com>
  118 +Jefferson Fernandes + Joao M. M. Silva <jeffs.fernandes@gmail.com>
  119 +João da Silva <jaodsilv@linux.ime.usp.br>
  120 +João Marco Maciel da Silva + Rafael Manzo + Renan Teruo <jaodsilv@linux.ime.usp.br>
  121 +João M. M. da Silva + Alessandro Palmeira + Diego Araújo + Caio Salgado <jaodsilv@linux.ime.usp.br>
  122 +João M. M. da Silva + Alessandro Palmeira + Diego Araújo <jaodsilv@linux.ime.usp.br>
  123 +Joao M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>
  124 +João M. M. da Silva + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>
  125 +João M. M. da Silva + Alessandro Palmeira + João Machini <jaodsilv@linux.ime.usp.br>
  126 +João M. M. da Silva + Caio Salgado + Alessandro Palmeira <jaodsilv@linux.ime.usp.br>
  127 +João M. M. da Silva + Caio Salgado <jaodsilv@linux.ime.usp.br>
  128 +João M. M. da Silva + Carlos Morais <jaodsilv@linux.ime.usp.br>
  129 +João M. M. da Silva + Diego Araújo <diegoamc90@gmail.com>
  130 +João M. M. da Silva + Diego Araújo <jaodsilv@linux.ime.usp.br>
  131 +João M. M. da Silva + Diego Araújo + Pedro Leal <jaodsilv@linux.ime.usp.br>
  132 +João M. M. da Silva <jaodsilv@linux.ime.usp.br>
  133 +Joao M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>
  134 +João M. M. da Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>
  135 +João M. M. da Silva + João M. Miranda <jaodsilv@linux.ime.usp.br>
  136 +João M. M. da Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br>
  137 +João M. M. da Silva + Pedro Leal <jaodsilv@linux.ime.usp.br>
  138 +João M. M. da Silva + Rafael Manzo + Diego Araújo <jaodsilv@linux.ime.usp.br>
  139 +João M. M. da Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br>
  140 +João M. M. da Silva + Renan Teruo <jaodsilv@linux.ime.usp.br>
  141 +João M. M. Silva + Caio Salgado <jaodsilv@linux.ime.usp.br>
  142 +João M. M. Silva + Diego Araújo <jaodsilv@linux.ime.usp.br>
  143 +Joao M. M. Silva + Jefferson Fernandes <jaodsilv@linux.ime.usp.br>
  144 +João M. M. Silva + Paulo Meirelles <jaodsilv@linux.ime.usp.br>
  145 +João M. M. Silva + Rafael Manzo <jaodsilv@linux.ime.usp.br>
  146 +João M. M. Silva + Renan Teruo <jaodsilv@linux.ime.usp.br>
  147 +Joenio Costa <joenio@colivre.coop.br>
  148 +Josef Spillner <josef.spillner@tu-dresden.de>
  149 +Junior Silva <juniorsilva1001@gmail.com>
  150 +Junior Silva <juniorsilva7@juniorsilva-Aspire-5750Z.(none)>
  151 +Keilla Menezes <keilla@colivre.coop.br>
  152 +Larissa Reis <larissa@colivre.coop.br>
  153 +Larissa Reis <reiss.larissa@gmail.com>
  154 +Leandro Nunes dos Santos <leandronunes@gmail.com>
  155 +Leandro Nunes dos Santos <leandro.santos@serpro.gov.br>
  156 +LinguÁgil 2010 <linguagil.bahia@gmail.com>
  157 +Lucas Melo <lucas@colivre.coop.br>
  158 +Lucas Melo <lucaspradomelo@gmail.com>
  159 +Luis David Aguilar Carlos <ludwig9003@gmail.com>
  160 +Martín Olivera <molivera@solar.org.ar>
  161 +Moises Machado <moises@colivre.coop.br>
  162 +Naíla Alves <naila@colivre.coop.br>
  163 +Nanda Lopes <nanda.listas+psl@gmail.com>
  164 +Paulo Meirelles + Alessandro Palmeira + João M. M. da Silva <paulo@softwarelivre.org>
  165 +Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org>
  166 +Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org>
  167 +Paulo Meirelles + Diego Araújo <paulo@softwarelivre.org>
  168 +Paulo Meirelles + João M. M. da Silva <paulo@softwarelivre.org>
  169 +Paulo Meirelles <paulo@softwarelivre.org>
  170 +Paulo Meirelles + Rafael Manzo <paulo@softwarelivre.org>
  171 +Rafael Gomes <rafaelgomes@techfree.com.br>
  172 +Rafael Manzo + Alessandro Palmeira <rr.manzo@gmail.com>
  173 +Rafael Manzo + Daniel Alves <danpaulalves@gmail.com>
  174 +Rafael Manzo + Diego Araújo <rr.manzo@gmail.com>
  175 +Rafael Manzo + João M. M. Silva <rr.manzo@gmail.com>
  176 +Rafael Manzo + Paulo Meirelles <rr.manzo@gmail.com>
  177 +Rafael Martins <rmmartins@gmail.com>
  178 +Rafael Reggiani Manzo + Caio Salgado + Jefferson Fernandes <rr.manzo@gmail.com>
  179 +Rafael Reggiani Manzo + Diego Araujo <diegoamc90@gmail.com>
  180 +Rafael Reggiani Manzo + Diego Araujo <rr.manzo@gmail.com>
  181 +Rafael Reggiani Manzo + Diego Araújo <rr.manzo@gmail.com>
  182 +Rafael Reggiani Manzo + João M. M. da Silva <rr.manzo@gmail.com>
  183 +Rafael Reggiani Manzo <rr.manzo@gmail.com>
  184 +Raphaël Rousseau <raph@r4f.org>
  185 +Raquel Lira <raquel.lira@gmail.com>
  186 +Renan Teruo + Caio Salgado <renanteruoc@gmail.com>
  187 +Renan Teruoc + Diego Araujo <renanteruoc@gmail.com>
  188 +Renan Teruo + Diego Araujo <renanteruoc@gmail.com>
  189 +Renan Teruo + Diego Araújo <renanteruoc@gmail.com>
  190 +Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com>
  191 +Renan Teruo + Rafael Manzo <renanteruoc@gmail.com>
  192 +Rodrigo Souto <diguliu@gmail.com>
  193 +Rodrigo Souto <rodrigo@colivre.coop.br>
  194 +Ronny Kursawe <kursawe.ronny@googlemail.com>
  195 +root <root@debian.sdr.serpro>
  196 +Samuel R. C. Vale <srcvale@holoscopio.com>
  197 +Valessio Brito <valessio@gmail.com>
  198 +vfcosta <vfcosta@gmail.com>
  199 +Victor Costa <vfcosta@gmail.com>
  200 +Vinicius Cubas Brand <viniciuscb@gmail.com>
  201 +Visita <visita@debian.(none)>
  202 +Yann Lugrin <yann.lugrin@liquid-concept.ch>
  203 +
  204 +Ideas, specifications and incentive
  205 +===================================
  206 +Daniel Tygel <dtygel@fbes.org.br>
  207 +Guilherme Rocha <guilherme@gf7.com.br>
  208 +Raphael Rousseau <raph@r4f.org>
  209 +Théo Bondolfi <move@cooperation.net>
  210 +Vicente Aguiar <vicenteaguiar@colivre.coop.br>
  211 +
  212 +Arts
  213 +===================================
  214 +Nara Oliveira <narananet@gmail.com>
@@ -1,63 +0,0 @@ @@ -1,63 +0,0 @@
1 -= Noosfero instructions for developers  
2 -  
3 -== A work about your the development platform  
4 -  
5 -These instructions are tested and known to work on Debian stable, which is the  
6 -system that the Noosfero core developers use to work on Noosfero.  
7 -  
8 -If you want to use another OS, read "Instructions for other systems" below.  
9 -  
10 -== Instructions for Debian stable  
11 -  
12 -Download the source code:  
13 -  
14 - $ git clone git://gitorious.org/noosfero/noosfero.git  
15 - $ cd noosfero  
16 -  
17 -Run the quick start script:  
18 -  
19 - $ ./script/quick-start  
20 -  
21 -Now you can execute the development server with:  
22 -  
23 - $ ./script/development  
24 -  
25 -You will be able to access Noosfero at http://localhost:3000/  
26 -  
27 -If you want to use a different port than 3000, pass `-p <PORT>` to  
28 -./script/development  
29 -  
30 -== Instructions for other systems  
31 -  
32 -On other OS, you have 2 options:  
33 -  
34 -1) using a chroot or a VM with Debian stable (easier)  
35 -  
36 -Use a chroot (http://wiki.debian.org/Schroot) or a Virtual Machine (e.g. with  
37 -VirtualBox) with a Debian stable system and follow the instructions above for  
38 -Debian stable.  
39 -  
40 -2) Installing dependencies on other OS (harder)  
41 -  
42 -If you want to setup a development environment in another OS, you can create a  
43 -file under script/install-dependencies/, called <OS>-<CODENAME>.sh, which  
44 -installed the dependencies for your system. With this script in place,  
45 -./script/quick-start will call it at the point of installing the required  
46 -packages for Noosfero development.  
47 -  
48 -You can check script/install-dependencies/debian-squeeze.sh to have an idea of  
49 -what kind of stuff that script has to do.  
50 -  
51 -If you write such script for your own OS, *please* share it with us at the  
52 -development mailing list so that we can include it in the official repository.  
53 -This way other people using the same OS will have to put less effort to develop  
54 -Noosfero.  
55 -  
56 -== Submitting your changes back  
57 -  
58 -For now please read:  
59 -  
60 -- Coding conventions  
61 - http://noosfero.org/Development/CodingConventions  
62 -- Patch guidelines  
63 - http://noosfero.org/Development/PatchGuidelines  
HACKING.md 0 → 100644
@@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
  1 +Noosfero instructions for developers
  2 +====================================
  3 +
  4 +A work about your the development platform
  5 +------------------------------------------
  6 +
  7 +These instructions are tested and known to work on Debian stable, which is the system that the Noosfero core developers use to work on Noosfero.
  8 +
  9 +If you want to use another OS, read "Instructions for other systems" below.
  10 +
  11 +Instructions for Debian stable
  12 +------------------------------
  13 +
  14 +Download the source code:
  15 +
  16 + $ git clone https://gitlab.com/noosfero/noosfero.git
  17 + $ cd noosfero
  18 +
  19 +Run the quick start script:
  20 +
  21 + $ ./script/quick-start
  22 +
  23 +Now you can execute the development server with:
  24 +
  25 + $ ./script/development
  26 +
  27 +You will be able to access Noosfero at http://localhost:3000/
  28 +
  29 +If you want to use a different port than 3000, pass `-p <PORT>` to `./script/development`
  30 +
  31 +Instructions for other systems
  32 +------------------------------
  33 +
  34 +On other OS, you have 2 options:
  35 +
  36 +### 1) using a chroot or a VM with Debian stable (easier)
  37 +
  38 +Use a chroot (http://wiki.debian.org/Schroot) or a Virtual Machine (e.g. with VirtualBox) with a Debian stable system and follow the instructions above for Debian stable.
  39 +
  40 +### 2) Installing dependencies on other OS (harder)
  41 +
  42 +If you want to setup a development environment in another OS, you can create a file under `./script/install-dependencies/`, called `<OS>-<CODENAME>.sh`, which installed the dependencies for your system. With this script in place, `./script/quick-start` will call it at the point of installing the required packages for Noosfero development.
  43 +
  44 +You can check `./script/install-dependencies/debian-squeeze.sh` to have an idea of what kind of stuff that script has to do.
  45 +
  46 +If you write such script for your own OS, *please* share it with us at the development mailing list so that we can include it in the official repository. This way other people using the same OS will have to put less effort to develop Noosfero.
  47 +
  48 +Submitting your changes back
  49 +----------------------------
  50 +
  51 +For now please read:
  52 +
  53 +- Coding conventions
  54 + http://noosfero.org/Development/CodingConventions
  55 +- Patch guidelines
  56 + http://noosfero.org/Development/PatchGuidelines
@@ -1,417 +0,0 @@ @@ -1,417 +0,0 @@
1 -= Noosfero installation instructions from source for production environments  
2 -  
3 -The instructions below can be used for setting up a Noosfero production  
4 -environment from the Noosfero sources.  
5 -  
6 -Before you start installing Noosfero manually, see the information about the  
7 -Noosfero Debian package at http://noosfero.org/Development/DebianPackage. Using  
8 -the Debian packages on a Debian stable system is the recommended method for  
9 -installing production environments.  
10 -  
11 -If you want to setup a development environment instead of a production one,  
12 -stop reading this file right now and read the file HACKING instead.  
13 -  
14 -For a complete installation guide, please see the following web page:  
15 -http://noosfero.org/Development/HowToInstall  
16 -  
17 -If you have problems with the setup, please feel free to ask questions in the  
18 -development mailing list.  
19 -  
20 -== Requirements  
21 -  
22 -DISCLAIMER: this installation procedure is tested with Debian stable, which is  
23 -currently the only recommended operating system for production usage. It is  
24 -possible that you can install it on other systems, and if you do so, please  
25 -report it on one of the Noosfero mailing lists, and please send a patch  
26 -updating these instructions.  
27 -  
28 -Noosfero is written in Ruby with the "Rails  
29 -framework":http://www.rubyonrails.org, so the process of setting it up is  
30 -pretty similar to other Rails applications.  
31 -  
32 -You need to install some packages Noosfero depends on. On Debian GNU/Linux or  
33 -Debian-based systems, all of these packages are available through the Debian  
34 -archive. You can install them with the following command:  
35 -  
36 - # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby libdaemons-ruby thin tango-icon-theme libhpricot-ruby  
37 -  
38 -On other systems, they may or may not be available through your regular package  
39 -management system. Below are the links to their homepages.  
40 -  
41 -* Ruby: http://www.ruby-lang.org/  
42 -* Rake: http://rake.rubyforge.org/  
43 -* po4a: http://po4a.alioth.debian.org/  
44 -* Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby  
45 -* rcov: http://eigenclass.org/hiki/rcov  
46 -* RMagick: http://rmagick.rubyforge.org/  
47 -* RedCloth: http://redcloth.org/  
48 -* will_paginate: http://github.com/mislav/will_paginate/wikis  
49 -* iso-codes: http://pkg-isocodes.alioth.debian.org/  
50 -* feedparser: http://packages.debian.org/sid/libfeedparser-ruby  
51 -* Daemons - http://daemons.rubyforge.org/  
52 -* Thin: http://code.macournoyer.com/thin/  
53 -* tango-icon-theme: http://tango.freedesktop.org/Tango_Icon_Library  
54 -* Hpricot: http://hpricot.com/  
55 -  
56 -If you manage to install Noosfero successfully on other systems than Debian,  
57 -please feel free to contact the Noosfero development mailing with the  
58 -instructions for doing so, and we'll include it here.  
59 -  
60 -As root user  
61 -============  
62 -  
63 -Install memcached. On Debian:  
64 -  
65 -# apt-get install memcached  
66 -  
67 -Study whether you need to raise the ammount of memory it uses for caching,  
68 -depending on the demand you expect for your site. If you are going to run a  
69 -high-traffic site, you will want to raise the ammount of memory reserved for  
70 -caching.  
71 -  
72 -It is recommended that you run noosfero with its own user account. To create  
73 -such an account, please do the following:  
74 -  
75 -# adduser --system --group noosfero --shell /bin/sh --home /var/lib/noosfero  
76 -  
77 -(note that you can change the $HOME directory of the user if you wish, here we  
78 -are using /var/lib/noosfero)  
79 -  
80 -The --system option will tell adduser to create a system user, i.e. this user  
81 -will not have a password and cannot login to the system directly. To become  
82 -this user, you have to use sudo:  
83 -  
84 -# sudo -u noosfero -i  
85 -  
86 -or  
87 -  
88 -# su - noosfero  
89 -  
90 -As noosfero user  
91 -================  
92 -  
93 -downloading from git  
94 ---------------------  
95 -  
96 -Here we are cloning the noosfero repository from git. Note: you will need to  
97 -install git before.  
98 -  
99 -$ git clone git://gitorious.org/noosfero/noosfero.git current  
100 -$ cd current  
101 -$ git checkout -b stable origin/stable  
102 -  
103 -downloading tarball  
104 --------------------  
105 -  
106 -Note: replace 0.39.0 below from the latest stable version.  
107 -  
108 -$ wget http://noosfero.org/pub/Development/NoosferoVersion00x39x00/noosfero-0.39.0.tar.gz  
109 -$ tar -zxvf noosfero-0.39.0.tar.gz  
110 -$ ln -s noosfero-0.39.0 current  
111 -$ cd current  
112 -  
113 -Create the thin configuration file:  
114 -  
115 -$ thin -C config/thin.yml -e production config  
116 -  
117 -Edit config/thin.yml to suit your needs. Make sure your apache  
118 -configuration matches the thin cluster configuration, specially in respect  
119 -to the ports and numbers of thin instances.  
120 -  
121 -Note: currently Noosfero only supports Rails 2.3.5, which is the version in  
122 -Debian Squeeze. If you have a Rails version newer than that, Noosfero will  
123 -probably not work. You can install Rails 2.3.5 into your Noosfero installation  
124 -with the following procedure:  
125 -  
126 -$ cd /var/lib/noosfero/current/vendor  
127 -$ wget http://ftp.de.debian.org/debian/pool/main/r/rails/rails_2.3.5.orig.tar.gz  
128 -$ tar xzf rails_2.3.5.orig.tar.gz  
129 -$ ln -s rails-2.3.5 rails  
130 -  
131 -As root user  
132 -============  
133 -  
134 -Setup Noosfero log and tmp directories:  
135 -  
136 -# cd /var/lib/noosfero/current  
137 -# ./etc/init.d/noosfero setup  
138 -  
139 -Now it's time to setup the database. In this example we are using PostgreSQL,  
140 -so if you are planning to use a different database this steps won't apply.  
141 -  
142 -# apt-get install postgresql libpgsql-ruby  
143 -# su postgres -c 'createuser noosfero -S -d -R'  
144 -  
145 -By default Rails will try to connect on postgresql through 5432 port,  
146 -you can check it on /etc/postgresql/8.4/main/postgresql.conf file.  
147 -  
148 -Restart postgresql:  
149 -  
150 -# invoke-rc.d postgresql restart  
151 -  
152 -Noosfero needs a functional e-mail setup to work: the local mail system should  
153 -be able to deliver e-mail to the internet, either directly or through an  
154 -external SMTP server. Please check the documentation at the INSTALL.email file.  
155 -  
156 -As noosfero user  
157 -================  
158 -  
159 -Now create the databases:  
160 -  
161 -$ cd /var/lib/noosfero/current  
162 -$ createdb noosfero_production  
163 -$ createdb noosfero_development  
164 -$ createdb noosfero_test  
165 -  
166 -The development and test databases are actually optional. If you are creating a  
167 -stricly production server, you will probably not need them.  
168 -  
169 -Now we want to configure Noosfero for accessing the database we just created.  
170 -To do that, you can 1) copy config/database.yml.pgsql to config/database.yml,  
171 -or create config/database.yml from scratch with the following content:  
172 -  
173 - production:  
174 - adapter: postgresql  
175 - encoding: unicode  
176 - database: noosfero_production  
177 - username: noosfero  
178 -  
179 -Now, to test the database access, you can fire the Rails database console:  
180 -  
181 -$ ./script/dbconsole production  
182 -  
183 -If it connects to your database, then everything is fine. If you got an error  
184 -message, then you have to check your database configuration.  
185 -  
186 -Create the database structure:  
187 -  
188 -$ RAILS_ENV=production rake db:schema:load  
189 -  
190 -Compile the translations:  
191 -  
192 -$ RAILS_ENV=production rake noosfero:translations:compile  
193 -  
194 -Now we must create some initial data. To create your default environment  
195 -(the first one), run the command below:  
196 -  
197 -$ RAILS_ENV=production ./script/runner 'Environment.create!(:name => "My environment", :is_default => true)'  
198 -  
199 -(of course, replace "My environment" with your environment's name!)  
200 -  
201 -And now you have to add the domain name you will be using for your noosfero  
202 -site to the list of domains of that default environment you just created:  
203 -  
204 -$ RAILS_ENV=production ./script/runner "Environment.default.domains << Domain.new(:name => 'your.domain.com')"  
205 -  
206 -(replace "your.domain.com" with your actual domain name)  
207 -  
208 -Add at least one user as admin of environment:  
209 -  
210 -$ RAILS_ENV=production ./script/runner "User.create(:login => 'adminuser', :email => 'admin@example.com', :password => 'admin', :password_confirmation => 'admin', :environment => Environment.default, :activated_at => Time.new)"  
211 -  
212 -(replace "adminuser", "admin@example.com", "admin" with the login, email  
213 -and password of your environment administrator)  
214 -  
215 -To start the Noosfero application servers:  
216 -  
217 -$ ./script/production start  
218 -  
219 -At this point you have a functional Noosfero installation running, the only  
220 -thing left is to configure your webserver as a reverse proxy to pass requests  
221 -to them.  
222 -  
223 -  
224 -==================  
225 -Apache instalation  
226 -==================  
227 -  
228 -# apt-get install apache2  
229 -  
230 -Apache configuration  
231 ---------------------  
232 -  
233 -First you have to enable the following some apache modules:  
234 -  
235 - deflate  
236 - expires  
237 - proxy  
238 - proxy_balancer  
239 - proxy_http  
240 - rewrite  
241 -  
242 -On Debian GNU/Linux system, these modules can be enabled with the following  
243 -command line, as root:  
244 -  
245 -# a2enmod deflate expires proxy proxy_balancer proxy_http rewrite  
246 -  
247 -In other systems the way by which you enable apache modules may be different.  
248 -  
249 -Now with the Apache configuration. You can use the template below, replacing  
250 -/var/lib/noosfero/current with the directory in which your noosfero  
251 -installation is, your.domain.com with the domain name of your noosfero site.  
252 -We are assuming that you are running two thin instances on ports 3000 and  
253 -3001. If your setup is different you'll need to adjust <Proxy> section. If you  
254 -don't understand something in the configuration, please refer to the apache  
255 -documentation.  
256 -  
257 -Add a file called "mysite" (or whatever name you want to give to your noosfero  
258 -site) to /etc/apache2/sites-available with the following content, and customize  
259 -as needed (as usual, make sure you replace "your.domain.com" with you actual  
260 -domain name, and "/var/lib/noosfero/current" with the directory where Noosfero  
261 -is installed):  
262 -  
263 - <VirtualHost *:80>  
264 - ServerName your.domain.com  
265 -  
266 - DocumentRoot "/var/lib/noosfero/current/public"  
267 - <Directory "/var/lib/noosfero/current/public">  
268 - Options FollowSymLinks  
269 - AllowOverride None  
270 - Order Allow,Deny  
271 - Allow from all  
272 - </Directory>  
273 -  
274 - RewriteEngine On  
275 -  
276 - # Rewrite index to check for static index.html  
277 - RewriteRule ^/$ /index.html [QSA]  
278 -  
279 - # Rewrite to check for Rails cached page  
280 - RewriteRule ^([^.]+)$ $1.html [QSA]  
281 -  
282 - RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f  
283 - RewriteRule ^.*$ balancer://noosfero%{REQUEST_URI} [P,QSA,L]  
284 -  
285 - ErrorDocument 503 /503.html  
286 -  
287 - ErrorLog /var/log/apache2/noosfero.log  
288 - LogLevel warn  
289 - CustomLog /var/log/apache2/noosfero.access.log combined  
290 -  
291 - Include /var/lib/noosfero/current/etc/noosfero/apache/cache.conf  
292 -  
293 - </VirtualHost>  
294 -  
295 - <Proxy balancer://noosfero>  
296 - BalancerMember http://127.0.0.1:3000  
297 - BalancerMember http://127.0.0.1:3001  
298 - Order Allow,Deny  
299 - Allow from All  
300 - </Proxy>  
301 -  
302 -The cache.conf file included in the end of the <VirtualHost> section is  
303 -important, since it will tell apache to pass expiration and cache headers to  
304 -clients so that the site feels faster for users. Do we need to say that using  
305 -that configuration is strongly recommended?  
306 -  
307 -Enable that site with (as root, replace "mysite" with the actual name you gave  
308 -to your site configuration):  
309 -  
310 -# a2ensite mysite  
311 -  
312 -Now restart your apache server (as root):  
313 -  
314 -# invoke-rc.d apache2 restart  
315 -  
316 -  
317 -Enabling exception notifications  
318 -================================  
319 -  
320 -This is an optional step. You will need it only if you want to receive e-mail  
321 -notifications when some exception occurs on Noosfero.  
322 -  
323 -First, install this version of the gem.  
324 -Others versions may not be compatible with Noosfero:  
325 -  
326 -# gem install exception_notification -v 1.0.20090728  
327 -  
328 -You can configure the e-mails that will receive the notifications.  
329 -Change the file config/noosfero.yml as the following example, replacing the  
330 -e-mails by real ones:  
331 -  
332 - production:  
333 - exception_recipients: [admin@example.com, you@example.com]  
334 -  
335 -  
336 -============  
337 -Maintainance  
338 -============  
339 -  
340 -To ease the maintainance, install a symbolic link for the Noosfero startup  
341 -script in your server and add it to the system initialization and shutdown  
342 -sequences (as root):  
343 -  
344 -# ln -s /var/lib/noosfero/current/etc/init.d/noosfero /etc/init.d/noosfero  
345 -# update-rc.d noosfero defaults  
346 - Adding system startup for /etc/init.d/noosfero ...  
347 - /etc/rc0.d/K20noosfero -> ../init.d/noosfero  
348 - /etc/rc1.d/K20noosfero -> ../init.d/noosfero  
349 - /etc/rc6.d/K20noosfero -> ../init.d/noosfero  
350 - /etc/rc2.d/S20noosfero -> ../init.d/noosfero  
351 - /etc/rc3.d/S20noosfero -> ../init.d/noosfero  
352 - /etc/rc4.d/S20noosfero -> ../init.d/noosfero  
353 - /etc/rc5.d/S20noosfero -> ../init.d/noosfero  
354 -  
355 -Now to start Noosfero, you do as root:  
356 -  
357 -# invoke-rc.d noosfero start  
358 -  
359 -To stop Noosfero:  
360 -  
361 -# invoke-rc.d noosfero start  
362 -  
363 -To restart Noosfero:  
364 -  
365 -# invoke-rc.d noosfero restart  
366 -  
367 -Noosfero will be automatically started during system boot, and automatically  
368 -stopped if the system shuts down for some reason (or during the shutdown part  
369 -of a reboot).  
370 -  
371 -=============  
372 -Rotating logs  
373 -=============  
374 -  
375 -Noosfero provides an example logrotate configuation to rotate its logs. To use  
376 -it, create a symbolic link in /etc/logrotate.d/:  
377 -  
378 -# cd /etc/logrotate.d/  
379 -# ln -s /var/lib/noosfero/current/etc/logrotate.d/noosfero  
380 -  
381 -Note that the provided file assumes Noosfero logging is being done in  
382 -/var/log/noosfero (which is the case if you followed the instructions above  
383 -correctly). If the logs are stored elsewhere, it's recommended that you copy  
384 -the file over to /etc/logrotate.d/ and modify it to point to your local log  
385 -directly.  
386 -  
387 -=========  
388 -Upgrading  
389 -=========  
390 -  
391 -If you followed the steps in this document and installed Noosfero from the git  
392 -repository, then upgrading is easy. First, you need to allow the noosfero user  
393 -to restart the memcached server with sudo, by adding the following line in  
394 -/etc/sudoers:  
395 -  
396 -noosfero ALL=NOPASSWD: /etc/init.d/memcached  
397 -  
398 -Then, to perform an upgrade, do the following as the noosfero user:  
399 -  
400 -$ cd /var/lib/noosfero/current  
401 -$ ./script/git-upgrade  
402 -  
403 -The git-upgrade script will take care of everything for you. It will first stop  
404 -the service, then fetch the current source code, upgrade database, compile  
405 -translations, and then start the service again.  
406 -  
407 -Note 1: make sure your local git repository is following the "stable" branch,  
408 -just like the instructions above. The "master" branch is not recommended for  
409 -use in production environments.  
410 -  
411 -Note 2: always read the release notes before upgrading. Sometimes there will be  
412 -steps that must be performed manually. If that is the case, you can invoke the  
413 -git-upgrade script with the special parameter "--shell" that will give you a  
414 -shell after the upgrade, which you can use to perform any manual steps  
415 -required:  
416 -  
417 -$ ./script/git-upgrade --shell  
INSTALL.awstats
@@ -1,78 +0,0 @@ @@ -1,78 +0,0 @@
1 -= AWStats setup for Noosfero  
2 -  
3 -AWStats is a free powerful and featureful tool that generates advanced web,  
4 -streaming, ftp or mail server statistics, graphically.  
5 -  
6 -See http://awstats.sourceforge.net/  
7 -  
8 -This guide supposes that the Noosfero server is running GNU/Linux Debian Squeeze.  
9 -  
10 -1. Install AWStats  
11 -  
12 -# apt-get install awstats libgeo-ip-perl geoip-database  
13 -  
14 -2. Basic setup  
15 -  
16 -Create AWStats config file:  
17 -  
18 - * /etc/awstats/awstats.<domain>.conf  
19 -  
20 -Include "/etc/awstats/awstats.conf"  
21 -Include "/etc/noosfero/awstats-noosfero.conf"  
22 -SiteDomain="<domain>"  
23 -HostAliases="<domain-aliases>"  
24 -  
25 -<domain> should be the domain used in your Noosfero server (eg.:  
26 -softwarelivre.org) and the <domain-aliases> should be a list with all aliases  
27 -that you configured in apache (eg.: www.softwarelivre.org  
28 -www2.softwarelivre.org etc).  
29 -  
30 -This setup is considering that the Noosfero server is running varnish (see  
31 -INSTALL.varnish) and varnishncsa-vhost [1].  
32 -  
33 -[1] http://gitorious.org/varnisnncsa-vhost  
34 -  
35 -3. Running AWStats for the first time  
36 -  
37 -Run awstats by hand via command line:  
38 -  
39 -# /usr/lib/cgi-bin/awstats.pl -config=<domain>  
40 -  
41 -You should see something as below as output of this command:  
42 -  
43 -# /usr/lib/cgi-bin/awstats.pl -config=softwarelivre.org  
44 -Create/Update database for config "/etc/awstats/awstats.softwarelivre.org.conf" by AWStats version 6.7 (build 1.892)  
45 -From data in log file "/var/log/varnish/varnishncsa-vhost.log"...  
46 -Phase 1 : First bypass old records, searching new record...  
47 -Searching new records from beginning of log file...  
48 -Phase 2 : Now process new records (Flush history on disk after 20000 hosts)...  
49 -Jumped lines in file: 0  
50 -Parsed lines in file: 452  
51 - Found 0 dropped records,  
52 - Found 0 corrupted records,  
53 - Found 0 old records,  
54 - Found 452 new qualified records.  
55 -  
56 -4. Setup frontend  
57 -  
58 -You should create a new subdomain to have access to the AWStats, usually  
59 -something like tools.<domain> (eg.: tools.softwarelivre.org). Don't include  
60 -this subdomain in HostAliases in the AWStats neither in SiteAlias in the  
61 -Apache.  
62 -  
63 -# cp /usr/share/doc/awstats/examples/apache.conf /etc/apache2/conf.d/awstats.conf  
64 -# invoke-rc.d apache2 restart  
65 -  
66 -ps.: Don't forget to change the port /etc/apache/sites-enabled/000-default to  
67 -8080.  
68 -  
69 -Try: http://tools.<domain>/cgi-bin/awstats.pl?config=<domain>  
70 -(eg.: http://tools.softwarelivre.org/cgi-bin/awstats.pl?config=softwarelivre.org).  
71 -  
72 -5. Schedule AWStats in crontab  
73 -  
74 - * /etc/cron.d/awstats  
75 -  
76 -0,10,20,30,40,50 * * * * www-data [ -x /usr/lib/cgi-bin/awstats.pl -a -f /etc/awstats/awstats.<domain>.conf -a -r /var/log/apache/access.log ] && /usr/lib/cgi-bin/awstats.pl -config=<domain> -update >/dev/null  
77 -  
78 -Done, check the AWStats frontend after one or two days to see if everything is working properly.  
INSTALL.awstats.md 0 → 100644
@@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
  1 +AWStats setup for Noosfero
  2 +==========================
  3 +
  4 +AWStats is a free powerful and featureful tool that generates advanced web, streaming, ftp or mail server statistics, graphically.
  5 +
  6 +See http://awstats.sourceforge.net
  7 +
  8 +This guide supposes that the Noosfero server is running GNU/Linux Debian Squeeze.
  9 +
  10 +### 1. Install AWStats
  11 +
  12 + # apt-get install awstats libgeo-ip-perl geoip-database
  13 +
  14 +### 2. Basic setup
  15 +
  16 +Create AWStats config file:
  17 +`/etc/awstats/awstats.<domain>.conf`
  18 +
  19 + Include "/etc/awstats/awstats.conf"
  20 + Include "/etc/noosfero/awstats-noosfero.conf"
  21 + SiteDomain="<domain>"
  22 + HostAliases="<domain-aliases>"
  23 +
  24 +`<domain>` should be the domain used in your Noosfero server (eg.: `softwarelivre.org`) and the `<domain-aliases>` should be a list with all aliases that you configured in apache (eg.: `www.softwarelivre.org`, `www2.softwarelivre.org`, etc).
  25 +
  26 +This setup is considering that the Noosfero server is running varnish (see `INSTALL.varnish`) and [varnishncsa-vhost](http://gitorious.org/varnisnncsa-vhost).
  27 +
  28 +### 3. Running AWStats for the first time
  29 +
  30 +Run awstats by hand via command line:
  31 +
  32 + # /usr/lib/cgi-bin/awstats.pl -config=<domain>
  33 +
  34 +You should see something as below as output of this command:
  35 +
  36 + # /usr/lib/cgi-bin/awstats.pl -config=softwarelivre.org
  37 + Create/Update database for config "/etc/awstats/awstats.softwarelivre.org.conf" by AWStats version 6.7 (build 1.892)
  38 + From data in log file "/var/log/varnish/varnishncsa-vhost.log"...
  39 + Phase 1 : First bypass old records, searching new record...
  40 + Searching new records from beginning of log file...
  41 + Phase 2 : Now process new records (Flush history on disk after 20000 hosts)...
  42 + Jumped lines in file: 0
  43 + Parsed lines in file: 452
  44 + Found 0 dropped records,
  45 + Found 0 corrupted records,
  46 + Found 0 old records,
  47 + Found 452 new qualified records.
  48 +
  49 +### 4. Setup frontend
  50 +
  51 +You should create a new subdomain to have access to the AWStats, usually something like tools.<domain> (eg.: tools.softwarelivre.org). Don't include this subdomain in HostAliases in the AWStats neither in SiteAlias in the Apache.
  52 +
  53 + # cp /usr/share/doc/awstats/examples/apache.conf /etc/apache2/conf.d/awstats.conf
  54 + # invoke-rc.d apache2 restart
  55 +
  56 +ps.: Don't forget to change the port `/etc/apache/sites-enabled/000-default` to `8080`.
  57 +
  58 +Try: `http://tools.<domain>/cgi-bin/awstats.pl?config=<domain>`
  59 +(eg.: `http://tools.softwarelivre.org/cgi-bin/awstats.pl?config=softwarelivre.org`).
  60 +
  61 +### 5. Schedule AWStats in crontab
  62 +
  63 +`/etc/cron.d/awstats`
  64 +
  65 + 0,10,20,30,40,50 * * * * www-data [ -x /usr/lib/cgi-bin/awstats.pl -a -f /etc/awstats/awstats.<domain>.conf -a -r /var/log/apache/access.log ] && /usr/lib/cgi-bin/awstats.pl -config=<domain> -update >/dev/null
  66 +
  67 +Done, check the AWStats frontend after one or two days to see if everything is working properly.
INSTALL.chat
@@ -1,258 +0,0 @@ @@ -1,258 +0,0 @@
1 -== XMPP/Chat Client Setup  
2 -  
3 -To configure XMPP/BOSH in Noosfero you need:  
4 -  
5 -* REST Client - http://github.com/archiloque/rest-client  
6 -* SystemTimer - http://ph7spot.com/musings/system-timer  
7 -* Pidgin data files - http://www.pidgin.im/  
8 -  
9 -If you use Debian 6.0 (squeeze):  
10 -  
11 -# apt-get install librestclient-ruby pidgin-data ruby1.8-dev  
12 -# gem install SystemTimer  
13 -  
14 -The samples of config file to configure a XMPP/BOSH server with  
15 -ejabberd, postgresql and apache2 can be found at util/chat directory.  
16 -  
17 -== XMPP/Chat Server Setup  
18 -  
19 -This is a step-by-step guide to get a XMPP service working, in a Debian system.  
20 -  
21 -1. Install the required packages  
22 -  
23 -# apt-get install ejabberd odbc-postgresql  
24 -  
25 -2. Ejabberd configuration  
26 -  
27 -All the following changes must be done in config file:  
28 -  
29 - /etc/ejabberd/ejabberd.cfg  
30 -  
31 - 2.1. Set the default admin user  
32 -  
33 -{ acl, admin, { user, "john", "www.example.com" } }.  
34 -{ acl, admin, { user, "bart", "www.example.com" } }.  
35 -  
36 - 2.2. Set the default host  
37 -  
38 -{ hosts, [ "www.example.com" ] }.  
39 -  
40 - 2.3. Http-Bind activation  
41 -  
42 -{ 5280, ejabberd_http, [  
43 - http_bind,  
44 - web_admin  
45 - ]  
46 -}  
47 -  
48 -(...)  
49 -  
50 -{ modules, [  
51 - {mod_http_bind, []},  
52 - ...  
53 -] }.  
54 -  
55 -Ejabberd creates semi-anonymous rooms by default, but Noosfero's Jabber client  
56 -needs non-anonymous room, then we need to change default params of creation  
57 -rooms in ejabberd to create non-anonymous rooms.  
58 -  
59 -In non-anonymous rooms the jabber service sends the new occupant's full JID to  
60 -all occupants in the room[1].  
61 -  
62 -Add option "{default_room_options, [{anonymous, false}]}" to  
63 -/etc/ejabberd/ejabberd.cfg in mod_muc session. See below:  
64 -  
65 -{ mod_muc, [  
66 - %%{host, "conference.@HOST@"},  
67 - {access, muc},  
68 - {access_create, muc},  
69 - {access_persistent, muc},  
70 - {access_admin, muc_admin},  
71 - {max_users, 500},  
72 - {default_room_options, [{anonymous, false}]}  
73 -]},  
74 -  
75 -[1] - http://xmpp.org/extensions/xep-0045.html#enter-nonanon  
76 -  
77 -  
78 - 2.4. Authentication method  
79 -  
80 -To use Postgresql through ODBC, the following modifications must be done:  
81 -  
82 - * Disable the default method:  
83 -  
84 -{auth_method, internal}.  
85 -  
86 - * Enable autheticantion through ODBC:  
87 -  
88 -{auth_method, odbc}.  
89 -  
90 - * Set database server name  
91 -  
92 -{odbc_server, "DSN=PostgreSQLEjabberdNoosfero"}.  
93 -  
94 -  
95 - 2.5. Increase the shaper traffic limit  
96 -  
97 -{ shaper, normal, { maxrate, 10000000 } }.  
98 -  
99 -  
100 - 2.6. Disable unused modules  
101 -  
102 -Unused modules can be disabled, for example:  
103 -  
104 - * s2s  
105 - * web_admin  
106 - * mod_pubsub  
107 - * mod_irc  
108 - * mod_offline  
109 - * mod_admin_extra  
110 - * mod_register  
111 -  
112 -  
113 - 2.7. Enable ODBC modules  
114 -  
115 - * mod_privacy -> mod_privacy_odbc  
116 - * mod_private -> mod_private_odbc  
117 - * mod_roster -> mod_roster_odbc  
118 -  
119 -3. Configuring Postgresql  
120 -  
121 -Login as noosfero user, and execute:  
122 -  
123 - $ psql noosfero < /path/to/noosfero/util/chat/postgresql/ejabberd.sql  
124 -  
125 -Where 'noosfero' may need to be replace by the name of the database used for  
126 -Noosfero.  
127 -  
128 -This will create a new schema inside the noosfero database, called 'ejabberd'.  
129 -  
130 -Note 'noosfero' user should have permission to create Postgresql schemas. Also,  
131 -there should be at least one domain with 'is_default = true' in 'domains'  
132 -table, otherwise people won't be able to see their friends online.  
133 -  
134 -  
135 -4. ODBC configuration  
136 -  
137 -The following files must be created:  
138 -  
139 - * /etc/odbc.ini  
140 -  
141 -[PostgreSQLEjabberdNoosfero]  
142 -Description = PostgreSQL Noosfero ejabberd database  
143 -Driver = PostgreSQL Unicode  
144 -Trace = No  
145 -TraceFile = /tmp/psqlodbc.log  
146 -Database = noosfero  
147 -Servername = localhost  
148 -UserName = <DBUSER>  
149 -Password = <DBPASS>  
150 -Port =  
151 -ReadOnly = No  
152 -RowVersioning = No  
153 -ShowSystemTables = No  
154 -ShowOidColumn = No  
155 -FakeOidIndex = No  
156 -ConnSettings = SET search_path TO ejabberd  
157 -  
158 - * /etc/odbcinst.ini  
159 -  
160 -[PostgreSQL Unicode]  
161 -Description = PostgreSQL ODBC driver (Unicode version)  
162 -Driver = /usr/lib/odbc/psqlodbcw.so  
163 -Setup = /usr/lib/odbc/libodbcpsqlS.so  
164 -Debug = 0  
165 -CommLog = 1  
166 -UsageCount = 3  
167 -  
168 - 4.1 testing all:  
169 -  
170 -# isql 'PostgreSQLEjabberdNoosfero'  
171 -  
172 -If the configuration was done right, the message "Connected!"  
173 -will be displayed.  
174 -  
175 -  
176 -5. Enabling kernel polling and SMP in /etc/default/ejabberd  
177 -  
178 -POLL=true  
179 -SMP=auto  
180 -  
181 -  
182 -6. Increase the file descriptors limit for user ejabberd  
183 -  
184 - 6.1. Uncomment this line in file /etc/pam.d/su:  
185 -  
186 -session required pam_limits.so  
187 -  
188 -  
189 - 6.2. Add this lines to file /etc/security/limits.conf:  
190 -  
191 -ejabberd hard nofile 65536  
192 -ejabberd soft nofile 65536  
193 -  
194 -Now, test the configuration:  
195 -  
196 -# cat /proc/<EJABBERD_BEAM_PROCESS_PID>/limits  
197 -  
198 -  
199 -7. Apache Configuration  
200 -  
201 -Apache server must be configurated as follow:  
202 -  
203 - * /etc/apache2/sites-enabled/noosfero  
204 -  
205 -RewriteEngine On  
206 -Include /usr/share/noosfero/util/chat/apache/xmpp.conf  
207 -  
208 - * /etc/apache2/apache2.conf:  
209 -  
210 -<IfModule mpm_worker_module>  
211 - StartServers 8  
212 - MinSpareThreads 25  
213 - MaxSpareThreads 75  
214 - ThreadLimit 128  
215 - ThreadsPerChild 128  
216 - MaxClients 2048  
217 - MaxRequestsPerChild 0  
218 -</IfModule>  
219 -  
220 -Note: module proxy_http must be enabled:  
221 -  
222 -# a2enmod proxy_http  
223 -  
224 -8. DNS configuration  
225 -  
226 -For this point, we assume you are using BIND as your DNS server. You need to  
227 -add the following entries to the DNS zone file corresponding to the domain  
228 -of your noosfero site:  
229 -  
230 -_xmpp-client._tcp SRV 5 100 5222 master  
231 -conference CNAME master  
232 -_xmpp-client._tcp.conference SRV 5 100 5222 master  
233 -  
234 -If you are running a DNS server other than BIND, you will have to figure out  
235 -how to create equivalente rules for your zone file. Patches to this  
236 -documentation are welcome.  
237 -  
238 -9. Testing this Setup  
239 -  
240 -Adjust shell limits to proceed with some benchmarks and load tests:  
241 -  
242 -# ulimit −s 256  
243 -# ulimit −n 8192  
244 -# echo 10 > /proc/sys/net/ipv4/tcp_syn_retries  
245 -  
246 -To measure the bandwidth between server and client:  
247 -  
248 - * at server side:  
249 -  
250 -# iperf −s  
251 -  
252 - * at client side:  
253 -  
254 -# iperf −c server_ip  
255 -  
256 -For heavy load tests, clone and use this software:  
257 -  
258 -git clone http://git.holoscopio.com/git/metal/tester.git  
INSTALL.chat.md 0 → 100644
@@ -0,0 +1,236 @@ @@ -0,0 +1,236 @@
  1 +XMPP/Chat Client Setup
  2 +======================
  3 +
  4 +To configure XMPP/BOSH in Noosfero you need:
  5 +
  6 +* REST Client - http://github.com/archiloque/rest-client
  7 +* SystemTimer - http://ph7spot.com/musings/system-timer
  8 +* Pidgin data files - http://www.pidgin.im/
  9 +
  10 +If you use Debian 6.0 (squeeze):
  11 +
  12 + # apt-get install librestclient-ruby pidgin-data ruby1.8-dev
  13 + # gem install SystemTimer
  14 +
  15 +The samples of config file to configure a XMPP/BOSH server with ejabberd, postgresql and apache2 can be found at util/chat directory.
  16 +
  17 +XMPP/Chat Server Setup
  18 +======================
  19 +
  20 +This is a step-by-step guide to get a XMPP service working, in a Debian system.
  21 +
  22 +## 1. Install the required packages
  23 +
  24 + # apt-get install ejabberd odbc-postgresql
  25 +
  26 +## 2. Ejabberd configuration
  27 +
  28 +All the following changes must be done in config file: `/etc/ejabberd/ejabberd.cfg`
  29 +
  30 +### 2.1. Set the default admin user
  31 +
  32 + { acl, admin, { user, "john", "www.example.com" } }.
  33 + { acl, admin, { user, "bart", "www.example.com" } }.
  34 +
  35 +### 2.2. Set the default host
  36 +
  37 + { hosts, [ "www.example.com" ] }.
  38 +
  39 +### 2.3. Http-Bind activation
  40 +
  41 + { 5280, ejabberd_http, [
  42 + http_bind,
  43 + web_admin
  44 + ]
  45 + }
  46 +
  47 + (...)
  48 +
  49 + { modules, [
  50 + {mod_http_bind, []},
  51 + ...
  52 + ] }.
  53 +
  54 +Ejabberd creates semi-anonymous rooms by default, but Noosfero's Jabber client needs non-anonymous room, then we need to change default params of creation rooms in ejabberd to create non-anonymous rooms.
  55 +
  56 +In non-anonymous rooms the jabber service sends the new occupant's full JID to all occupants in the room [[1]].
  57 +
  58 +Add option "`{default_room_options, [{anonymous, false}]}`" to `/etc/ejabberd/ejabberd.cfg` in mod_muc session. See below:
  59 +
  60 + { mod_muc, [
  61 + %%{host, "conference.@HOST@"},
  62 + {access, muc},
  63 + {access_create, muc},
  64 + {access_persistent, muc},
  65 + {access_admin, muc_admin},
  66 + {max_users, 500},
  67 + {default_room_options, [{anonymous, false}]}
  68 + ]},
  69 +
  70 +[1]: http://xmpp.org/extensions/xep-0045.html#enter-nonanon
  71 +
  72 +
  73 +### 2.4. Authentication method
  74 +
  75 +To use Postgresql through ODBC, the following modifications must be done:
  76 +
  77 + * Disable the default method:
  78 + `{auth_method, internal}.`
  79 +
  80 + * Enable autheticantion through ODBC:
  81 + `{auth_method, odbc}.`
  82 +
  83 + * Set database server name
  84 + `{odbc_server, "DSN=PostgreSQLEjabberdNoosfero"}.`
  85 +
  86 +
  87 +### 2.5. Increase the shaper traffic limit
  88 +
  89 + { shaper, normal, { maxrate, 10000000 } }.
  90 +
  91 +
  92 +### 2.6. Disable unused modules
  93 +
  94 +Unused modules can be disabled, for example:
  95 +
  96 + * s2s
  97 + * web_admin
  98 + * mod_pubsub
  99 + * mod_irc
  100 + * mod_offline
  101 + * mod_admin_extra
  102 + * mod_register
  103 +
  104 +
  105 +### 2.7. Enable ODBC modules
  106 +
  107 + * mod_privacy -> mod_privacy_odbc
  108 + * mod_private -> mod_private_odbc
  109 + * mod_roster -> mod_roster_odbc
  110 +
  111 +## 3. Configuring Postgresql
  112 +
  113 +Login as noosfero user, and execute:
  114 +
  115 + $ psql noosfero < /path/to/noosfero/util/chat/postgresql/ejabberd.sql
  116 +
  117 +Where `noosfero` may need to be replace by the name of the database used for Noosfero.
  118 +
  119 +This will create a new schema inside the noosfero database, called `ejabberd`.
  120 +
  121 +Note `noosfero` user should have permission to create Postgresql schemas. Also, there should be at least one domain with `is_default = true` in `domains` table, otherwise people won't be able to see their friends online.
  122 +
  123 +## 4. ODBC configuration
  124 +
  125 +The following files must be created:
  126 +
  127 +`/etc/odbc.ini`:
  128 +
  129 + [PostgreSQLEjabberdNoosfero]
  130 + Description = PostgreSQL Noosfero ejabberd database
  131 + Driver = PostgreSQL Unicode
  132 + Trace = No
  133 + TraceFile = /tmp/psqlodbc.log
  134 + Database = noosfero
  135 + Servername = localhost
  136 + UserName = <DBUSER>
  137 + Password = <DBPASS>
  138 + Port =
  139 + ReadOnly = No
  140 + RowVersioning = No
  141 + ShowSystemTables = No
  142 + ShowOidColumn = No
  143 + FakeOidIndex = No
  144 + ConnSettings = SET search_path TO ejabberd
  145 +
  146 +`/etc/odbcinst.ini`:
  147 +
  148 + [PostgreSQL Unicode]
  149 + Description = PostgreSQL ODBC driver (Unicode version)
  150 + Driver = /usr/lib/odbc/psqlodbcw.so
  151 + Setup = /usr/lib/odbc/libodbcpsqlS.so
  152 + Debug = 0
  153 + CommLog = 1
  154 + UsageCount = 3
  155 +
  156 +## 4.1 testing all:
  157 +
  158 + # isql 'PostgreSQLEjabberdNoosfero'
  159 +
  160 +If the configuration was done right, the message "Connected!" will be displayed.
  161 +
  162 +
  163 +## 5. Enabling kernel polling and SMP in `/etc/default/ejabberd`
  164 +
  165 + POLL=true
  166 + SMP=auto
  167 +
  168 +## 6. Increase the file descriptors limit for user ejabberd
  169 +
  170 +### 6.1. Uncomment this line in file `/etc/pam.d/su`:
  171 +
  172 + session required pam_limits.so
  173 +
  174 +### 6.2. Add this lines to file `/etc/security/limits.conf`:
  175 +
  176 + ejabberd hard nofile 65536
  177 + ejabberd soft nofile 65536
  178 +
  179 +Now, test the configuration:
  180 +
  181 + # cat /proc/<EJABBERD_BEAM_PROCESS_PID>/limits
  182 +
  183 +## 7. Apache Configuration
  184 +
  185 +Apache server must be configurated as follow:
  186 +
  187 +`/etc/apache2/sites-enabled/noosfero`:
  188 +
  189 + RewriteEngine On
  190 + Include /usr/share/noosfero/util/chat/apache/xmpp.conf
  191 +
  192 +`/etc/apache2/apache2.conf`:
  193 +
  194 + <IfModule mpm_worker_module>
  195 + StartServers 8
  196 + MinSpareThreads 25
  197 + MaxSpareThreads 75
  198 + ThreadLimit 128
  199 + ThreadsPerChild 128
  200 + MaxClients 2048
  201 + MaxRequestsPerChild 0
  202 + </IfModule>
  203 +
  204 +Note: module proxy_http must be enabled:
  205 +
  206 + # a2enmod proxy_http
  207 +
  208 +## 8. DNS configuration
  209 +
  210 +For this point, we assume you are using BIND as your DNS server. You need to add the following entries to the DNS zone file corresponding to the domain of your noosfero site:
  211 +
  212 + _xmpp-client._tcp SRV 5 100 5222 master
  213 + conference CNAME master
  214 + _xmpp-client._tcp.conference SRV 5 100 5222 master
  215 +
  216 +If you are running a DNS server other than BIND, you will have to figure out how to create equivalente rules for your zone file. Patches to this documentation are welcome.
  217 +
  218 +## 9. Testing this Setup
  219 +
  220 +Adjust shell limits to proceed with some benchmarks and load tests:
  221 +
  222 + # ulimit −s 256
  223 + # ulimit −n 8192
  224 + # echo 10 > /proc/sys/net/ipv4/tcp_syn_retries
  225 +
  226 +To measure the bandwidth between server and client:
  227 +
  228 + * at server side:
  229 + `# iperf −s`
  230 +
  231 + * at client side:
  232 + `# iperf −c server_ip`
  233 +
  234 +For heavy load tests, clone and use this software:
  235 +
  236 + $ git clone http://git.holoscopio.com/git/metal/tester.git
INSTALL.email
@@ -1,43 +0,0 @@ @@ -1,43 +0,0 @@
1 -= Noosfero email setup  
2 -  
3 -If you know mail systems well, you just need to make sure that the local MTA,  
4 -listening on localhost:25, is able to deliver e-mails to the internet. Any mail  
5 -server will do it. You can stop reading now.  
6 -  
7 -If you are not an email specialist, then follow the instructions below. We  
8 -suggest that you use the Postfix mail server, since it is easy to configure and  
9 -very reliable. Just follow the instructions below.  
10 -  
11 -To install Postfix:  
12 -  
13 -# apt-get install postfix  
14 -  
15 -During the installation process, you will be asked a few questions. Your answer  
16 -to them will vary in 2 cases:  
17 -  
18 -Case 1: you can send e-mails directly to the internet. This will be the case  
19 -for most commercial private servers. Your answers should be:  
20 -  
21 - General type of mail configuration: Internet site  
22 - System mail name: the name of your domain, e.g. "mysocialnetwork.com"  
23 -  
24 -Case 2: you cannot, or don't want to, send e-mail directly to the internet.  
25 -This happens for example if your server is not allowed to make outbound  
26 -connections on port 25, or if you want to concentrate all your outbound mail  
27 -through a single SMTP server. Your answers in this case should be:  
28 -  
29 - General type of mail configuration: Internet with smarthost  
30 - System mail name: the name of your domain, e.g. "mysocialnetwork.com"  
31 - SMTP relay host: smtp.yourprovider.com  
32 -  
33 -Note that smtp.yourprovider.com must allow your server to deliver e-mails  
34 -through it. You should probably ask your servive provider about this.  
35 -  
36 -There is another possibility: if you are installing on a shared server, and  
37 -don't have permission to configure the local MTA, you can instruct Noosfero to  
38 -send e-mails directly through an external server. Please note that this should  
39 -be your last option, since contacting an external SMTP server directly may slow  
40 -down your Noosfero application server. To configure Noosfero to send e-mails  
41 -through an external SMTP server, follow the instructions on  
42 -http://noosfero.org/Development/SMTPMailSending  
43 -  
INSTALL.email.md 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +Noosfero email setup
  2 +====================
  3 +
  4 +If you know mail systems well, you just need to make sure that the local MTA, listening on localhost:25, is able to deliver e-mails to the internet. Any mail server will do it. You can stop reading now.
  5 +
  6 +If you are not an email specialist, then follow the instructions below. We suggest that you use the Postfix mail server, since it is easy to configure and very reliable. Just follow the instructions below.
  7 +
  8 +To install Postfix:
  9 +
  10 + # apt-get install postfix
  11 +
  12 +During the installation process, you will be asked a few questions. Your answer to them will vary in 2 cases:
  13 +
  14 +**Case 1**: you can send e-mails directly to the internet. This will be the case for most commercial private servers. Your answers should be:
  15 +
  16 + * General type of mail configuration: Internet site
  17 + * System mail name: the name of your domain, e.g. "mysocialnetwork.com"
  18 +
  19 +**Case 2**: you cannot, or don't want to, send e-mail directly to the internet. This happens for example if your server is not allowed to make outbound connections on port 25, or if you want to concentrate all your outbound mail through a single SMTP server. Your answers in this case should be:
  20 +
  21 + * General type of mail configuration: Internet with smarthost
  22 + * System mail name: the name of your domain, e.g. "mysocialnetwork.com"
  23 + * SMTP relay host: smtp.yourprovider.com
  24 +
  25 +Note that smtp.yourprovider.com must allow your server to deliver e-mails through it. You should probably ask your servive provider about this.
  26 +
  27 +There is another possibility: if you are installing on a shared server, and don't have permission to configure the local MTA, you can instruct Noosfero to send e-mails directly through an external server. Please note that this should be your last option, since contacting an external SMTP server directly may slow down your Noosfero application server. To configure Noosfero to send e-mails through an external SMTP server, follow the instructions on http://noosfero.org/Development/SMTPMailSending
  28 +
INSTALL.md 0 → 100644
@@ -0,0 +1,335 @@ @@ -0,0 +1,335 @@
  1 +Noosfero installation instructions from source for production environments
  2 +==========================================================================
  3 +
  4 +The instructions below can be used for setting up a Noosfero production environment from the Noosfero sources.
  5 +
  6 +Before you start installing Noosfero manually, see the information about the Noosfero Debian package at http://noosfero.org/Development/DebianPackage. Using the Debian packages on a Debian stable system is the recommended method for installing production environments.
  7 +
  8 +If you want to setup a development environment instead of a production one, stop reading this file right now and read the file `HACKING.md` instead.
  9 +
  10 +For a complete installation guide, please see the following web page: http://noosfero.org/Development/HowToInstall
  11 +
  12 +If you have problems with the setup, please feel free to ask questions in the development mailing list.
  13 +
  14 +Requirements
  15 +------------
  16 +
  17 +**DISCLAIMER**: this installation procedure is tested with Debian stable, which is currently the only recommended operating system for production usage. It is possible that you can install it on other systems, and if you do so, please report it on one of the Noosfero mailing lists, and please send a patch updating these instructions.
  18 +
  19 +Noosfero is written in Ruby with the "[Rails framework](http://www.rubyonrails.org)", so the process of setting it up is pretty similar to other Rails applications.
  20 +
  21 +You need to install some packages Noosfero depends on. On Debian GNU/Linux or Debian-based systems, all of these packages are available through the Debian archive. You can install them with the following command:
  22 +
  23 + # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby1.8 \
  24 + libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libhpricot-ruby \
  25 + libwill-paginate-ruby iso-codes libfeedparser-ruby libdaemons-ruby thin \
  26 + tango-icon-theme libnokogiri-ruby
  27 +
  28 +On other systems, they may or may not be available through your regular package management system. Below are the links to their homepages.
  29 +
  30 +* Ruby: http://www.ruby-lang.org
  31 +* Rake: http://rake.rubyforge.org
  32 +* po4a: http://po4a.alioth.debian.org
  33 +* Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby
  34 +* rcov: http://eigenclass.org/hiki/rcov
  35 +* RMagick: http://rmagick.rubyforge.org
  36 +* RedCloth: http://redcloth.org
  37 +* will_paginate: http://github.com/mislav/will_paginate/wikis
  38 +* iso-codes: http://pkg-isocodes.alioth.debian.org
  39 +* feedparser: http://packages.debian.org/sid/libfeedparser-ruby
  40 +* Daemons - http://daemons.rubyforge.org
  41 +* Thin: http://code.macournoyer.com/thin
  42 +* tango-icon-theme: http://tango.freedesktop.org/Tango_Icon_Library
  43 +* Hpricot: http://hpricot.com
  44 +* Nokogiri: http://nokogiri.org/
  45 +
  46 +If you manage to install Noosfero successfully on other systems than Debian,
  47 +please feel free to contact the Noosfero development mailing with the
  48 +instructions for doing so, and we'll include it here.
  49 +
  50 +As root user
  51 +============
  52 +
  53 +Install memcached. On Debian:
  54 +
  55 + # apt-get install memcached
  56 +
  57 +Study whether you need to raise the ammount of memory it uses for caching, depending on the demand you expect for your site. If you are going to run a high-traffic site, you will want to raise the ammount of memory reserved for caching.
  58 +
  59 +It is recommended that you run noosfero with its own user account. To create such an account, please do the following:
  60 +
  61 + # adduser --system --group noosfero --shell /bin/sh --home /var/lib/noosfero
  62 +
  63 +(note that you can change the `$HOME` directory of the user if you wish, here we are using `/var/lib/noosfero`)
  64 +
  65 +The `--system` option will tell adduser to create a system user, i.e. this user will not have a password and cannot login to the system directly. To become this user, you have to use sudo:
  66 +
  67 + # sudo -u noosfero -i
  68 + or
  69 + # su - noosfero
  70 +
  71 +As noosfero user
  72 +================
  73 +
  74 +downloading from git
  75 +--------------------
  76 +
  77 +Here we are cloning the noosfero repository from git. Note: you will need to install git before.
  78 +
  79 + $ git clone git://gitorious.org/noosfero/noosfero.git current
  80 + $ cd current
  81 + $ git checkout -b stable origin/stable
  82 +
  83 +downloading tarball
  84 +-------------------
  85 +
  86 +Note: replace 0.39.0 below from the latest stable version.
  87 +
  88 + $ wget http://noosfero.org/pub/Development/NoosferoVersion00x39x00/noosfero-0.39.0.tar.gz
  89 + $ tar -zxvf noosfero-0.39.0.tar.gz
  90 + $ ln -s noosfero-0.39.0 current
  91 + $ cd current
  92 +
  93 +Create the thin configuration file:
  94 +
  95 + $ thin -C config/thin.yml -e production config
  96 +
  97 +Edit config/thin.yml to suit your needs. Make sure your apache configuration matches the thin cluster configuration, specially in respect to the ports and numbers of thin instances.
  98 +
  99 +*Note*: currently Noosfero only supports Rails 2.3.5, which is the version in Debian Squeeze. If you have a Rails version newer than that, Noosfero will probably not work. You can install Rails 2.3.5 into your Noosfero installation with the following procedure:
  100 +
  101 + $ cd /var/lib/noosfero/current/vendor
  102 + $ wget http://ftp.de.debian.org/debian/pool/main/r/rails/rails_2.3.5.orig.tar.gz
  103 + $ tar xzf rails_2.3.5.orig.tar.gz
  104 + $ ln -s rails-2.3.5 rails
  105 +
  106 +As root user
  107 +============
  108 +
  109 +Setup Noosfero log and tmp directories:
  110 +
  111 + # cd /var/lib/noosfero/current
  112 + # ./etc/init.d/noosfero setup
  113 +
  114 +Now it's time to setup the database. In this example we are using PostgreSQL, so if you are planning to use a different database this steps won't apply.
  115 +
  116 + # apt-get install postgresql libpgsql-ruby
  117 + # su postgres -c 'createuser noosfero -S -d -R'
  118 +
  119 +By default Rails will try to connect on postgresql through 5432 port, you can check it on `/etc/postgresql/8.4/main/postgresql.conf` file.
  120 +
  121 +Restart postgresql:
  122 + # invoke-rc.d postgresql restart
  123 +
  124 +Noosfero needs a functional e-mail setup to work: the local mail system should be able to deliver e-mail to the internet, either directly or through an external SMTP server. Please check the documentation at the INSTALL.email file.
  125 +
  126 +As noosfero user
  127 +================
  128 +
  129 +Now create the databases:
  130 +
  131 + $ cd /var/lib/noosfero/current
  132 + $ createdb noosfero_production
  133 + $ createdb noosfero_development
  134 + $ createdb noosfero_test
  135 +
  136 +The development and test databases are actually optional. If you are creating a stricly production server, you will probably not need them.
  137 +
  138 +Now we want to configure Noosfero for accessing the database we just created. To do that, you can 1) copy `config/database.yml.pgsql` to `config/database.yml`, or create `config/database.yml` from scratch with the following content:
  139 +
  140 + production:
  141 + adapter: postgresql
  142 + encoding: unicode
  143 + database: noosfero_production
  144 + username: noosfero
  145 +
  146 +Now, to test the database access, you can fire the Rails database console:
  147 +
  148 + $ ./script/dbconsole production
  149 +
  150 +If it connects to your database, then everything is fine. If you got an error message, then you have to check your database configuration.
  151 +
  152 +Create the database structure:
  153 +
  154 + $ RAILS_ENV=production rake db:schema:load
  155 +
  156 +Compile the translations:
  157 +
  158 + $ RAILS_ENV=production rake noosfero:translations:compile
  159 +
  160 +Now we must create some initial data. To create your default environment (the first one), run the command below:
  161 +
  162 + $ RAILS_ENV=production ./script/runner 'Environment.create!(:name => "My environment", :is_default => true)'
  163 +
  164 +(of course, replace "My environment" with your environment's name!)
  165 +
  166 +And now you have to add the domain name you will be using for your noosfero site to the list of domains of that default environment you just created:
  167 +
  168 + $ RAILS_ENV=production ./script/runner "Environment.default.domains << Domain.new(:name => 'your.domain.com')"
  169 +
  170 +(replace "your.domain.com" with your actual domain name)
  171 +
  172 +Add at least one user as admin of environment:
  173 +
  174 + $ RAILS_ENV=production ./script/runner "User.create(:login => 'adminuser', :email => 'admin@example.com', :password => 'admin', :password_confirmation => 'admin', :environment => Environment.default, :activated_at => Time.new)"
  175 +
  176 +(replace "adminuser", "admin@example.com", "admin" with the login, email and password of your environment administrator)
  177 +
  178 +To start the Noosfero application servers:
  179 +
  180 + $ ./script/production start
  181 +
  182 +At this point you have a functional Noosfero installation running, the only thing left is to configure your webserver as a reverse proxy to pass requests to them.
  183 +
  184 +
  185 +Apache instalation
  186 +==================
  187 +
  188 + # apt-get install apache2
  189 +
  190 +Apache configuration
  191 +--------------------
  192 +
  193 +First you have to enable the following some apache modules:
  194 +
  195 + * deflate
  196 + * expires
  197 + * proxy
  198 + * proxy_balancer
  199 + * proxy_http
  200 + * rewrite
  201 +
  202 +On Debian GNU/Linux system, these modules can be enabled with the following command line, as root:
  203 +
  204 + # a2enmod deflate expires proxy proxy_balancer proxy_http rewrite
  205 +
  206 +In other systems the way by which you enable apache modules may be different.
  207 +
  208 +Now with the Apache configuration. You can use the template below, replacing `/var/lib/noosfero/current` with the directory in which your noosfero installation is, your.domain.com with the domain name of your noosfero site. We are assuming that you are running two thin instances on ports 3000 and 3001. If your setup is different you'll need to adjust `<Proxy>` section. If you don't understand something in the configuration, please refer to the apache documentation.
  209 +
  210 +Add a file called "mysite" (or whatever name you want to give to your noosfero site) to `/etc/apache2/sites-available` with the following content, and customize as needed (as usual, make sure you replace "your.domain.com" with you actual domain name, and "`/var/lib/noosfero/current`" with the directory where Noosfero is installed):
  211 +
  212 + <VirtualHost *:80>
  213 + ServerName your.domain.com
  214 +
  215 + DocumentRoot "/var/lib/noosfero/current/public"
  216 + <Directory "/var/lib/noosfero/current/public">
  217 + Options FollowSymLinks
  218 + AllowOverride None
  219 + Order Allow,Deny
  220 + Allow from all
  221 + </Directory>
  222 +
  223 + RewriteEngine On
  224 +
  225 + # Rewrite index to check for static index.html
  226 + RewriteRule ^/$ /index.html [QSA]
  227 +
  228 + # Rewrite to check for Rails cached page
  229 + RewriteRule ^([^.]+)$ $1.html [QSA]
  230 +
  231 + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  232 + RewriteRule ^.*$ balancer://noosfero%{REQUEST_URI} [P,QSA,L]
  233 +
  234 + ErrorDocument 503 /503.html
  235 +
  236 + ErrorLog /var/log/apache2/noosfero.log
  237 + LogLevel warn
  238 + CustomLog /var/log/apache2/noosfero.access.log combined
  239 +
  240 + Include /var/lib/noosfero/current/etc/noosfero/apache/cache.conf
  241 +
  242 + </VirtualHost>
  243 +
  244 + <Proxy balancer://noosfero>
  245 + BalancerMember http://127.0.0.1:3000
  246 + BalancerMember http://127.0.0.1:3001
  247 + Order Allow,Deny
  248 + Allow from All
  249 + </Proxy>
  250 +
  251 +The cache.conf file included in the end of the <VirtualHost> section is important, since it will tell apache to pass expiration and cache headers to clients so that the site feels faster for users. Do we need to say that using that configuration is strongly recommended?
  252 +
  253 +Enable that site with (as root, replace "mysite" with the actual name you gave to your site configuration):
  254 +
  255 + # a2ensite mysite
  256 +
  257 +Now restart your apache server (as root):
  258 +
  259 + # invoke-rc.d apache2 restart
  260 +
  261 +
  262 +Enabling exception notifications
  263 +================================
  264 +
  265 +This is an optional step. You will need it only if you want to receive e-mail notifications when some exception occurs on Noosfero.
  266 +
  267 +First, install this version of the gem. Others versions may not be compatible with Noosfero:
  268 +
  269 + # gem install exception_notification -v 1.0.20090728
  270 +
  271 +You can configure the e-mails that will receive the notifications. Change the file config/noosfero.yml as the following example, replacing the e-mails by real ones:
  272 +
  273 + production:
  274 + exception_recipients: [admin@example.com, you@example.com]
  275 +
  276 +
  277 +Maintainance
  278 +============
  279 +
  280 +To ease the maintainance, install a symbolic link for the Noosfero startup script in your server and add it to the system initialization and shutdown sequences (as root):
  281 +
  282 + # ln -s /var/lib/noosfero/current/etc/init.d/noosfero /etc/init.d/noosfero
  283 + # update-rc.d noosfero defaults
  284 + Adding system startup for /etc/init.d/noosfero ...
  285 + /etc/rc0.d/K20noosfero -> ../init.d/noosfero
  286 + /etc/rc1.d/K20noosfero -> ../init.d/noosfero
  287 + /etc/rc6.d/K20noosfero -> ../init.d/noosfero
  288 + /etc/rc2.d/S20noosfero -> ../init.d/noosfero
  289 + /etc/rc3.d/S20noosfero -> ../init.d/noosfero
  290 + /etc/rc4.d/S20noosfero -> ../init.d/noosfero
  291 + /etc/rc5.d/S20noosfero -> ../init.d/noosfero
  292 +
  293 +Now to start Noosfero, you do as root:
  294 +
  295 + # invoke-rc.d noosfero start
  296 +
  297 +To stop Noosfero:
  298 +
  299 + # invoke-rc.d noosfero start
  300 +
  301 +To restart Noosfero:
  302 +
  303 + # invoke-rc.d noosfero restart
  304 +
  305 +Noosfero will be automatically started during system boot, and automatically stopped if the system shuts down for some reason (or during the shutdown part of a reboot).
  306 +
  307 +Rotating logs
  308 +=============
  309 +
  310 +Noosfero provides an example logrotate configuation to rotate its logs. To use it, create a symbolic link in `/etc/logrotate.d/`:
  311 +
  312 + # cd /etc/logrotate.d/
  313 + # ln -s /var/lib/noosfero/current/etc/logrotate.d/noosfero
  314 +
  315 +Note that the provided file assumes Noosfero logging is being done in `/var/log/noosfero` (which is the case if you followed the instructions above correctly). If the logs are stored elsewhere, it's recommended that you copy the file over to `/etc/logrotate.d/` and modify it to point to your local log directly.
  316 +
  317 +Upgrading
  318 +=========
  319 +
  320 +If you followed the steps in this document and installed Noosfero from the git repository, then upgrading is easy. First, you need to allow the noosfero user to restart the memcached server with sudo, by adding the following line in `/etc/sudoers`:
  321 +
  322 + noosfero ALL=NOPASSWD: /etc/init.d/memcached
  323 +
  324 +Then, to perform an upgrade, do the following as the noosfero user:
  325 +
  326 + $ cd /var/lib/noosfero/current
  327 + $ ./script/git-upgrade
  328 +
  329 +The `git-upgrade` script will take care of everything for you. It will first stop the service, then fetch the current source code, upgrade database, compile translations, and then start the service again.
  330 +
  331 +*Note 1*: make sure your local git repository is following the "stable" branch, just like the instructions above. The `master` branch is **not** recommended for use in production environments.
  332 +
  333 +*Note 2*: always read the release notes before upgrading. Sometimes there will be steps that must be performed manually. If that is the case, you can invoke the `git-upgrade` script with the special parameter `--shell` that will give you a shell after the upgrade, which you can use to perform any manual steps required:
  334 +
  335 + $ ./script/git-upgrade --shell
INSTALL.multitenancy
@@ -1,163 +0,0 @@ @@ -1,163 +0,0 @@
1 -== Multitenancy support  
2 -  
3 -Multitenancy refers to a principle in software architecture where a  
4 -single instance of the software runs on a server, serving multiple  
5 -client organizations (tenants). Multitenancy is contrasted with a  
6 -multi-instance architecture where separate software instances (or  
7 -hardware systems) are set up for different client organizations. With  
8 -a multitenant architecture, a software application is designed to  
9 -virtually partition its data and configuration, and each client  
10 -organization works with a customized virtual application instance.  
11 -  
12 -Today this feature is available only for PostgreSQL databases.  
13 -  
14 -This document assumes that you have a new fully PostgresSQL default Noosfero  
15 -installation as explained at the INSTALL file.  
16 -  
17 -== Separated data  
18 -  
19 -The items below are separated for each hosted environment:  
20 -  
21 -* Uploaded files  
22 -* Database  
23 -* Solr index  
24 -* ActiveRecord#cache_key  
25 -* Feed updater  
26 -* Delayed Job Workers  
27 -  
28 -== Database configuration file  
29 -  
30 -The file config/database.yml must follow a structure in order to  
31 -achieve multitenancy support. In this example, we will set 3  
32 -different environments: env1, env2 and env3.  
33 -  
34 -Each "hosted" environment must have an entry like this:  
35 -  
36 -env1_production:  
37 - adapter: postgresql  
38 - encoding: unicode  
39 - database: noosfero  
40 - schema_search_path: public  
41 - username: noosfero  
42 - domains:  
43 - - env1.com  
44 - - env1.org  
45 -  
46 -env2_production:  
47 - adapter: postgresql  
48 - encoding: unicode  
49 - database: noosfero  
50 - schema_search_path: env2  
51 - username: noosfero  
52 - domains:  
53 - - env2.com  
54 - - env2.org  
55 -  
56 -env3_production:  
57 - adapter: postgresql  
58 - encoding: unicode  
59 - database: noosfero  
60 - schema_search_path: env3  
61 - username: noosfero  
62 - domains:  
63 - - env3.com  
64 - - env3.net  
65 -  
66 -The "hosted" environments define, besides the schema_search_path, a  
67 -list of domains that, when accessed, tells which database the  
68 -application should use. Also, the environment name must end with  
69 -'_hosting', where 'hosting' is the name of the hosting environment.  
70 -  
71 -You must also tell the application which is the default environment.  
72 -  
73 -production:  
74 - env1_production  
75 -  
76 -On the example above there are only three hosted environments, but it  
77 -can be more than three. The schemas 'env2' and 'env3' must already  
78 -exist in the same database of the hosting environment. As postgres  
79 -user, you can create them typing:  
80 -  
81 -$ psql database_name -c "CREATE SCHEMA env2 AUTHORIZATION database_user"  
82 -$ psql database_name -c "CREATE SCHEMA env3 AUTHORIZATION database_user"  
83 -  
84 -Replace database_name and database_user above with your stuff.  
85 -  
86 -So, yet on this same example, when a user accesses http://env2.com or  
87 -http://env2.org, the Noosfero application running on production will  
88 -turn the database schema to 'env2'. When the access is from domains  
89 -http://env3.com or http://env3.net, the schema to be loaded will be  
90 -'env3'.  
91 -  
92 -There is an example of this file in config/database.yml.multitenancy  
93 -  
94 -== Preparing the database  
95 -  
96 -Now create the environments:  
97 -  
98 -$ RAILS_ENV=production rake multitenancy:create  
99 -  
100 -This command above will create the hosted environment files equal to  
101 -their hosting environment, here called 'production'.  
102 -  
103 -Run db:schema:load for each other environment:  
104 -  
105 -$ RAILS_ENV=env2_production rake db:schema:load  
106 -$ RAILS_ENV=env3_production rake db:schema:load  
107 -  
108 -Then run the migrations for the hosting environment, and it will  
109 -run for each of its hosted environments:  
110 -  
111 -RAILS_ENV=production rake db:migrate  
112 -  
113 -== Start Noosfero  
114 -  
115 -Run Noosfero init file as root:  
116 -  
117 -# invoke-rc.d noosfero start  
118 -  
119 -== Solr  
120 -  
121 -It's necessary to run only one instance of Solr. Don't worry  
122 -about this, Noosfero initializer had already done this for you.  
123 -  
124 -== Feed updater & Delayed job  
125 -  
126 -Just for your information, a daemon of feed-updater and delayed_job  
127 -must be running for each environment. Noosfero initializer do this,  
128 -relax.  
129 -  
130 -== Uploaded files  
131 -  
132 -When running with PostgreSQL, Noosfero uploads stuff to a folder named  
133 -the same way as the running schema. Inside the upload folder root, for  
134 -example, will be public/image_uploads/env2 and public/image_uploads/env3.  
135 -  
136 -== Adding multitenancy support to an existing Noosfero environment  
137 -  
138 -If you already have a Noosfero environment, you can turn it multitenant  
139 -by following the steps below in addition to the previous steps:  
140 -  
141 -1. Reindex your database  
142 -  
143 -Rebuild the Solr index by running the following task just  
144 -for your hosting environment, do this as noosfero user:  
145 -  
146 -$ RAILS_ENV=production rake multitenancy:reindex  
147 -  
148 -2. Move the uploaded files to the right place  
149 -  
150 -Add a directory with the same name as your schema name (by default this  
151 -name is 'public') in the root of each upload directory, for example,  
152 -public/articles/0000 will be moved to public/articles/public/0000. Do this  
153 -with the directories public/image_uploads, public/articles and public/thumbnails.  
154 -  
155 -3. Fix paths on activities  
156 -  
157 -The profile activities store static paths to the images, so it's necessary to fix  
158 -these paths. You can do this easily by setting an alias on your webserver.  
159 -On Apache you can add the three rules below, where 'public' is the schema name:  
160 -  
161 - RewriteRule ^/articles(.+) /articles/public$1  
162 - RewriteRule ^/image_uploads(.+) /image_uploads/public$1  
163 - RewriteRule ^/thumbnails(.+) /thumbnails/public$1  
INSTALL.multitenancy.md 0 → 100644
@@ -0,0 +1,133 @@ @@ -0,0 +1,133 @@
  1 +Multitenancy support
  2 +====================
  3 +
  4 +Multitenancy refers to a principle in software architecture where a single instance of the software runs on a server, serving multiple client organizations (tenants). Multitenancy is contrasted with a multi-instance architecture where separate software instances (or hardware systems) are set up for different client organizations. With a multitenant architecture, a software application is designed to virtually partition its data and configuration, and each client organization works with a customized virtual application instance.
  5 +
  6 +Today this feature is available only for PostgreSQL databases.
  7 +
  8 +This document assumes that you have a new fully PostgresSQL default Noosfero installation as explained at the `INSTALL.md` file.
  9 +
  10 +Separated data
  11 +--------------
  12 +
  13 +The items below are separated for each hosted environment:
  14 +
  15 +* Uploaded files
  16 +* Database
  17 +* Solr index
  18 +* ActiveRecord#cache_key
  19 +* Feed updater
  20 +* Delayed Job Workers
  21 +
  22 +Database configuration file
  23 +---------------------------
  24 +
  25 +The file config/database.yml must follow a structure in order to achieve multitenancy support. In this example, we will set 3 different environments: env1, env2 and env3.
  26 +
  27 +Each "hosted" environment must have an entry like this:
  28 +
  29 + env1_production:
  30 + adapter: postgresql
  31 + encoding: unicode
  32 + database: noosfero
  33 + schema_search_path: public
  34 + username: noosfero
  35 + domains:
  36 + - env1.com
  37 + - env1.org
  38 +
  39 + env2_production:
  40 + adapter: postgresql
  41 + encoding: unicode
  42 + database: noosfero
  43 + schema_search_path: env2
  44 + username: noosfero
  45 + domains:
  46 + - env2.com
  47 + - env2.org
  48 +
  49 + env3_production:
  50 + adapter: postgresql
  51 + encoding: unicode
  52 + database: noosfero
  53 + schema_search_path: env3
  54 + username: noosfero
  55 + domains:
  56 + - env3.com
  57 + - env3.net
  58 +
  59 +The "hosted" environments define, besides the `schema_search_path`, a list of domains that, when accessed, tells which database the application should use. Also, the environment name must end with "`_<hosting>`", where `<hosting>` is the name of the hosting environment.
  60 +
  61 +You must also tell the application which is the default environment.
  62 +
  63 + production:
  64 + env1_production
  65 +
  66 +On the example above there are only three hosted environments, but it can be more than three. The schemas `env2` and `env3` must already exist in the same database of the hosting environment. As postgres user, you can create them typing:
  67 +
  68 + $ psql database_name -c "CREATE SCHEMA env2 AUTHORIZATION database_user"
  69 + $ psql database_name -c "CREATE SCHEMA env3 AUTHORIZATION database_user"
  70 +
  71 +Replace `database_name` and `database_user` above with your stuff.
  72 +
  73 +So, yet on this same example, when a user accesses http://env2.com or http://env2.org, the Noosfero application running on production will turn the database schema to `env2`. When the access is from domains http://env3.com or http://env3.net, the schema to be loaded will be `env3`.
  74 +
  75 +There is an example of this file in `config/database.yml.multitenancy`
  76 +
  77 +Preparing the database
  78 +----------------------
  79 +
  80 +Now create the environments:
  81 +
  82 + $ RAILS_ENV=production rake multitenancy:create
  83 +
  84 +This command above will create the hosted environment files equal to their hosting environment, here called 'production'.
  85 +
  86 +Run db:schema:load for each other environment:
  87 +
  88 + $ RAILS_ENV=env2_production rake db:schema:load
  89 + $ RAILS_ENV=env3_production rake db:schema:load
  90 +
  91 +Then run the migrations for the hosting environment, and it will run for each of its hosted environments:
  92 +
  93 + RAILS_ENV=production rake db:migrate
  94 +
  95 +Start Noosfero
  96 +--------------
  97 +
  98 +Run Noosfero init file as root:
  99 +
  100 + # invoke-rc.d noosfero start
  101 +
  102 +Feed updater & Delayed job
  103 +--------------------------
  104 +
  105 +Just for your information, a daemon of `feed-updater` and `delayed_job` must be running for each environment. Noosfero initializer do this, relax.
  106 +
  107 +Uploaded files
  108 +--------------
  109 +
  110 +When running with PostgreSQL, Noosfero uploads stuff to a folder named the same way as the running schema. Inside the upload folder root, for example, will be `public/image_uploads/env2` and `public/image_uploads/env3`.
  111 +
  112 +Adding multitenancy support to an existing Noosfero environment
  113 +---------------------------------------------------------------
  114 +
  115 +If you already have a Noosfero environment, you can turn it multitenant by following the steps below in addition to the previous steps:
  116 +
  117 +### 1. Reindex your database
  118 +
  119 +Rebuild the Solr index by running the following task just for your hosting environment, do this as noosfero user:
  120 +
  121 + $ RAILS_ENV=production rake multitenancy:reindex
  122 +
  123 +### 2. Move the uploaded files to the right place
  124 +
  125 +Add a directory with the same name as your schema name (by default this name is `public`) in the root of each upload directory, for example, `public/articles/0000` will be moved to `public/articles/public/0000`. Do this with the directories `public/image_uploads`, `public/articles` and `public/thumbnails`.
  126 +
  127 +### 3. Fix paths on activities
  128 +
  129 +The profile activities store static paths to the images, so it's necessary to fix these paths. You can do this easily by setting an alias on your webserver. On Apache you can add the three rules below, where 'public' is the schema name:
  130 +
  131 + RewriteRule ^/articles(.+) /articles/public$1
  132 + RewriteRule ^/image_uploads(.+) /image_uploads/public$1
  133 + RewriteRule ^/thumbnails(.+) /thumbnails/public$1
INSTALL.varnish
@@ -1,71 +0,0 @@ @@ -1,71 +0,0 @@
1 -= Setting up Varnish for your Noosfero site  
2 -  
3 -Varnish is a HTTP caching server, and using it together with Noosfero is highly  
4 -recommended. See http://www.varnish-cache.org/ for more information on Varnish.  
5 -  
6 -Varnish can be set up to use with Noosfero with the following steps:  
7 -  
8 -1) setup Noosfero with apache according to the INSTALL file. If you used the  
9 -Debian package to install noosfero, you don't need to do anything about this.  
10 -  
11 -2) install Varnish  
12 -  
13 - # apt-get install varnish  
14 -  
15 -Install the RPAF apache module (or skip this step if not using apache):  
16 -  
17 - # apt-get install libapache2-mod-rpaf  
18 -  
19 -3) Change Apache to listen on port 8080 instead of 80  
20 -  
21 -3a) Edit /etc/apache2/ports.conf, and:  
22 -  
23 - * change 'NameVirtualHost *:80' to 'NameVirtualHost *:8080'  
24 - * change 'Listen 80' to 'Listen 127.0.0.1:8080'  
25 -  
26 -3b) Edit /etc/apache2/sites-enabled/*, and change '<VirtualHost *:80>' to  
27 -'<VirtualHost *:8080>'  
28 -  
29 -3c) Restart apache  
30 -  
31 - # invoke-rc.d apache2 restart  
32 -  
33 -4) Varnish configuration  
34 -  
35 -4a) Edit /etc/default/varnish  
36 -  
37 - * change the line that says "START=no" to say "START=yes"  
38 - * change '-a :6081' to '-a :80'  
39 -  
40 -4b) Edit /etc/varnish/default.vcl and add the following lines at the end:  
41 -  
42 - include "/etc/noosfero/varnish-noosfero.vcl";  
43 - include "/etc/noosfero/varnish-accept-language.vcl";  
44 -  
45 -On manual installations, change "/etc/noosfero/*" to  
46 -"{Rails.root}/etc/noosfero/*"  
47 -  
48 -NOTE: it is very important that the *.vcl files are included in that order,  
49 -i.e. *first* include "varnish-noosfero.vcl", and *after*  
50 -"noosfero-accept-language.cvl".  
51 -  
52 -4c) Restart Varnish  
53 -  
54 - # invoke-rc.d varnish restart  
55 -  
56 -5) Enable varnish logging:  
57 -  
58 -5a) Edit /etc/default/varnishncsa and uncomment the line that contains:  
59 -  
60 -VARNISHNCSA_ENABLED=1  
61 -  
62 -The varnish log will be written to /var/log/varnish/varnishncsa.log in an  
63 -apache-compatible format. You should change your statistics generation software  
64 -(e.g. awstats) to use that instead of apache logs.  
65 -  
66 -5b) Restart Varnish Logging service  
67 -  
68 - # invoke-rc.d varnishncsa restart  
69 -  
70 -Thanks to Cosimo Streppone for varnish-accept-language. See  
71 -http://github.com/cosimo/varnish-accept-language for more information.  
INSTALL.varnish.md 0 → 100644
@@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
  1 +Setting up Varnish for your Noosfero site
  2 +=========================================
  3 +
  4 +Varnish is a HTTP caching server, and using it together with Noosfero is highly recommended. See http://www.varnish-cache.org/ for more information on Varnish.
  5 +
  6 +Varnish can be set up to use with Noosfero with the following steps:
  7 +
  8 +1) setup Noosfero with apache according to the `INSTALL.md` file. If you used the Debian package to install noosfero, you don't need to do anything about this.
  9 +
  10 +2) install Varnish
  11 +
  12 + # apt-get install varnish
  13 +
  14 +Install the RPAF apache module (or skip this step if not using apache):
  15 +
  16 + # apt-get install libapache2-mod-rpaf
  17 +
  18 +3) Change Apache to listen on port `8080` instead of `80`
  19 +
  20 +3a) Edit `/etc/apache2/ports.conf`, and:
  21 +
  22 + * change `NameVirtualHost *:80` to `NameVirtualHost *:8080`
  23 + * change `Listen 80` to `Listen 127.0.0.1:8080`
  24 +
  25 +3b) Edit `/etc/apache2/sites-enabled/*`, and change `<VirtualHost *:80>` to `<VirtualHost *:8080>`
  26 +
  27 +3c) Restart apache
  28 +
  29 + # invoke-rc.d apache2 restart
  30 +
  31 +4) Varnish configuration
  32 +
  33 +4a) Edit `/etc/default/varnish`
  34 +
  35 + * change the line that says `START=no` to say `START=yes`
  36 + * change `-a :6081` to `-a :80`
  37 +
  38 +4b) Edit `/etc/varnish/default.vcl` and add the following lines at the end:
  39 +
  40 + include "/etc/noosfero/varnish-noosfero.vcl";
  41 + include "/etc/noosfero/varnish-accept-language.vcl";
  42 +
  43 +On manual installations, change `/etc/noosfero/*` to `{Rails.root}/etc/noosfero/*`
  44 +
  45 +**NOTE**: it is very important that the `*.vcl` files are included in that order, i.e. *first* include `varnish-noosfero.vcl`, and *after* `noosfero-accept-language.cvl`.
  46 +
  47 +4c) Restart Varnish
  48 +
  49 + # invoke-rc.d varnish restart
  50 +
  51 +5) Enable varnish logging:
  52 +
  53 +5a) Edit `/etc/default/varnishncsa` and uncomment the line that contains:
  54 +
  55 + VARNISHNCSA_ENABLED=1
  56 +
  57 +The varnish log will be written to `/var/log/varnish/varnishncsa.log` in an apache-compatible format. You should change your statistics generation software (e.g. awstats) to use that instead of apache logs.
  58 +
  59 +5b) Restart Varnish Logging service
  60 +
  61 + # invoke-rc.d varnishncsa restart
  62 +
  63 +Thanks to Cosimo Streppone for varnish-accept-language. See http://github.com/cosimo/varnish-accept-language for more information.
@@ -1,33 +0,0 @@ @@ -1,33 +0,0 @@
1 -Noosfero - a web-based social platform  
2 -======================================  
3 -  
4 -http://www.noosfero.org/  
5 -  
6 -Documentation  
7 --------------  
8 -  
9 -The following documentation is available:  
10 -  
11 -File Purpose  
12 -~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
13 -INSTALL install instructions  
14 -INSTALL.awstats install instructions - access statistics service  
15 -INSTALL.chat install instructions - chat service  
16 -INSTALL.email install instructions - email service  
17 -INSTALL.multitenancy install instructions - multiple sites  
18 -INSTALL.varnish install instructions - varnish HTTP caching (recommended)  
19 -HACKING development instruction  
20 -RELEASING instructions for doing releases  
21 -doc/noosfero/* user documentation (available through the app itself)  
22 -  
23 -  
24 -Authors and copyright  
25 ----------------------  
26 -  
27 -Authorship and copyright information is available in the files listed below.  
28 -  
29 -File Purpose  
30 -~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
31 -AUTHORS list of authors (updated at each release)  
32 -COPYRIGHT Copyright statement for the project  
33 -COPYING Full text of the project license  
README.md 0 → 100644
@@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
  1 +Noosfero - a web-based social platform
  2 +======================================
  3 +
  4 +http://www.noosfero.org
  5 +
  6 +Documentation
  7 +-------------
  8 +
  9 +The following documentation is available:
  10 +
  11 + File Purpose
  12 + ----------------------- --------------------------------------------------------
  13 + INSTALL.md install instructions
  14 + INSTALL.awstats.md install instructions - access statistics service
  15 + INSTALL.chat.md install instructions - chat service
  16 + INSTALL.email.md install instructions - email service
  17 + INSTALL.multitenancy.md install instructions - multiple sites
  18 + INSTALL.varnish.md install instructions - varnish HTTP caching (recommended)
  19 + HACKING.md development instruction
  20 + RELEASING.md instructions for doing releases
  21 + doc/noosfero/* user documentation (available through the app itself)
  22 +
  23 +
  24 +Authors and copyright
  25 +---------------------
  26 +
  27 +Authorship and copyright information is available in the files listed below.
  28 +
  29 + File Purpose
  30 + -------------------- -----------------------------------------
  31 + AUTHORS.md list of authors (updated at each release)
  32 + COPYRIGHT Copyright statement for the project
  33 + COPYING Full text of the project license
README.rails
@@ -1,183 +0,0 @@ @@ -1,183 +0,0 @@
1 -== Welcome to Rails  
2 -  
3 -Rails is a web-application and persistence framework that includes everything  
4 -needed to create database-backed web-applications according to the  
5 -Model-View-Control pattern of separation. This pattern splits the view (also  
6 -called the presentation) into "dumb" templates that are primarily responsible  
7 -for inserting pre-built data in between HTML tags. The model contains the  
8 -"smart" domain objects (such as Account, Product, Person, Post) that holds all  
9 -the business logic and knows how to persist themselves to a database. The  
10 -controller handles the incoming requests (such as Save New Account, Update  
11 -Product, Show Post) by manipulating the model and directing data to the view.  
12 -  
13 -In Rails, the model is handled by what's called an object-relational mapping  
14 -layer entitled Active Record. This layer allows you to present the data from  
15 -database rows as objects and embellish these data objects with business logic  
16 -methods. You can read more about Active Record in  
17 -link:files/vendor/rails/activerecord/README.html.  
18 -  
19 -The controller and view are handled by the Action Pack, which handles both  
20 -layers by its two parts: Action View and Action Controller. These two layers  
21 -are bundled in a single package due to their heavy interdependence. This is  
22 -unlike the relationship between the Active Record and Action Pack that is much  
23 -more separate. Each of these packages can be used independently outside of  
24 -Rails. You can read more about Action Pack in  
25 -link:files/vendor/rails/actionpack/README.html.  
26 -  
27 -  
28 -== Getting started  
29 -  
30 -1. Start the web server: <tt>ruby script/server</tt> (run with --help for options)  
31 -2. Go to http://localhost:3000/ and get "Welcome aboard: You’re riding the Rails!"  
32 -3. Follow the guidelines to start developing your application  
33 -  
34 -  
35 -== Web servers  
36 -  
37 -Rails uses the built-in web server in Ruby called WEBrick by default, so you don't  
38 -have to install or configure anything to play around.  
39 -  
40 -If you have lighttpd installed, though, it'll be used instead when running script/server.  
41 -It's considerably faster than WEBrick and suited for production use, but requires additional  
42 -installation and currently only works well on OS X/Unix (Windows users are encouraged  
43 -to start with WEBrick). We recommend version 1.4.11 and higher. You can download it from  
44 -http://www.lighttpd.net.  
45 -  
46 -If you want something that's halfway between WEBrick and lighttpd, we heartily recommend  
47 -Mongrel. It's a Ruby-based web server with a C-component (so it requires compilation) that  
48 -also works very well with Windows. See more at http://mongrel.rubyforge.org/.  
49 -  
50 -But of course its also possible to run Rails with the premiere open source web server Apache.  
51 -To get decent performance, though, you'll need to install FastCGI. For Apache 1.3, you want  
52 -to use mod_fastcgi. For Apache 2.0+, you want to use mod_fcgid.  
53 -  
54 -See http://wiki.rubyonrails.com/rails/pages/FastCGI for more information on FastCGI.  
55 -  
56 -== Example for Apache conf  
57 -  
58 - <VirtualHost *:80>  
59 - ServerName rails  
60 - DocumentRoot /path/application/public/  
61 - ErrorLog /path/application/log/server.log  
62 -  
63 - <Directory /path/application/public/>  
64 - Options ExecCGI FollowSymLinks  
65 - AllowOverride all  
66 - Allow from all  
67 - Order allow,deny  
68 - </Directory>  
69 - </VirtualHost>  
70 -  
71 -NOTE: Be sure that CGIs can be executed in that directory as well. So ExecCGI  
72 -should be on and ".cgi" should respond. All requests from 127.0.0.1 go  
73 -through CGI, so no Apache restart is necessary for changes. All other requests  
74 -go through FCGI (or mod_ruby), which requires a restart to show changes.  
75 -  
76 -  
77 -== Debugging Rails  
78 -  
79 -Have "tail -f" commands running on both the server.log, production.log, and  
80 -test.log files. Rails will automatically display debugging and runtime  
81 -information to these files. Debugging info will also be shown in the browser  
82 -on requests from 127.0.0.1.  
83 -  
84 -  
85 -== Breakpoints  
86 -  
87 -Breakpoint support is available through the script/breakpointer client. This  
88 -means that you can break out of execution at any point in the code, investigate  
89 -and change the model, AND then resume execution! Example:  
90 -  
91 - class WeblogController < ActionController::Base  
92 - def index  
93 - @posts = Post.find_all  
94 - breakpoint "Breaking out from the list"  
95 - end  
96 - end  
97 -  
98 -So the controller will accept the action, run the first line, then present you  
99 -with a IRB prompt in the breakpointer window. Here you can do things like:  
100 -  
101 -Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'  
102 -  
103 - >> @posts.inspect  
104 - => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,  
105 - #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"  
106 - >> @posts.first.title = "hello from a breakpoint"  
107 - => "hello from a breakpoint"  
108 -  
109 -...and even better is that you can examine how your runtime objects actually work:  
110 -  
111 - >> f = @posts.first  
112 - => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>  
113 - >> f.  
114 - Display all 152 possibilities? (y or n)  
115 -  
116 -Finally, when you're ready to resume execution, you press CTRL-D  
117 -  
118 -  
119 -== Console  
120 -  
121 -You can interact with the domain model by starting the console through script/console.  
122 -Here you'll have all parts of the application configured, just like it is when the  
123 -application is running. You can inspect domain models, change values, and save to the  
124 -database. Starting the script without arguments will launch it in the development environment.  
125 -Passing an argument will specify a different environment, like <tt>script/console production</tt>.  
126 -  
127 -To reload your controllers and models after launching the console run <tt>reload!</tt>  
128 -  
129 -  
130 -  
131 -== Description of contents  
132 -  
133 -app  
134 - Holds all the code that's specific to this particular application.  
135 -  
136 -app/controllers  
137 - Holds controllers that should be named like weblog_controller.rb for  
138 - automated URL mapping. All controllers should descend from  
139 - ActionController::Base.  
140 -  
141 -app/models  
142 - Holds models that should be named like post.rb.  
143 - Most models will descend from ActiveRecord::Base.  
144 -  
145 -app/views  
146 - Holds the template files for the view that should be named like  
147 - weblog/index.rhtml for the WeblogController#index action. All views use eRuby  
148 - syntax. This directory can also be used to keep stylesheets, images, and so on  
149 - that can be symlinked to public.  
150 -  
151 -app/helpers  
152 - Holds view helpers that should be named like weblog_helper.rb.  
153 -  
154 -app/apis  
155 - Holds API classes for web services.  
156 -  
157 -config  
158 - Configuration files for the Rails environment, the routing map, the database, and other dependencies.  
159 -  
160 -components  
161 - Self-contained mini-applications that can bundle together controllers, models, and views.  
162 -  
163 -db  
164 - Contains the database schema in schema.rb. db/migrate contains all  
165 - the sequence of Migrations for your schema.  
166 -  
167 -lib  
168 - Application specific libraries. Basically, any kind of custom code that doesn't  
169 - belong under controllers, models, or helpers. This directory is in the load path.  
170 -  
171 -public  
172 - The directory available for the web server. Contains subdirectories for images, stylesheets,  
173 - and javascripts. Also contains the dispatchers and the default HTML files.  
174 -  
175 -script  
176 - Helper scripts for automation and generation.  
177 -  
178 -test  
179 - Unit and functional tests along with fixtures.  
180 -  
181 -vendor  
182 - External libraries that the application depends on. Also includes the plugins subdirectory.  
183 - This directory is in the load path.  
README.rails.md 0 → 100644
@@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
  1 +Welcome to Rails
  2 +================
  3 +
  4 +Rails is a web-application and persistence framework that includes everything needed to create database-backed web-applications according to the Model-View-Control pattern of separation. This pattern splits the view (also called the presentation) into "dumb" templates that are primarily responsible for inserting pre-built data in between HTML tags. The model contains the "smart" domain objects (such as Account, Product, Person, Post) that holds all the business logic and knows how to persist themselves to a database. The controller handles the incoming requests (such as Save New Account, Update Product, Show Post) by manipulating the model and directing data to the view.
  5 +
  6 +In Rails, the model is handled by what's called an object-relational mapping layer entitled Active Record. This layer allows you to present the data from database rows as objects and embellish these data objects with business logic methods. You can read more about Active Record in `.../rails/activerecord/README.html`.
  7 +
  8 +The controller and view are handled by the Action Pack, which handles both layers by its two parts: Action View and Action Controller. These two layers are bundled in a single package due to their heavy interdependence. This is unlike the relationship between the Active Record and Action Pack that is much more separate. Each of these packages can be used independently outside of Rails. You can read more about Action Pack in `.../rails/actionpack/README.html`.
  9 +
  10 +
  11 +Getting started
  12 +---------------
  13 +
  14 +1. Start the web server: `ruby script/server` (run with `--help` for options)
  15 +2. Go to http://localhost:3000/ and get "Welcome aboard: You’re riding the Rails!"
  16 +3. Follow the guidelines to start developing your application
  17 +
  18 +Web servers
  19 +-----------
  20 +
  21 +Rails uses the built-in web server in Ruby called WEBrick by default, so you don't have to install or configure anything to play around.
  22 +
  23 +If you have lighttpd installed, though, it'll be used instead when running script/server. It's considerably faster than WEBrick and suited for production use, but requires additional installation and currently only works well on OS X/Unix (Windows users are encouraged to start with WEBrick). We recommend version 1.4.11 and higher. You can download it from http://www.lighttpd.net.
  24 +
  25 +If you want something that's halfway between WEBrick and lighttpd, we heartily recommend Mongrel. It's a Ruby-based web server with a C-component (so it requires compilation) that also works very well with Windows. See more at http://mongrel.rubyforge.org/.
  26 +
  27 +But of course its also possible to run Rails with the premiere open source web server Apache. To get decent performance, though, you'll need to install FastCGI. For Apache 1.3, you want to use mod_fastcgi. For Apache 2.0+, you want to use mod_fcgid.
  28 +
  29 +See http://wiki.rubyonrails.com/rails/pages/FastCGI for more information on FastCGI.
  30 +
  31 +Example for Apache conf
  32 +-----------------------
  33 +
  34 + <VirtualHost *:80>
  35 + ServerName rails
  36 + DocumentRoot /path/application/public/
  37 + ErrorLog /path/application/log/server.log
  38 +
  39 + <Directory /path/application/public/>
  40 + Options ExecCGI FollowSymLinks
  41 + AllowOverride all
  42 + Allow from all
  43 + Order allow,deny
  44 + </Directory>
  45 + </VirtualHost>
  46 +
  47 +NOTE: Be sure that CGIs can be executed in that directory as well. So ExecCGI should be on and ".cgi" should respond. All requests from 127.0.0.1 go through CGI, so no Apache restart is necessary for changes. All other requests go through FCGI (or mod_ruby), which requires a restart to show changes.
  48 +
  49 +Debugging Rails
  50 +---------------
  51 +
  52 +Have "tail -f" commands running on both the server.log, production.log, and test.log files. Rails will automatically display debugging and runtime information to these files. Debugging info will also be shown in the browser on requests from 127.0.0.1.
  53 +
  54 +Breakpoints
  55 +-----------
  56 +
  57 +Breakpoint support is available through the script/breakpointer client. This means that you can break out of execution at any point in the code, investigate and change the model, AND then resume execution! Example:
  58 +
  59 + class WeblogController < ActionController::Base
  60 + def index
  61 + @posts = Post.find_all
  62 + breakpoint "Breaking out from the list"
  63 + end
  64 + end
  65 +
  66 +So the controller will accept the action, run the first line, then present you with a IRB prompt in the breakpointer window. Here you can do things like:
  67 +
  68 +Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint'
  69 +
  70 + >> @posts.inspect
  71 + => "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
  72 + #<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
  73 + >> @posts.first.title = "hello from a breakpoint"
  74 + => "hello from a breakpoint"
  75 +
  76 +...and even better is that you can examine how your runtime objects actually work:
  77 +
  78 + >> f = @posts.first
  79 + => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
  80 + >> f.
  81 + Display all 152 possibilities? (y or n)
  82 +
  83 +Finally, when you're ready to resume execution, you press CTRL-D
  84 +
  85 +Console
  86 +-------
  87 +
  88 +You can interact with the domain model by starting the console through script/console. Here you'll have all parts of the application configured, just like it is when the application is running. You can inspect domain models, change values, and save to the database. Starting the script without arguments will launch it in the development environment. Passing an argument will specify a different environment, like `script/console production`.
  89 +
  90 +To reload your controllers and models after launching the console run `reload!`
  91 +
  92 +Description of contents
  93 +-----------------------
  94 +
  95 +* `app`
  96 + Holds all the code that's specific to this particular application.
  97 +
  98 +* `app/controllers`
  99 + Holds controllers that should be named like weblog_controller.rb for automated URL mapping. All controllers should descend from `ActionController::Base`.
  100 +
  101 +* `app/models`
  102 + Holds models that should be named like post.rb. Most models will descend from `ActiveRecord::Base`.
  103 +
  104 +* `app/views`
  105 + Holds the template files for the view that should be named like `weblog/index.rhtml` for the `WeblogController#index` action. All views use eRuby syntax. This directory can also be used to keep stylesheets, images, and so on that can be symlinked to public.
  106 +
  107 +* `app/helpers`
  108 + Holds view helpers that should be named like `weblog_helper.rb`.
  109 +
  110 +* `app/apis`
  111 + Holds API classes for web services.
  112 +
  113 +* `config`
  114 + Configuration files for the Rails environment, the routing map, the database, and other dependencies.
  115 +
  116 +* `components`
  117 + Self-contained mini-applications that can bundle together controllers, models, and views.
  118 +
  119 +* `db`
  120 + Contains the database schema in `schema.rb`.
  121 +
  122 +* `db/migrate`
  123 + Contains all the sequence of Migrations for your schema.
  124 +
  125 +* `lib`
  126 + Application specific libraries. Basically, any kind of custom code that doesn't belong under controllers, models, or helpers. This directory is in the load path.
  127 +
  128 +* `public`
  129 + The directory available for the web server. Contains subdirectories for images, stylesheets, and javascripts. Also contains the dispatchers and the default HTML files.
  130 +
  131 +* `script`
  132 + Helper scripts for automation and generation.
  133 +
  134 +* `test`
  135 + Unit and functional tests along with fixtures.
  136 +
  137 +* `vendor`
  138 + External libraries that the application depends on. Also includes the plugins subdirectory. This directory is in the load path.
RELEASING
@@ -1,40 +0,0 @@ @@ -1,40 +0,0 @@
1 -= Noosfero release tasks  
2 -  
3 -This file documents release-related activities.  
4 -  
5 -== Working with translations  
6 -  
7 -* Update translation files: <tt>rake updatepo</tt>. Then <tt>git commit</tt> them.  
8 -* Send the PO files to the translators.  
9 -* Get the PO files back from translators, put in po/ under the correct language  
10 - name (e.,g. po/pt_BR/) and <tt>git commit</tt>.  
11 -* test translations: <tt>rake makemo</tt> and browse the application on the web.  
12 -  
13 -== Releasing noosfero  
14 -  
15 -Considering you are on a Debian GNU/Linux or Debian-based system  
16 - # apt-get install devscripts debhelper  
17 -  
18 -To prepare a release of noosfero, you must follow the steps below:  
19 -  
20 -* Finish all requirements and bugs assigned to the to-be-released version  
21 -* Make sure all tests pass  
22 -* Write release notes at the version's wiki topic  
23 -* Generate packages with <tt>rake noosfero:release[(stable|test)]</tt>. This task will:  
24 - * Update the version in lib/noosfero.rb and debian/changelog.  
25 - * Create the tarbal and the deb pkg under pkg/ directory.  
26 - * Create a git tag and push it.  
27 - * Upload the pkg to the configured repository (if configured) on ~/.dput.cf.  
28 -* Test that the tarball and deb package are ok  
29 -* Go to the version's wiki topic and edit it to reflect the new reality  
30 -* Edit the topic WebPreferences and update DEBIAN_REPOSITORY_TOPICS setting  
31 -* Attach the generated packages to that topic. Before attaching calculate the  
32 - sha1 of the package (with sha1sum and paste the SHA1 hash as comment in the  
33 - attachment form)  
34 -* Download the attached and verify the MD5 hash  
35 -* Update an eventual demonstration version that you run.  
36 -* Write an announcement e-mail to the relevant mailing lists pointing to the  
37 - release notes, and maybe to the demonstration version.  
38 -  
39 -If you had any problem during these steps, you can do <tt>rake clobber_package</tt> to  
40 -completely delete the generated packages and start the process again.  
RELEASING.md 0 → 100644
@@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
  1 +Noosfero release tasks
  2 +======================
  3 +
  4 +This file documents release-related activities.
  5 +
  6 +Working with translations
  7 +-------------------------
  8 +
  9 +* Update translation files: `rake updatepo`. Then `git commit` them.
  10 +* Send the PO files to the translators.
  11 +* Get the PO files back from translators, put in `po/` under the correct language name (e.,g. `po/pt_BR/`) and `git commit`.
  12 +* test translations: `rake makemo` and browse the application on the web.
  13 +
  14 +Releasing noosfero
  15 +------------------
  16 +
  17 +Considering you are on a Debian GNU/Linux or Debian-based system
  18 +
  19 + # apt-get install devscripts debhelper
  20 +
  21 +To prepare a release of noosfero, you must follow the steps below:
  22 +
  23 +* Finish all requirements and bugs assigned to the to-be-released version
  24 +* Make sure all tests pass
  25 +* Write release notes at the version's wiki topic
  26 +* Generate packages with `rake noosfero:release[(stable|test)]`. This task will:
  27 + * Update the version in lib/noosfero.rb and debian/changelog.
  28 + * Create the tarbal and the deb pkg under pkg/ directory.
  29 + * Create a git tag and push it.
  30 + * Upload the pkg to the configured repository (if configured) on ~/.dput.cf.
  31 +* Test that the tarball and deb package are ok
  32 +* Go to the version's wiki topic and edit it to reflect the new reality
  33 +* Edit the topic WebPreferences and update DEBIAN_REPOSITORY_TOPICS setting
  34 +* Attach the generated packages to that topic. Before attaching calculate the sha1 of the package (with sha1sum and paste the SHA1 hash as comment in the attachment form)
  35 +* Download the attached and verify the MD5 hash
  36 +* Update an eventual demonstration version that you run.
  37 +* Write an announcement e-mail to the relevant mailing lists pointing to the release notes, and maybe to the demonstration version.
  38 +
  39 +If you had any problem during these steps, you can do `rake clobber_package` to completely delete the generated packages and start the process again.
app/controllers/my_profile/cms_controller.rb
@@ -2,6 +2,8 @@ class CmsController &lt; MyProfileController @@ -2,6 +2,8 @@ class CmsController &lt; MyProfileController
2 2
3 protect 'edit_profile', :profile, :only => [:set_home_page] 3 protect 'edit_profile', :profile, :only => [:set_home_page]
4 4
  5 + include ArticleHelper
  6 +
5 def self.protect_if(*args) 7 def self.protect_if(*args)
6 before_filter(*args) do |c| 8 before_filter(*args) do |c|
7 user, profile = c.send(:user), c.send(:profile) 9 user, profile = c.send(:user), c.send(:profile)
@@ -63,12 +65,23 @@ class CmsController &lt; MyProfileController @@ -63,12 +65,23 @@ class CmsController &lt; MyProfileController
63 end 65 end
64 66
65 def edit 67 def edit
  68 + @success_back_to = params[:success_back_to]
66 @article = profile.articles.find(params[:id]) 69 @article = profile.articles.find(params[:id])
  70 + version = params[:version]
  71 + @article.revert_to(version) if version
  72 +
67 @parent_id = params[:parent_id] 73 @parent_id = params[:parent_id]
68 @type = params[:type] || @article.class.to_s 74 @type = params[:type] || @article.class.to_s
69 translations if @article.translatable? 75 translations if @article.translatable?
70 continue = params[:continue] 76 continue = params[:continue]
71 77
  78 + @article.article_privacy_exceptions = params[:q].split(/,/).map{|n| environment.people.find n.to_i} unless params[:q].nil?
  79 +
  80 + @tokenized_children = prepare_to_token_input(
  81 + profile.members.includes(:articles_with_access).find_all{ |m|
  82 + m.articles_with_access.include?(@article)
  83 + }
  84 + )
72 refuse_blocks 85 refuse_blocks
73 record_coming 86 record_coming
74 if request.post? 87 if request.post?
@@ -77,7 +90,7 @@ class CmsController &lt; MyProfileController @@ -77,7 +90,7 @@ class CmsController &lt; MyProfileController
77 if @article.update_attributes(params[:article]) 90 if @article.update_attributes(params[:article])
78 if !continue 91 if !continue
79 if @article.content_type.nil? || @article.image? 92 if @article.content_type.nil? || @article.image?
80 - redirect_to @article.view_url 93 + success_redirect
81 else 94 else
82 redirect_to :action => (@article.parent ? 'view' : 'index'), :id => @article.parent 95 redirect_to :action => (@article.parent ? 'view' : 'index'), :id => @article.parent
83 end 96 end
@@ -89,6 +102,7 @@ class CmsController &lt; MyProfileController @@ -89,6 +102,7 @@ class CmsController &lt; MyProfileController
89 def new 102 def new
90 # FIXME this method should share some logic wirh edit !!! 103 # FIXME this method should share some logic wirh edit !!!
91 104
  105 + @success_back_to = params[:success_back_to]
92 # user must choose an article type first 106 # user must choose an article type first
93 107
94 @parent = profile.articles.find(params[:parent_id]) if params && params[:parent_id] 108 @parent = profile.articles.find(params[:parent_id]) if params && params[:parent_id]
@@ -129,11 +143,13 @@ class CmsController &lt; MyProfileController @@ -129,11 +143,13 @@ class CmsController &lt; MyProfileController
129 143
130 continue = params[:continue] 144 continue = params[:continue]
131 if request.post? 145 if request.post?
  146 + @article.article_privacy_exceptions = params[:q].split(/,/).map{|n| environment.people.find n.to_i} unless params[:q].nil?
  147 +
132 if @article.save 148 if @article.save
133 if continue 149 if continue
134 redirect_to :action => 'edit', :id => @article 150 redirect_to :action => 'edit', :id => @article
135 else 151 else
136 - redirect_to @article.view_url 152 + success_redirect
137 end 153 end
138 return 154 return
139 end 155 end
@@ -188,7 +204,7 @@ class CmsController &lt; MyProfileController @@ -188,7 +204,7 @@ class CmsController &lt; MyProfileController
188 @article.destroy 204 @article.destroy
189 session[:notice] = _("\"#{@article.name}\" was removed.") 205 session[:notice] = _("\"#{@article.name}\" was removed.")
190 referer = Rails.application.routes.recognize_path URI.parse(request.referer).path rescue nil 206 referer = Rails.application.routes.recognize_path URI.parse(request.referer).path rescue nil
191 - if referer and referer[:controller] == 'cms' 207 + if referer and referer[:controller] == 'cms' and referer[:action] != 'edit'
192 redirect_to referer 208 redirect_to referer
193 elsif @article.parent 209 elsif @article.parent
194 redirect_to @article.parent.url 210 redirect_to @article.parent.url
@@ -289,6 +305,12 @@ class CmsController &lt; MyProfileController @@ -289,6 +305,12 @@ class CmsController &lt; MyProfileController
289 render :text => article_list_to_json(results), :content_type => 'application/json' 305 render :text => article_list_to_json(results), :content_type => 'application/json'
290 end 306 end
291 307
  308 + def search_article_privacy_exceptions
  309 + arg = params[:q].downcase
  310 + result = profile.members.find(:all, :conditions => ['LOWER(name) LIKE ?', "%#{arg}%"])
  311 + render :text => prepare_to_token_input(result).to_json
  312 + end
  313 +
292 def media_upload 314 def media_upload
293 files_uploaded = [] 315 files_uploaded = []
294 parent = check_parent(params[:parent_id]) 316 parent = check_parent(params[:parent_id])
@@ -385,5 +407,12 @@ class CmsController &lt; MyProfileController @@ -385,5 +407,12 @@ class CmsController &lt; MyProfileController
385 true 407 true
386 end 408 end
387 409
388 -end 410 + def success_redirect
  411 + if !@success_back_to.blank?
  412 + redirect_to @success_back_to
  413 + else
  414 + redirect_to @article.view_url
  415 + end
  416 + end
389 417
  418 +end
app/controllers/my_profile/profile_design_controller.rb
@@ -32,6 +32,7 @@ class ProfileDesignController &lt; BoxOrganizerController @@ -32,6 +32,7 @@ class ProfileDesignController &lt; BoxOrganizerController
32 if profile.enterprise? 32 if profile.enterprise?
33 blocks << DisabledEnterpriseMessageBlock 33 blocks << DisabledEnterpriseMessageBlock
34 blocks << HighlightsBlock 34 blocks << HighlightsBlock
  35 + blocks << ProductCategoriesBlock
35 blocks << FeaturedProductsBlock 36 blocks << FeaturedProductsBlock
36 blocks << FansBlock 37 blocks << FansBlock
37 blocks += plugins.dispatch(:extra_blocks, :type => Enterprise) 38 blocks += plugins.dispatch(:extra_blocks, :type => Enterprise)
@@ -54,4 +55,10 @@ class ProfileDesignController &lt; BoxOrganizerController @@ -54,4 +55,10 @@ class ProfileDesignController &lt; BoxOrganizerController
54 blocks 55 blocks
55 end 56 end
56 57
  58 + def clone
  59 + block = Block.find(params[:id])
  60 + block.duplicate
  61 + redirect_to :action => 'index'
  62 + end
  63 +
57 end 64 end
app/controllers/public/catalog_controller.rb
1 class CatalogController < PublicController 1 class CatalogController < PublicController
2 needs_profile 2 needs_profile
3 - no_design_blocks  
4 3
5 before_filter :check_enterprise_and_environment 4 before_filter :check_enterprise_and_environment
6 5
app/controllers/public/content_viewer_controller.rb
@@ -7,6 +7,7 @@ class ContentViewerController &lt; ApplicationController @@ -7,6 +7,7 @@ class ContentViewerController &lt; ApplicationController
7 7
8 def view_page 8 def view_page
9 path = params[:page] 9 path = params[:page]
  10 + @version = params[:version].to_i
10 11
11 if path.blank? 12 if path.blank?
12 @page = profile.home_page 13 @page = profile.home_page
@@ -25,31 +26,29 @@ class ContentViewerController &lt; ApplicationController @@ -25,31 +26,29 @@ class ContentViewerController &lt; ApplicationController
25 end 26 end
26 end 27 end
27 28
28 - if !@page.nil? && !@page.display_to?(user)  
29 - if !profile.public?  
30 - private_profile_partial_parameters  
31 - render :template => 'profile/_private_profile.rhtml', :status => 403  
32 - else #if !profile.visible?  
33 - message = _('You are not allowed to view this content.')  
34 - message += ' ' + _('You can contact the owner of this profile to request access then.')  
35 - render_access_denied(message) 29 + return unless allow_access_to_page(path)
  30 +
  31 + if @version > 0
  32 + return render_access_denied unless @page.display_versions?
  33 + @versioned_article = @page.versions.find_by_version(@version)
  34 + if @versioned_article && @page.versions.latest.version != @versioned_article.version
  35 + render :template => 'content_viewer/versioned_article.rhtml'
  36 + return
36 end 37 end
37 - return  
38 end 38 end
39 39
40 - # page not found, give error  
41 - if @page.nil?  
42 - render_not_found(@path)  
43 - return  
44 - end 40 + redirect_to_translation if @page.profile.redirect_l10n
45 41
46 - if request.xhr? && params[:toolbar]  
47 - render :partial => 'article_toolbar'  
48 - return 42 + if request.post?
  43 + if @page.forum? && @page.has_terms_of_use && params[:terms_accepted] == "true"
  44 + @page.add_agreed_user(user)
  45 + end
  46 + elsif !@page.parent.nil? && @page.parent.forum?
  47 + unless @page.parent.agrees_with_terms?(user)
  48 + redirect_to @page.parent.url
  49 + end
49 end 50 end
50 51
51 - redirect_to_translation if @page.profile.redirect_l10n  
52 -  
53 # At this point the page will be showed 52 # At this point the page will be showed
54 @page.hit 53 @page.hit
55 54
@@ -118,6 +117,15 @@ class ContentViewerController &lt; ApplicationController @@ -118,6 +117,15 @@ class ContentViewerController &lt; ApplicationController
118 end 117 end
119 end 118 end
120 119
  120 + def article_versions
  121 + path = params[:page].join('/')
  122 + @page = profile.articles.find_by_path(path)
  123 + return unless allow_access_to_page(path)
  124 +
  125 + render_access_denied unless @page.display_versions?
  126 + @versions = @page.versions.paginate(:per_page => per_page, :page => params[:npage])
  127 + end
  128 +
121 protected 129 protected
122 130
123 def per_page 131 def per_page
@@ -148,4 +156,22 @@ class ContentViewerController &lt; ApplicationController @@ -148,4 +156,22 @@ class ContentViewerController &lt; ApplicationController
148 end 156 end
149 helper_method :pass_without_comment_captcha? 157 helper_method :pass_without_comment_captcha?
150 158
  159 + def allow_access_to_page(path)
  160 + allowed = true
  161 + if @page.nil? # page not found, give error
  162 + render_not_found(path)
  163 + allowed = false
  164 + elsif !@page.display_to?(user)
  165 + if !profile.public?
  166 + private_profile_partial_parameters
  167 + render :template => 'profile/_private_profile.rhtml', :status => 403
  168 + allowed = false
  169 + else #if !profile.visible?
  170 + render_access_denied
  171 + allowed = false
  172 + end
  173 + end
  174 + allowed
  175 + end
  176 +
151 end 177 end
app/helpers/application_helper.rb
@@ -42,7 +42,9 @@ module ApplicationHelper @@ -42,7 +42,9 @@ module ApplicationHelper
42 42
43 include Noosfero::Gravatar 43 include Noosfero::Gravatar
44 44
45 - VIEW_EXTENSIONS = ['.rhtml', '.html.erb'] 45 + include TokenHelper
  46 +
  47 + include CatalogHelper
46 48
47 def locale 49 def locale
48 (@page && !@page.language.blank?) ? @page.language : FastGettext.locale 50 (@page && !@page.language.blank?) ? @page.language : FastGettext.locale
@@ -293,10 +295,8 @@ module ApplicationHelper @@ -293,10 +295,8 @@ module ApplicationHelper
293 search_name = "_" + search_name 295 search_name = "_" + search_name
294 end 296 end
295 297
296 - VIEW_EXTENSIONS.each do |ext|  
297 - path = defined?(params) && params[:controller] ? File.join(view_path, params[:controller], search_name + ext) : File.join(view_path, search_name + ext)  
298 - return name if File.exists?(File.join(path))  
299 - end 298 + path = defined?(params) && params[:controller] ? File.join(view_path, params[:controller], search_name + 'html.erb') : File.join(view_path, search_name + 'html.erb')
  299 + return name if File.exists?(File.join(path))
300 300
301 partial_for_class_in_view_path(klass.superclass, view_path, prefix, suffix) 301 partial_for_class_in_view_path(klass.superclass, view_path, prefix, suffix)
302 end 302 end
@@ -403,17 +403,16 @@ module ApplicationHelper @@ -403,17 +403,16 @@ module ApplicationHelper
403 def theme_view_file(template) 403 def theme_view_file(template)
404 # Since we cannot control what people are doing in external themes, we 404 # Since we cannot control what people are doing in external themes, we
405 # will keep looking for the deprecated .rhtml extension here. 405 # will keep looking for the deprecated .rhtml extension here.
406 - VIEW_EXTENSIONS.each do |ext|  
407 - file = Rails.root.join('public', theme_path[1..-1], template + ext)  
408 - return file if File.exists?(file)  
409 - end 406 + file = Rails.root.join('public', theme_path[1..-1], template + 'html.erb')
  407 + return file if File.exists?(file)
410 nil 408 nil
411 end 409 end
412 410
413 - def theme_include(template) 411 + def theme_include(template, options = {})
414 file = theme_view_file(template) 412 file = theme_view_file(template)
  413 + options.merge!({:file => file, :use_full_path => false})
415 if file 414 if file
416 - render :file => file, :use_full_path => false 415 + render options
417 else 416 else
418 nil 417 nil
419 end 418 end
@@ -1314,10 +1313,6 @@ module ApplicationHelper @@ -1314,10 +1313,6 @@ module ApplicationHelper
1314 content_tag(:div, content_tag(:ul, titles) + raw(contents), :class => 'ui-tabs') 1313 content_tag(:div, content_tag(:ul, titles) + raw(contents), :class => 'ui-tabs')
1315 end 1314 end
1316 1315
1317 - def jquery_token_input_messages_json(hintText = _('Type in an keyword'), noResultsText = _('No results'), searchingText = _('Searching...'))  
1318 - "hintText: '#{hintText}', noResultsText: '#{noResultsText}', searchingText: '#{searchingText}'"  
1319 - end  
1320 -  
1321 def delete_article_message(article) 1316 def delete_article_message(article)
1322 if article.folder? 1317 if article.folder?
1323 _("Are you sure that you want to remove the folder \"#{article.name}\"? Note that all the items inside it will also be removed!") 1318 _("Are you sure that you want to remove the folder \"#{article.name}\"? Note that all the items inside it will also be removed!")
@@ -1358,50 +1353,6 @@ module ApplicationHelper @@ -1358,50 +1353,6 @@ module ApplicationHelper
1358 ) 1353 )
1359 end 1354 end
1360 1355
1361 - def token_input_field_tag(name, element_id, search_action, options = {}, text_field_options = {}, html_options = {})  
1362 - options[:min_chars] ||= 3  
1363 - options[:hint_text] ||= _("Type in a search term")  
1364 - options[:no_results_text] ||= _("No results")  
1365 - options[:searching_text] ||= _("Searching...")  
1366 - options[:search_delay] ||= 1000  
1367 - options[:prevent_duplicates] ||= true  
1368 - options[:backspace_delete_item] ||= false  
1369 - options[:focus] ||= false  
1370 - options[:avoid_enter] ||= true  
1371 - options[:on_result] ||= 'null'  
1372 - options[:on_add] ||= 'null'  
1373 - options[:on_delete] ||= 'null'  
1374 - options[:on_ready] ||= 'null'  
1375 -  
1376 - result = text_field_tag(name, nil, text_field_options.merge(html_options.merge({:id => element_id})))  
1377 - result += javascript_tag("jQuery('##{element_id}')  
1378 - .tokenInput('#{url_for(search_action)}', {  
1379 - minChars: #{options[:min_chars].to_json},  
1380 - prePopulate: #{options[:pre_populate].to_json},  
1381 - hintText: #{options[:hint_text].to_json},  
1382 - noResultsText: #{options[:no_results_text].to_json},  
1383 - searchingText: #{options[:searching_text].to_json},  
1384 - searchDelay: #{options[:serach_delay].to_json},  
1385 - preventDuplicates: #{options[:prevent_duplicates].to_json},  
1386 - backspaceDeleteItem: #{options[:backspace_delete_item].to_json},  
1387 - queryParam: #{name.to_json},  
1388 - tokenLimit: #{options[:token_limit].to_json},  
1389 - onResult: #{options[:on_result]},  
1390 - onAdd: #{options[:on_add]},  
1391 - onDelete: #{options[:on_delete]},  
1392 - onReady: #{options[:on_ready]},  
1393 - });  
1394 - ")  
1395 - result += javascript_tag("jQuery('##{element_id}').focus();") if options[:focus]  
1396 - if options[:avoid_enter]  
1397 - result += javascript_tag("jQuery('#token-input-#{element_id}')  
1398 - .live('keydown', function(event){  
1399 - if(event.keyCode == '13') return false;  
1400 - });")  
1401 - end  
1402 - result  
1403 - end  
1404 -  
1405 def expirable_content_reference(content, action, text, url, options = {}) 1356 def expirable_content_reference(content, action, text, url, options = {})
1406 reason = @plugins.dispatch("content_expire_#{action.to_s}", content).first 1357 reason = @plugins.dispatch("content_expire_#{action.to_s}", content).first
1407 options[:title] = reason 1358 options[:title] = reason
@@ -1459,16 +1410,16 @@ module ApplicationHelper @@ -1459,16 +1410,16 @@ module ApplicationHelper
1459 end 1410 end
1460 1411
1461 def convert_macro(html, source) 1412 def convert_macro(html, source)
1462 - doc = Hpricot(html) 1413 + doc = Nokogiri::HTML(html)
1463 #TODO This way is more efficient but do not support macro inside of 1414 #TODO This way is more efficient but do not support macro inside of
1464 # macro. You must parse them from the inside-out in order to enable 1415 # macro. You must parse them from the inside-out in order to enable
1465 # that. 1416 # that.
1466 - doc.search('.macro').each do |macro| 1417 + doc.css('.macro').each do |macro|
1467 macro_name = macro['data-macro'] 1418 macro_name = macro['data-macro']
1468 result = @plugins.parse_macro(macro_name, macro, source) 1419 result = @plugins.parse_macro(macro_name, macro, source)
1469 - macro.inner_html = result.kind_of?(Proc) ? self.instance_eval(&result) : result 1420 + macro.content = result.kind_of?(Proc) ? self.instance_eval(&result) : result
1470 end 1421 end
1471 - doc.html 1422 + CGI.unescapeHTML(doc.xpath('//body/*').to_s)
1472 end 1423 end
1473 1424
1474 def default_folder_for_image_upload(profile) 1425 def default_folder_for_image_upload(profile)
@@ -1481,4 +1432,8 @@ module ApplicationHelper @@ -1481,4 +1432,8 @@ module ApplicationHelper
1481 content.nil? ? '' : content.id.to_s 1432 content.nil? ? '' : content.id.to_s
1482 end 1433 end
1483 1434
  1435 + def display_article_versions(article, version = nil)
  1436 + content_tag('ul', article.versions.map {|v| link_to("r#{v.version}", @page.url.merge(:version => v.version))})
  1437 + end
  1438 +
1484 end 1439 end
app/helpers/article_helper.rb
1 module ArticleHelper 1 module ArticleHelper
2 2
3 include PrototypeHelper 3 include PrototypeHelper
  4 + include TokenHelper
4 5
5 - def custom_options_for_article(article) 6 + def custom_options_for_article(article, tokenized_children)
6 @article = article 7 @article = article
7 - content_tag('h4', _('Visibility')) +  
8 - content_tag('div',  
9 - content_tag('div',  
10 - radio_button(:article, :published, true) +  
11 - content_tag('label', _('Public (visible to other people)'), :for => 'article_published_true')  
12 - ) +  
13 - content_tag('div',  
14 - radio_button(:article, :published, false) +  
15 - content_tag('label', _('Private'), :for => 'article_published_false')  
16 - )  
17 - ) + 8 +
  9 + visibility_options(@article, tokenized_children) +
18 content_tag('h4', _('Options')) + 10 content_tag('h4', _('Options')) +
19 content_tag('div', 11 content_tag('div',
20 (article.profile.has_members? ? 12 (article.profile.has_members? ?
@@ -37,7 +29,7 @@ module ArticleHelper @@ -37,7 +29,7 @@ module ArticleHelper
37 'div', 29 'div',
38 check_box(:article, :notify_comments) + 30 check_box(:article, :notify_comments) +
39 content_tag('label', _('I want to receive a notification of each comment written by e-mail'), :for => 'article_notify_comments') + 31 content_tag('label', _('I want to receive a notification of each comment written by e-mail'), :for => 'article_notify_comments') +
40 - observe_field(:article_accept_comments, :function => "$('article_notify_comments').disabled = ! $('article_accept_comments').checked;$('article_moderate_comments').disabled = ! $('article_accept_comments').checked") 32 + observe_field(:article_accept_comments, :function => "$('article_notify_comments').disabled = ! $('article_accept_comments').checked;$('article_moderate_comments').disabled = ! $('article_accept_comments').checked")
41 ) + 33 ) +
42 34
43 content_tag( 35 content_tag(
@@ -51,10 +43,40 @@ module ArticleHelper @@ -51,10 +43,40 @@ module ArticleHelper
51 'div', 43 'div',
52 check_box(:article, :display_hits) + 44 check_box(:article, :display_hits) +
53 content_tag('label', _('I want this article to display the number of hits it received'), :for => 'article_display_hits') 45 content_tag('label', _('I want this article to display the number of hits it received'), :for => 'article_display_hits')
  46 + ) : '') +
  47 +
  48 + (article.can_display_versions? ?
  49 + content_tag(
  50 + 'div',
  51 + check_box(:article, :display_versions) +
  52 + content_tag('label', _('I want this article to display a link to older versions'), :for => 'article_display_versions')
54 ) : '') 53 ) : '')
  54 +
55 ) 55 )
56 end 56 end
57 57
  58 + def visibility_options(article, tokenized_children)
  59 + content_tag('h4', _('Visibility')) +
  60 + content_tag('div',
  61 + content_tag('div',
  62 + radio_button(:article, :published, true) +
  63 + content_tag('label', _('Public (visible to other people)'), :for => 'article_published_true')
  64 + ) +
  65 + content_tag('div',
  66 + radio_button(:article, :published, false) +
  67 + content_tag('label', _('Private'), :for => 'article_published_false', :id => "label_private")
  68 + ) +
  69 + (article.profile.community? ? content_tag('div',
  70 + content_tag('label', _('Fill in the search field to add the exception users to see this content'), :id => "text-input-search-exception-users") +
  71 + token_input_field_tag(:q, 'search-article-privacy-exceptions', {:action => 'search_article_privacy_exceptions'},
  72 + {:focus => false, :hint_text => _('Type in a search term for a user'), :pre_populate => tokenized_children})) :
  73 + ''))
  74 + end
  75 +
  76 + def prepare_to_token_input(array)
  77 + array.map { |object| {:id => object.id, :name => object.name} }
  78 + end
  79 +
58 def cms_label_for_new_children 80 def cms_label_for_new_children
59 _('New article') 81 _('New article')
60 end 82 end
app/helpers/blog_helper.rb
1 module BlogHelper 1 module BlogHelper
2 2
3 - def custom_options_for_article(article) 3 + include ArticleHelper
  4 +
  5 + def custom_options_for_article(article,tokenized_children)
4 @article = article 6 @article = article
5 hidden_field_tag('article[published]', 1) + 7 hidden_field_tag('article[published]', 1) +
6 - hidden_field_tag('article[accept_comments]', 0) 8 + hidden_field_tag('article[accept_comments]', 0) +
  9 + visibility_options(article,tokenized_children)
7 end 10 end
8 11
9 def cms_label_for_new_children 12 def cms_label_for_new_children
app/helpers/box_organizer_helper.rb
@@ -4,4 +4,10 @@ module BoxOrganizerHelper @@ -4,4 +4,10 @@ module BoxOrganizerHelper
4 render :partial => 'icon_selector', :locals => { :icon => icon } 4 render :partial => 'icon_selector', :locals => { :icon => icon }
5 end 5 end
6 6
7 -end 7 + def extra_option_checkbox(option)
  8 + if [:human_name, :name, :value, :checked, :options].all? {|k| option.key? k}
  9 + labelled_check_box(option[:human_name], option[:name], option[:value], option[:checked], option[:options])
  10 + end
  11 + end
  12 +
  13 +end
8 \ No newline at end of file 14 \ No newline at end of file
app/helpers/boxes_helper.rb
@@ -65,7 +65,7 @@ module BoxesHelper @@ -65,7 +65,7 @@ module BoxesHelper
65 end 65 end
66 66
67 def display_box_content(box, main_content) 67 def display_box_content(box, main_content)
68 - context = { :article => @page, :request_path => request.path, :locale => locale } 68 + context = { :article => @page, :request_path => request.path, :locale => locale, :params => request.params }
69 box_decorator.select_blocks(box.blocks.includes(:box), context).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box) 69 box_decorator.select_blocks(box.blocks.includes(:box), context).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box)
70 end 70 end
71 71
@@ -212,6 +212,7 @@ module BoxesHelper @@ -212,6 +212,7 @@ module BoxesHelper
212 212
213 if !block.main? 213 if !block.main?
214 buttons << icon_button(:delete, _('Remove block'), { :action => 'remove', :id => block.id }, { :method => 'post', :confirm => _('Are you sure you want to remove this block?')}) 214 buttons << icon_button(:delete, _('Remove block'), { :action => 'remove', :id => block.id }, { :method => 'post', :confirm => _('Are you sure you want to remove this block?')})
  215 + buttons << icon_button(:clone, _('Clone'), { :action => 'clone', :id => block.id }, { :method => 'post' })
215 end 216 end
216 217
217 if block.respond_to?(:help) 218 if block.respond_to?(:help)
app/helpers/catalog_helper.rb
@@ -9,7 +9,9 @@ module CatalogHelper @@ -9,7 +9,9 @@ module CatalogHelper
9 @categories = ProductCategory.on_level(params[:level]).order(:name) 9 @categories = ProductCategory.on_level(params[:level]).order(:name)
10 end 10 end
11 11
12 - @products = profile.products.from_category(@category).reorder('available desc, highlighted desc, name asc').paginate(:per_page => 9, :page => options[:page]) 12 + @products = profile.products.from_category(@category).
  13 + reorder('available desc, highlighted desc, name asc').
  14 + paginate(:per_page => @profile.products_per_catalog_page, :page => options[:page])
13 end 15 end
14 16
15 def breadcrumb(category) 17 def breadcrumb(category)
@@ -20,20 +22,24 @@ module CatalogHelper @@ -20,20 +22,24 @@ module CatalogHelper
20 content_tag('div', all_items.join(' &rarr; '), :id => 'breadcrumb') 22 content_tag('div', all_items.join(' &rarr; '), :id => 'breadcrumb')
21 end 23 end
22 24
23 - def category_link(category, sub = false) 25 + def category_link(category)
24 count = profile.products.from_category(category).count 26 count = profile.products.from_category(category).count
25 name = truncate(category.name, :length => 22 - count.to_s.size) 27 name = truncate(category.name, :length => 22 - count.to_s.size)
26 - link_name = sub ? name : content_tag('strong', name)  
27 - link = link_to(link_name, {:controller => :catalog, :action => 'index', :level => category.id}, :title => category.name)  
28 - content_tag('li', "#{link} (#{count})") if count > 0 28 + link = link_to(name, {:controller => 'catalog', :action => 'index', :level => category.id}, :title => category.name)
  29 + content_tag('div', "#{link} <span class=\"count\">#{count}</span>") if count > 0
29 end 30 end
30 31
31 - def category_sub_links(category) 32 + def category_with_sub_list(category)
  33 + content_tag 'li', "#{category_link(category)}\n#{sub_category_list(category)}"
  34 + end
  35 +
  36 + def sub_category_list(category)
32 sub_categories = [] 37 sub_categories = []
33 category.children.order(:name).each do |sub_category| 38 category.children.order(:name).each do |sub_category|
34 - sub_categories << category_link(sub_category, true) 39 + cat_link = category_link sub_category
  40 + sub_categories << content_tag('li', cat_link) unless cat_link.nil?
35 end 41 end
36 - content_tag('ul', sub_categories.join) if sub_categories.size > 1 42 + content_tag('ul', sub_categories.join) if sub_categories.size > 0
37 end 43 end
38 44
39 end 45 end
app/helpers/cms_helper.rb
@@ -22,9 +22,9 @@ module CmsHelper @@ -22,9 +22,9 @@ module CmsHelper
22 22
23 attr_reader :environment 23 attr_reader :environment
24 24
25 - def options_for_article(article) 25 + def options_for_article(article, tokenized_children=nil)
26 article_helper = helper_for_article(article) 26 article_helper = helper_for_article(article)
27 - article_helper.custom_options_for_article(article) 27 + article_helper.custom_options_for_article(article, tokenized_children)
28 end 28 end
29 29
30 def link_to_article(article) 30 def link_to_article(article)
app/helpers/comment_helper.rb
@@ -25,7 +25,10 @@ module CommentHelper @@ -25,7 +25,10 @@ module CommentHelper
25 def comment_actions(comment) 25 def comment_actions(comment)
26 url = url_for(:profile => profile.identifier, :controller => :comment, :action => :check_actions, :id => comment.id) 26 url = url_for(:profile => profile.identifier, :controller => :comment, :action => :check_actions, :id => comment.id)
27 links = links_for_comment_actions(comment) 27 links = links_for_comment_actions(comment)
28 - content_tag(:li, link_to(content_tag(:span, _('Contents menu')), '#', :onclick => "toggleSubmenu(this,'',#{j links.to_json}); return false", :class => 'menu-submenu-trigger comment-trigger', :url => url), :class=> 'vcard') unless links.empty? 28 + links_submenu = links.select{|link| link[:action_bar].blank?}
  29 + links_action_bar = links - links_submenu
  30 + links_submenu = links_submenu.collect {|link| link.slice(:link)}
  31 + render :partial => 'comment/comment_actions', :locals => {:links_submenu => links_submenu, :links_action_bar => links_action_bar, :url => url, :comment => comment}
29 end 32 end
30 33
31 private 34 private
app/helpers/folder_helper.rb
@@ -3,6 +3,7 @@ require &#39;short_filename&#39; @@ -3,6 +3,7 @@ require &#39;short_filename&#39;
3 module FolderHelper 3 module FolderHelper
4 4
5 include ShortFilename 5 include ShortFilename
  6 + include ArticleHelper
6 7
7 def list_articles(articles, recursive = false) 8 def list_articles(articles, recursive = false)
8 if !articles.blank? 9 if !articles.blank?
@@ -62,19 +63,10 @@ module FolderHelper @@ -62,19 +63,10 @@ module FolderHelper
62 "icon-new icon-new%s" % klass.icon_name 63 "icon-new icon-new%s" % klass.icon_name
63 end 64 end
64 65
65 - def custom_options_for_article(article) 66 + def custom_options_for_article(article,tokenized_children)
66 @article = article 67 @article = article
67 - content_tag('h4', _('Visibility')) +  
68 - content_tag('div',  
69 - content_tag('div',  
70 - radio_button(:article, :published, true) +  
71 - content_tag('label', _('Public (visible to other people)'), :for => 'article_published_true')  
72 - ) +  
73 - content_tag('div',  
74 - radio_button(:article, :published, false) +  
75 - content_tag('label', _('Private'), :for => 'article_published_false')  
76 - )  
77 - ) + 68 +
  69 + visibility_options(article,tokenized_children) +
78 content_tag('div', 70 content_tag('div',
79 hidden_field_tag('article[accept_comments]', 0) 71 hidden_field_tag('article[accept_comments]', 0)
80 ) 72 )
app/helpers/profile_helper.rb
@@ -16,14 +16,17 @@ module ProfileHelper @@ -16,14 +16,17 @@ module ProfileHelper
16 end 16 end
17 17
18 def display_contact(profile) 18 def display_contact(profile)
19 - address = display_field(_('Address:'), profile, :address)  
20 - zip = display_field(_('ZIP code:'), profile, :zip_code)  
21 - phone = display_field(_('Contact phone:'), profile, :contact_phone)  
22 - email = display_field(_('e-Mail:'), profile, :email) { |email| link_to_email(email) }  
23 - if address.blank? && zip.blank? && phone.blank? && email.blank? 19 + fields = []
  20 + fields << display_field(_('Address:'), profile, :address).html_safe
  21 + fields << display_field(_('ZIP code:'), profile, :zip_code).html_safe
  22 + fields << display_field(_('Contact phone:'), profile, :contact_phone).html_safe
  23 + fields << display_field(_('e-Mail:'), profile, :email) { |email| link_to_email(email) }.html_safe
  24 + fields << display_field(_('Personal website:'), profile, :personal_website).html_safe
  25 + fields << display_field(_('Jabber:'), profile, :jabber_id).html_safe
  26 + if fields.reject!(&:blank?).empty?
24 '' 27 ''
25 else 28 else
26 - content_tag('tr', content_tag('th', _('Contact'), { :colspan => 2 })) + address + zip + phone + email 29 + content_tag('tr', content_tag('th', _('Contact'), { :colspan => 2 })) + fields.join.html_safe
27 end 30 end
28 end 31 end
29 32
app/helpers/token_helper.rb 0 → 100644
@@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
  1 +module TokenHelper
  2 +
  3 + def jquery_token_input_messages_json(hintText = _('Type in an keyword'), noResultsText = _('No results'), searchingText = _('Searching...'))
  4 + "hintText: '#{hintText}', noResultsText: '#{noResultsText}', searchingText: '#{searchingText}'"
  5 + end
  6 +
  7 + def token_input_field_tag(name, element_id, search_action, options = {}, text_field_options = {}, html_options = {})
  8 + options[:min_chars] ||= 3
  9 + options[:hint_text] ||= _("Type in a search term")
  10 + options[:no_results_text] ||= _("No results")
  11 + options[:searching_text] ||= _("Searching...")
  12 + options[:search_delay] ||= 1000
  13 + options[:prevent_duplicates] ||= true
  14 + options[:backspace_delete_item] ||= false
  15 + options[:focus] ||= false
  16 + options[:avoid_enter] ||= true
  17 + options[:on_result] ||= 'null'
  18 + options[:on_add] ||= 'null'
  19 + options[:on_delete] ||= 'null'
  20 + options[:on_ready] ||= 'null'
  21 +
  22 + result = text_field_tag(name, nil, text_field_options.merge(html_options.merge({:id => element_id})))
  23 + result += javascript_tag("jQuery('##{element_id}')
  24 + .tokenInput('#{url_for(search_action)}', {
  25 + minChars: #{options[:min_chars].to_json},
  26 + prePopulate: #{options[:pre_populate].to_json},
  27 + hintText: #{options[:hint_text].to_json},
  28 + noResultsText: #{options[:no_results_text].to_json},
  29 + searchingText: #{options[:searching_text].to_json},
  30 + searchDelay: #{options[:serach_delay].to_json},
  31 + preventDuplicates: #{options[:prevent_duplicates].to_json},
  32 + backspaceDeleteItem: #{options[:backspace_delete_item].to_json},
  33 + queryParam: #{name.to_json},
  34 + tokenLimit: #{options[:token_limit].to_json},
  35 + onResult: #{options[:on_result]},
  36 + onAdd: #{options[:on_add]},
  37 + onDelete: #{options[:on_delete]},
  38 + onReady: #{options[:on_ready]},
  39 + });
  40 + ")
  41 + result += javascript_tag("jQuery('##{element_id}').focus();") if options[:focus]
  42 + if options[:avoid_enter]
  43 + result += javascript_tag("jQuery('#token-input-#{element_id}')
  44 + .live('keydown', function(event){
  45 + if(event.keyCode == '13') return false;
  46 + });")
  47 + end
  48 + result
  49 + end
  50 +
  51 +end
0 \ No newline at end of file 52 \ No newline at end of file
app/models/article.rb
@@ -71,6 +71,7 @@ class Article &lt; ActiveRecord::Base @@ -71,6 +71,7 @@ class Article &lt; ActiveRecord::Base
71 settings_items :allow_members_to_edit, :type => :boolean, :default => false 71 settings_items :allow_members_to_edit, :type => :boolean, :default => false
72 settings_items :moderate_comments, :type => :boolean, :default => false 72 settings_items :moderate_comments, :type => :boolean, :default => false
73 settings_items :followers, :type => Array, :default => [] 73 settings_items :followers, :type => Array, :default => []
  74 + has_and_belongs_to_many :article_privacy_exceptions, :class_name => 'Person', :join_table => 'article_privacy_exceptions'
74 75
75 belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id' 76 belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id'
76 77
@@ -202,6 +203,7 @@ class Article &lt; ActiveRecord::Base @@ -202,6 +203,7 @@ class Article &lt; ActiveRecord::Base
202 acts_as_filesystem 203 acts_as_filesystem
203 204
204 acts_as_versioned 205 acts_as_versioned
  206 + self.non_versioned_columns << 'setting'
205 207
206 def comment_data 208 def comment_data
207 comments.map {|item| [item.title, item.body].join(' ') }.join(' ') 209 comments.map {|item| [item.title, item.body].join(' ') }.join(' ')
@@ -448,8 +450,8 @@ class Article &lt; ActiveRecord::Base @@ -448,8 +450,8 @@ class Article &lt; ActiveRecord::Base
448 end 450 end
449 451
450 scope :published, :conditions => { :published => true } 452 scope :published, :conditions => { :published => true }
451 - scope :folders, :conditions => { :type => folder_types}  
452 - scope :no_folders, :conditions => ['type NOT IN (?)', folder_types] 453 + scope :folders, lambda {|profile|{:conditions => { :type => profile.folder_types} }}
  454 + scope :no_folders, lambda {|profile|{:conditions => ['type NOT IN (?)', profile.folder_types]}}
453 scope :galleries, :conditions => { :type => 'Gallery' } 455 scope :galleries, :conditions => { :type => 'Gallery' }
454 scope :images, :conditions => { :is_image => true } 456 scope :images, :conditions => { :is_image => true }
455 scope :text_articles, :conditions => [ 'articles.type IN (?)', text_article_types ] 457 scope :text_articles, :conditions => [ 'articles.type IN (?)', text_article_types ]
@@ -470,7 +472,8 @@ class Article &lt; ActiveRecord::Base @@ -470,7 +472,8 @@ class Article &lt; ActiveRecord::Base
470 472
471 def display_unpublished_article_to?(user) 473 def display_unpublished_article_to?(user)
472 user == author || allow_view_private_content?(user) || user == profile || 474 user == author || allow_view_private_content?(user) || user == profile ||
473 - user.is_admin?(profile.environment) || user.is_admin?(profile) 475 + user.is_admin?(profile.environment) || user.is_admin?(profile) ||
  476 + article_privacy_exceptions.include?(user)
474 end 477 end
475 478
476 def display_to?(user = nil) 479 def display_to?(user = nil)
@@ -612,17 +615,34 @@ class Article &lt; ActiveRecord::Base @@ -612,17 +615,34 @@ class Article &lt; ActiveRecord::Base
612 false 615 false
613 end 616 end
614 617
615 - def author  
616 - if versions.empty?  
617 - last_changed_by  
618 - else  
619 - author_id = versions.first.last_changed_by_id 618 + settings_items :display_versions, :type => :boolean, :default => false
  619 +
  620 + def can_display_versions?
  621 + false
  622 + end
  623 +
  624 + def display_versions?
  625 + can_display_versions? && display_versions
  626 + end
  627 +
  628 + def author(version_number = nil)
  629 + if version_number
  630 + version = versions.find_by_version(version_number)
  631 + author_id = version.last_changed_by_id if version
620 Person.exists?(author_id) ? Person.find(author_id) : nil 632 Person.exists?(author_id) ? Person.find(author_id) : nil
  633 + else
  634 + if versions.empty?
  635 + last_changed_by
  636 + else
  637 + author_id = versions.first.last_changed_by_id
  638 + Person.exists?(author_id) ? Person.find(author_id) : nil
  639 + end
621 end 640 end
622 end 641 end
623 642
624 - def author_name  
625 - author ? author.name : (setting[:author_name] || _('Unknown')) 643 + def author_name(version_number = nil)
  644 + person = version_number ? author(version_number) : author
  645 + person ? person.name : (setting[:author_name] || _('Unknown'))
626 end 646 end
627 647
628 def author_url 648 def author_url
@@ -633,13 +653,20 @@ class Article &lt; ActiveRecord::Base @@ -633,13 +653,20 @@ class Article &lt; ActiveRecord::Base
633 author ? author.id : nil 653 author ? author.id : nil
634 end 654 end
635 655
  656 + def version_license(version_number = nil)
  657 + return license if version_number.nil?
  658 + profile.environment.licenses.find_by_id(versions.find_by_version(version_number).license_id)
  659 + end
  660 +
636 alias :active_record_cache_key :cache_key 661 alias :active_record_cache_key :cache_key
637 def cache_key(params = {}, the_profile = nil, language = 'en') 662 def cache_key(params = {}, the_profile = nil, language = 'en')
638 active_record_cache_key+'-'+language + 663 active_record_cache_key+'-'+language +
639 (allow_post_content?(the_profile) ? "-owner" : '') + 664 (allow_post_content?(the_profile) ? "-owner" : '') +
640 (params[:npage] ? "-npage-#{params[:npage]}" : '') + 665 (params[:npage] ? "-npage-#{params[:npage]}" : '') +
641 (params[:year] ? "-year-#{params[:year]}" : '') + 666 (params[:year] ? "-year-#{params[:year]}" : '') +
642 - (params[:month] ? "-month-#{params[:month]}" : '') 667 + (params[:month] ? "-month-#{params[:month]}" : '') +
  668 + (params[:version] ? "-version-#{params[:version]}" : '')
  669 +
643 end 670 end
644 671
645 def first_paragraph 672 def first_paragraph
app/models/block.rb
@@ -25,30 +25,41 @@ class Block &lt; ActiveRecord::Base @@ -25,30 +25,41 @@ class Block &lt; ActiveRecord::Base
25 # * <tt>:article</tt>: the article being viewed currently 25 # * <tt>:article</tt>: the article being viewed currently
26 # * <tt>:language</tt>: in which language the block will be displayed 26 # * <tt>:language</tt>: in which language the block will be displayed
27 def visible?(context = nil) 27 def visible?(context = nil)
28 - if display == 'never'  
29 - return false  
30 - end 28 + return false if display == 'never'
  29 +
31 if context 30 if context
32 - if language != 'all' && language != context[:locale]  
33 - return false  
34 - end  
35 - if display == 'home_page_only'  
36 - if context[:article]  
37 - return context[:article] == owner.home_page  
38 - else  
39 - return context[:request_path] == '/'  
40 - end  
41 - elsif display == 'except_home_page'  
42 - if context[:article]  
43 - return context[:article] != owner.home_page  
44 - else  
45 - return context[:request_path] != '/' + owner.identifier  
46 - end 31 + return false if language != 'all' && language != context[:locale]
  32 +
  33 + begin
  34 + return self.send("display_#{display}", context)
  35 + rescue NoMethodError => exception
  36 + raise "Display '#{display}' is not a valid value."
47 end 37 end
48 end 38 end
  39 +
  40 + true
  41 + end
  42 +
  43 + def display_always(context)
49 true 44 true
50 end 45 end
51 46
  47 + def display_home_page_only(context)
  48 + if context[:article]
  49 + return context[:article] == owner.home_page
  50 + else
  51 + return context[:request_path] == '/'
  52 + end
  53 + end
  54 +
  55 + def display_except_home_page(context)
  56 + if context[:article]
  57 + return context[:article] != owner.home_page
  58 + else
  59 + return context[:request_path] != '/' + (owner.kind_of?(Profile) ? owner.identifier : '')
  60 + end
  61 + end
  62 +
52 # The condition for displaying a block. It can assume the following values: 63 # The condition for displaying a block. It can assume the following values:
53 # 64 #
54 # * <tt>'always'</tt>: the block is always displayed 65 # * <tt>'always'</tt>: the block is always displayed
@@ -155,4 +166,29 @@ class Block &lt; ActiveRecord::Base @@ -155,4 +166,29 @@ class Block &lt; ActiveRecord::Base
155 } 166 }
156 end 167 end
157 168
  169 + DISPLAY_OPTIONS = {
  170 + 'always' => __('In all pages'),
  171 + 'home_page_only' => __('Only in the homepage'),
  172 + 'except_home_page' => __('In all pages, except in the homepage'),
  173 + 'never' => __('Don\'t display'),
  174 + }
  175 +
  176 + def display_options
  177 + DISPLAY_OPTIONS.keys
  178 + end
  179 +
  180 + def display_option_label(option)
  181 + DISPLAY_OPTIONS[option]
  182 + end
  183 +
  184 + def duplicate
  185 + duplicated_block = self.clone
  186 + duplicated_block.display = 'never'
  187 + duplicated_block.created_at = nil
  188 + duplicated_block.updated_at = nil
  189 + duplicated_block.save!
  190 + duplicated_block.insert_at(self.position + 1)
  191 + duplicated_block
  192 + end
  193 +
158 end 194 end
app/models/blog.rb
@@ -8,7 +8,7 @@ class Blog &lt; Folder @@ -8,7 +8,7 @@ class Blog &lt; Folder
8 #FIXME This should be used until there is a migration to fix all blogs that 8 #FIXME This should be used until there is a migration to fix all blogs that
9 # already have folders inside them 9 # already have folders inside them
10 def posts_with_no_folders 10 def posts_with_no_folders
11 - posts_without_no_folders.no_folders 11 + posts_without_no_folders.no_folders(profile)
12 end 12 end
13 alias_method_chain :posts, :no_folders 13 alias_method_chain :posts, :no_folders
14 14
app/models/box.rb
@@ -64,6 +64,7 @@ class Box &lt; ActiveRecord::Base @@ -64,6 +64,7 @@ class Box &lt; ActiveRecord::Base
64 MyNetworkBlock, 64 MyNetworkBlock,
65 PeopleBlock, 65 PeopleBlock,
66 ProductsBlock, 66 ProductsBlock,
  67 + ProductCategoriesBlock,
67 ProfileImageBlock, 68 ProfileImageBlock,
68 ProfileInfoBlock, 69 ProfileInfoBlock,
69 ProfileSearchBlock, 70 ProfileSearchBlock,
app/models/enterprise.rb
@@ -10,7 +10,7 @@ class Enterprise &lt; Organization @@ -10,7 +10,7 @@ class Enterprise &lt; Organization
10 10
11 N_('Enterprise') 11 N_('Enterprise')
12 12
13 - has_many :products, :dependent => :destroy, :order => 'name ASC' 13 + has_many :products, :foreign_key => :profile_id, :dependent => :destroy, :order => 'name ASC'
14 has_many :inputs, :through => :products 14 has_many :inputs, :through => :products
15 has_many :production_costs, :as => :owner 15 has_many :production_costs, :as => :owner
16 16
@@ -23,6 +23,7 @@ class Enterprise &lt; Organization @@ -23,6 +23,7 @@ class Enterprise &lt; Organization
23 N_('Organization website'); N_('Historic and current context'); N_('Activities short description'); N_('City'); N_('State'); N_('Country'); N_('ZIP code') 23 N_('Organization website'); N_('Historic and current context'); N_('Activities short description'); N_('City'); N_('State'); N_('Country'); N_('ZIP code')
24 24
25 settings_items :organization_website, :historic_and_current_context, :activities_short_description 25 settings_items :organization_website, :historic_and_current_context, :activities_short_description
  26 + settings_items :products_per_catalog_page, :type => :integer, :default => 6
26 27
27 extend SetProfileRegionFromCityState::ClassMethods 28 extend SetProfileRegionFromCityState::ClassMethods
28 set_profile_region_from_city_state 29 set_profile_region_from_city_state
@@ -132,8 +133,11 @@ class Enterprise &lt; Organization @@ -132,8 +133,11 @@ class Enterprise &lt; Organization
132 ] 133 ]
133 blocks = [ 134 blocks = [
134 [MainBlock.new], 135 [MainBlock.new],
135 - [ProfileImageBlock.new, LinkListBlock.new(:links => links)],  
136 - [] 136 + [ ProfileImageBlock.new,
  137 + LinkListBlock.new(:links => links),
  138 + ProductCategoriesBlock.new
  139 + ],
  140 + [LocationBlock.new]
137 ] 141 ]
138 if environment.enabled?('products_for_enterprises') 142 if environment.enabled?('products_for_enterprises')
139 blocks[2].unshift ProductsBlock.new 143 blocks[2].unshift ProductsBlock.new
app/models/environment.rb
@@ -681,7 +681,8 @@ class Environment &lt; ActiveRecord::Base @@ -681,7 +681,8 @@ class Environment &lt; ActiveRecord::Base
681 end 681 end
682 682
683 def community_template 683 def community_template
684 - Community.find_by_id settings[:community_template_id] 684 + template = Community.find_by_id settings[:community_template_id]
  685 + template if template && template.is_template
685 end 686 end
686 687
687 def community_template=(value) 688 def community_template=(value)
@@ -689,7 +690,8 @@ class Environment &lt; ActiveRecord::Base @@ -689,7 +690,8 @@ class Environment &lt; ActiveRecord::Base
689 end 690 end
690 691
691 def person_template 692 def person_template
692 - Person.find_by_id settings[:person_template_id] 693 + template = Person.find_by_id settings[:person_template_id]
  694 + template if template && template.is_template
693 end 695 end
694 696
695 def person_template=(value) 697 def person_template=(value)
@@ -697,7 +699,8 @@ class Environment &lt; ActiveRecord::Base @@ -697,7 +699,8 @@ class Environment &lt; ActiveRecord::Base
697 end 699 end
698 700
699 def enterprise_template 701 def enterprise_template
700 - Enterprise.find_by_id settings[:enterprise_template_id] 702 + template = Enterprise.find_by_id settings[:enterprise_template_id]
  703 + template if template && template.is_template
701 end 704 end
702 705
703 def enterprise_template=(value) 706 def enterprise_template=(value)
@@ -705,7 +708,8 @@ class Environment &lt; ActiveRecord::Base @@ -705,7 +708,8 @@ class Environment &lt; ActiveRecord::Base
705 end 708 end
706 709
707 def inactive_enterprise_template 710 def inactive_enterprise_template
708 - Enterprise.find_by_id settings[:inactive_enterprise_template_id] 711 + template = Enterprise.find_by_id settings[:inactive_enterprise_template_id]
  712 + template if template && template.is_template
709 end 713 end
710 714
711 def inactive_enterprise_template=(value) 715 def inactive_enterprise_template=(value)
@@ -824,7 +828,7 @@ class Environment &lt; ActiveRecord::Base @@ -824,7 +828,7 @@ class Environment &lt; ActiveRecord::Base
824 end 828 end
825 829
826 def highlighted_products_with_image(options = {}) 830 def highlighted_products_with_image(options = {})
827 - Product.find(:all, {:conditions => {:highlighted => true, :enterprise_id => self.enterprises.find(:all, :select => :id) }, :joins => :image}.merge(options)) 831 + Product.find(:all, {:conditions => {:highlighted => true, :profile_id => self.enterprises.find(:all, :select => :id) }, :joins => :image}.merge(options))
828 end 832 end
829 833
830 settings_items :home_cache_in_minutes, :type => :integer, :default => 5 834 settings_items :home_cache_in_minutes, :type => :integer, :default => 5
app/models/forum.rb
@@ -3,6 +3,21 @@ class Forum &lt; Folder @@ -3,6 +3,21 @@ class Forum &lt; Folder
3 acts_as_having_posts :order => 'updated_at DESC' 3 acts_as_having_posts :order => 'updated_at DESC'
4 include PostsLimit 4 include PostsLimit
5 5
  6 + settings_items :terms_of_use, :type => :string, :default => ""
  7 + settings_items :has_terms_of_use, :type => :boolean, :default => false
  8 + has_and_belongs_to_many :users_with_agreement, :class_name => 'Person', :join_table => 'terms_forum_people'
  9 +
  10 + before_save do |forum|
  11 + if forum.has_terms_of_use
  12 + last_editor = forum.profile.environment.people.find_by_id(forum.last_changed_by_id)
  13 + if last_editor && !forum.users_with_agreement.exists?(last_editor)
  14 + forum.users_with_agreement << last_editor
  15 + end
  16 + else
  17 + forum.users_with_agreement.clear
  18 + end
  19 + end
  20 +
6 def self.type_name 21 def self.type_name
7 _('Forum') 22 _('Forum')
8 end 23 end
@@ -39,4 +54,16 @@ class Forum &lt; Folder @@ -39,4 +54,16 @@ class Forum &lt; Folder
39 paragraphs = Hpricot(body).search('p') 54 paragraphs = Hpricot(body).search('p')
40 paragraphs.empty? ? '' : paragraphs.first.to_html 55 paragraphs.empty? ? '' : paragraphs.first.to_html
41 end 56 end
  57 +
  58 + def add_agreed_user(user)
  59 + self.users_with_agreement << user
  60 + self.save
  61 + end
  62 +
  63 + def agrees_with_terms?(user)
  64 + return true unless self.has_terms_of_use
  65 + return false unless user
  66 + self.users_with_agreement.exists? user
  67 + end
  68 +
42 end 69 end
app/models/main_block.rb
@@ -17,11 +17,15 @@ class MainBlock &lt; Block @@ -17,11 +17,15 @@ class MainBlock &lt; Block
17 end 17 end
18 18
19 def editable? 19 def editable?
20 - false 20 + true
21 end 21 end
22 22
23 def cacheable? 23 def cacheable?
24 - false 24 + false
  25 + end
  26 +
  27 + def display_options
  28 + ['always', 'except_home_page']
25 end 29 end
26 30
27 end 31 end
app/models/members_block.rb
1 class MembersBlock < ProfileListBlock 1 class MembersBlock < ProfileListBlock
  2 + settings_items :show_join_leave_button, :type => :boolean, :default => false
2 3
3 def self.description 4 def self.description
4 _('Members') 5 _('Members')
@@ -14,8 +15,10 @@ class MembersBlock &lt; ProfileListBlock @@ -14,8 +15,10 @@ class MembersBlock &lt; ProfileListBlock
14 15
15 def footer 16 def footer
16 profile = self.owner 17 profile = self.owner
  18 + s = show_join_leave_button
  19 +
17 proc do 20 proc do
18 - link_to _('View all'), :profile => profile.identifier, :controller => 'profile', :action => 'members' 21 + render :file => 'blocks/members', :locals => { :profile => profile, :show_join_leave_button => s}
19 end 22 end
20 end 23 end
21 24
@@ -23,4 +26,14 @@ class MembersBlock &lt; ProfileListBlock @@ -23,4 +26,14 @@ class MembersBlock &lt; ProfileListBlock
23 owner.members 26 owner.members
24 end 27 end
25 28
  29 + def extra_option
  30 + data = {
  31 + :human_name => _("Show join leave button"),
  32 + :name => 'block[show_join_leave_button]',
  33 + :value => true,
  34 + :checked => show_join_leave_button,
  35 + :options => {}
  36 + }
  37 + end
  38 +
26 end 39 end
app/models/person.rb
@@ -31,7 +31,12 @@ class Person &lt; Profile @@ -31,7 +31,12 @@ class Person &lt; Profile
31 alias_method_chain :has_permission?, :plugins 31 alias_method_chain :has_permission?, :plugins
32 32
33 def memberships 33 def memberships
34 - Profile.memberships_of(self) 34 + scopes = []
  35 + plugins_scopes = plugins.dispatch_scopes(:person_memberships, self)
  36 + scopes = plugins_scopes unless plugins_scopes.first.blank?
  37 + scopes << Profile.memberships_of(self)
  38 + return scopes.first if scopes.size == 1
  39 + ScopeTool.union *scopes
35 end 40 end
36 41
37 def memberships_by_role(role) 42 def memberships_by_role(role)
@@ -51,6 +56,9 @@ class Person &lt; Profile @@ -51,6 +56,9 @@ class Person &lt; Profile
51 56
52 has_many :scraps_sent, :class_name => 'Scrap', :foreign_key => :sender_id, :dependent => :destroy 57 has_many :scraps_sent, :class_name => 'Scrap', :foreign_key => :sender_id, :dependent => :destroy
53 58
  59 + has_and_belongs_to_many :acepted_forums, :class_name => 'Forum', :join_table => 'terms_forum_people'
  60 + has_and_belongs_to_many :articles_with_access, :class_name => 'Article', :join_table => 'article_privacy_exceptions'
  61 +
54 scope :more_popular, 62 scope :more_popular,
55 :select => "#{Profile.qualified_column_names}, count(friend_id) as total", 63 :select => "#{Profile.qualified_column_names}, count(friend_id) as total",
56 :group => Profile.qualified_column_names, 64 :group => Profile.qualified_column_names,
@@ -128,32 +136,34 @@ class Person &lt; Profile @@ -128,32 +136,34 @@ class Person &lt; Profile
128 end 136 end
129 137
130 FIELDS = %w[ 138 FIELDS = %w[
  139 + description
  140 + image
131 preferred_domain 141 preferred_domain
132 nickname 142 nickname
133 sex 143 sex
134 - address  
135 - zip_code  
136 - city  
137 - state  
138 - country  
139 - nationality  
140 birth_date 144 birth_date
  145 + nationality
  146 + country
  147 + state
  148 + city
  149 + district
  150 + zip_code
  151 + address
  152 + address_reference
141 cell_phone 153 cell_phone
142 comercial_phone 154 comercial_phone
  155 + personal_website
  156 + jabber_id
143 schooling 157 schooling
  158 + formation
  159 + custom_formation
  160 + area_of_study
  161 + custom_area_of_study
144 professional_activity 162 professional_activity
145 organization 163 organization
146 organization_website 164 organization_website
147 - area_of_study  
148 - custom_area_of_study  
149 - formation  
150 - custom_formation  
151 contact_phone 165 contact_phone
152 contact_information 166 contact_information
153 - description  
154 - image  
155 - district  
156 - address_reference  
157 ] 167 ]
158 168
159 validates_multiparameter_assignments 169 validates_multiparameter_assignments
app/models/product.rb
@@ -17,7 +17,11 @@ class Product &lt; ActiveRecord::Base @@ -17,7 +17,11 @@ class Product &lt; ActiveRecord::Base
17 'full' 17 'full'
18 end 18 end
19 19
20 - belongs_to :enterprise 20 + belongs_to :enterprise, :foreign_key => :profile_id, :class_name => 'Profile'
  21 + belongs_to :profile
  22 + alias_method :enterprise=, :profile=
  23 + alias_method :enterprise, :profile
  24 +
21 has_one :region, :through => :enterprise 25 has_one :region, :through => :enterprise
22 validates_presence_of :enterprise 26 validates_presence_of :enterprise
23 27
@@ -31,7 +35,10 @@ class Product &lt; ActiveRecord::Base @@ -31,7 +35,10 @@ class Product &lt; ActiveRecord::Base
31 has_many :qualifiers, :through => :product_qualifiers 35 has_many :qualifiers, :through => :product_qualifiers
32 has_many :certifiers, :through => :product_qualifiers 36 has_many :certifiers, :through => :product_qualifiers
33 37
34 - validates_uniqueness_of :name, :scope => :enterprise_id, :allow_nil => true 38 + acts_as_having_settings :field => :data
  39 +
  40 + validates_uniqueness_of :name, :scope => :profile_id, :allow_nil => true, :if => :validate_uniqueness_of_column_name?
  41 +
35 validates_presence_of :product_category_id 42 validates_presence_of :product_category_id
36 validates_associated :product_category 43 validates_associated :product_category
37 44
@@ -248,4 +255,10 @@ class Product &lt; ActiveRecord::Base @@ -248,4 +255,10 @@ class Product &lt; ActiveRecord::Base
248 255
249 delegate :enabled, :region, :region_id, :environment, :environment_id, :to => :enterprise 256 delegate :enabled, :region, :region_id, :environment, :environment_id, :to => :enterprise
250 257
  258 + protected
  259 +
  260 + def validate_uniqueness_of_column_name?
  261 + true
  262 + end
  263 +
251 end 264 end
app/models/product_categories_block.rb 0 → 100644
@@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
  1 +class ProductCategoriesBlock < Block
  2 +
  3 + def self.description
  4 + _('Product category menu')
  5 + end
  6 +
  7 + # the title of the block. Probably will be overriden in subclasses.
  8 + def default_title
  9 + _('Catalog')
  10 + end
  11 +
  12 + def help
  13 + _('Helps to filter the products catalog.')
  14 + end
  15 +
  16 + def content(args={})
  17 + profile = owner
  18 + lambda do
  19 + if @categories.nil? or @categories.length == 0
  20 + categories = ProductCategory.on_level().order(:name)
  21 + if @categories and @categories.length == 0
  22 + notice = _('There are no sub-categories for %s') % @category.name
  23 + end
  24 + else
  25 + categories = @categories
  26 + end
  27 + render :file => 'blocks/product_categories',
  28 + :locals => {
  29 + :profile => profile,
  30 + :categories => categories,
  31 + :notice => notice
  32 + }
  33 + end
  34 + end
  35 +
  36 + DISPLAY_OPTIONS['catalog_only'] = __('Only on the catalog')
  37 +
  38 + def display
  39 + settings[:display].nil? ? 'catalog_only' : super
  40 + end
  41 +
  42 + def display_catalog_only(context)
  43 + context[:params][:controller] == 'catalog'
  44 + end
  45 +
  46 + def visible?(*args)
  47 + box.environment.enabled?('products_for_enterprises') ? super(*args) : false
  48 + end
  49 +
  50 +end
app/models/profile.rb
@@ -92,15 +92,21 @@ class Profile &lt; ActiveRecord::Base @@ -92,15 +92,21 @@ class Profile &lt; ActiveRecord::Base
92 def members 92 def members
93 scopes = plugins.dispatch_scopes(:organization_members, self) 93 scopes = plugins.dispatch_scopes(:organization_members, self)
94 scopes << Person.members_of(self) 94 scopes << Person.members_of(self)
95 -  
96 - clauses = scopes.map do |relation|  
97 - clause = relation.arel.where_clauses.map { |clause| "(#{clause})" }.join(' AND ')  
98 - "(#{clause})"  
99 - end.join(' OR ')  
100 -  
101 - joins = scopes.map { |relation| relation.joins_values }.flatten.uniq  
102 - selects = scopes.map { |relation| relation.select_values }.flatten.uniq  
103 - Person.select(selects).joins(joins).where(clauses) 95 +# FIXME Review this change after the refactoring on or_scopes, now done through the ScopeTool.
  96 +#<<<<<<< HEAD
  97 +#
  98 +# clauses = scopes.map do |relation|
  99 +# clause = relation.arel.where_clauses.map { |clause| "(#{clause})" }.join(' AND ')
  100 +# "(#{clause})"
  101 +# end.join(' OR ')
  102 +#
  103 +# joins = scopes.map { |relation| relation.joins_values }.flatten.uniq
  104 +# selects = scopes.map { |relation| relation.select_values }.flatten.uniq
  105 +# Person.select(selects).joins(joins).where(clauses)
  106 +#=======
  107 + return scopes.first if scopes.size == 1
  108 + ScopeTool.union *scopes
  109 +#>>>>>>> master
104 end 110 end
105 111
106 def members_count 112 def members_count
@@ -779,8 +785,20 @@ private :generate_url, :url_options @@ -779,8 +785,20 @@ private :generate_url, :url_options
779 !environment.enabled?('disable_contact_' + self.class.name.downcase) 785 !environment.enabled?('disable_contact_' + self.class.name.downcase)
780 end 786 end
781 787
  788 + include Noosfero::Plugin::HotSpot
  789 +
  790 + def folder_types
  791 + types = Article.folder_types
  792 + plugins.dispatch(:content_types).each {|type|
  793 + if type < Folder
  794 + types << type.name
  795 + end
  796 + }
  797 + types
  798 + end
  799 +
782 def folders 800 def folders
783 - articles.folders 801 + articles.folders(self)
784 end 802 end
785 803
786 def image_galleries 804 def image_galleries
app/models/profile_list_block.rb
@@ -64,4 +64,8 @@ class ProfileListBlock &lt; Block @@ -64,4 +64,8 @@ class ProfileListBlock &lt; Block
64 title.gsub('{#}', profile_count.to_s) 64 title.gsub('{#}', profile_count.to_s)
65 end 65 end
66 66
  67 + # override in subclasses! See MembersBlock for example
  68 + def extra_option
  69 + {}
  70 + end
67 end 71 end
app/models/text_article.rb
@@ -19,4 +19,7 @@ class TextArticle &lt; Article @@ -19,4 +19,7 @@ class TextArticle &lt; Article
19 end 19 end
20 end 20 end
21 21
  22 + def can_display_versions?
  23 + true
  24 + end
22 end 25 end
app/views/blocks/members.rhtml 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +<%= link_to _('View all'), :profile => profile.identifier, :controller => 'profile', :action => 'members' %>
  2 +
  3 +<% if show_join_leave_button %>
  4 + <%= render :partial => 'blocks/profile_info_actions/join_leave_community' %>
  5 +<% end %>
app/views/blocks/product_categories.html.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<%= link_to _('Catalog start'), profile.catalog_url, :class=>'catalog-home-link' %>
  2 +<ul class="catalog-categories-list">
  3 + <% categories.each do |category| %>
  4 + <%= category_with_sub_list(category) %>
  5 + <% end %>
  6 +</ul>
  7 +<% if notice %>
  8 + <div class="catalog-categories-notice"><%= notice %></div>
  9 +<% end %>
app/views/blocks/profile_image.html.erb
@@ -14,11 +14,13 @@ @@ -14,11 +14,13 @@
14 <p><%= h block.owner.short_name %></p> 14 <p><%= h block.owner.short_name %></p>
15 <% end %> 15 <% end %>
16 16
17 -<div style="text-align: center; font-size: 75%; clear: both" id="profile-admin-url-<%= block.id %>"></div>  
18 -  
19 -<div class="profile-info-options" id="profile-info-options-<%= block.id %>"></div> 17 +<% if !user.nil? and user.has_permission?('edit_profile', profile) %>
  18 + <div class='admin-link'>
  19 + <%= link_to _('Control panel'), :controller => 'profile_editor' %>
  20 + </div>
  21 +<% end %>
20 22
  23 +<div class="profile-info-options">
  24 + <%= render :file => view_for_profile_actions(block.owner.class) %>
  25 +</div>
21 </div><!-- end class="vcard" --> 26 </div><!-- end class="vcard" -->
22 -<script type="text/javascript">  
23 - <%= remote_function :url => { :controller => 'profile', :profile => profile.identifier, :action => 'profile_info', :block_id => block.id } %>  
24 -</script>  
app/views/blocks/profile_info.html.erb
@@ -14,13 +14,15 @@ @@ -14,13 +14,15 @@
14 </div> 14 </div>
15 </div> 15 </div>
16 16
17 -<ul class="profile-info-data" id="profile-info-data-<%= block.id %>"> 17 +<ul class="profile-info-data">
18 <li><%= link_to _('Homepage'), block.owner.url, :class => 'url' %></li> 18 <li><%= link_to _('Homepage'), block.owner.url, :class => 'url' %></li>
19 <li><%= link_to _('View profile'), block.owner.public_profile_url %></li> 19 <li><%= link_to _('View profile'), block.owner.public_profile_url %></li>
20 <% if block.owner.enterprise? && block.owner.environment.enabled?('products_for_enterprises') %> 20 <% if block.owner.enterprise? && block.owner.environment.enabled?('products_for_enterprises') %>
21 <li><%= link_to(_('Products/Services'), :controller => 'catalog', :profile => block.owner.identifier) %></li> 21 <li><%= link_to(_('Products/Services'), :controller => 'catalog', :profile => block.owner.identifier) %></li>
22 <% end %> 22 <% end %>
23 - <li id="profile-admin-url-<%= block.id %>"></li> 23 + <% if !user.nil? and user.has_permission?('edit_profile', profile) %>
  24 + <li><%= link_to _('Control panel'), :controller => 'profile_editor' %></li>
  25 + <% end %>
24 <% if profile.person? %> 26 <% if profile.person? %>
25 <li><%= _('Since %{year}/%{month}') % { :year => block.owner.created_at.year, :month => block.owner.created_at.month } %></li> 27 <li><%= _('Since %{year}/%{month}') % { :year => block.owner.created_at.year, :month => block.owner.created_at.month } %></li>
26 <% end %> 28 <% end %>
@@ -37,9 +39,8 @@ @@ -37,9 +39,8 @@
37 </div> 39 </div>
38 <% end %> 40 <% end %>
39 41
40 -<div class="profile-info-options" id="profile-info-options-<%= block.id %>"></div> 42 +<div class="profile-info-options">
  43 + <%= render :file => view_for_profile_actions(@block.owner.class) %>
  44 +</div>
41 45
42 </div><!-- end class="vcard" --> 46 </div><!-- end class="vcard" -->
43 -<script type="text/javascript">  
44 - <%= remote_function :url => { :controller => 'profile', :profile => profile.identifier, :action => 'profile_info', :block_id => block.id } %>  
45 -</script>  
app/views/blocks/profile_info_actions/_join_leave_community.rhtml 0 → 100644
@@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
  1 +<div class='join-leave-button'>
  2 + <% if logged_in? %>
  3 + <% if profile.members.include?(user) %>
  4 + <%= button(:delete, content_tag('span', _('Leave community')), profile.leave_url,
  5 + :class => 'leave-community',
  6 + :title => _("Leave community"),
  7 + :style => 'position: relative;') %>
  8 + <%= button(:add, content_tag('span', _('Join')), profile.join_url,
  9 + :class => 'join-community',
  10 + :title => _("Join community"),
  11 + :style => 'position: relative; display: none;') %>
  12 + <% else %>
  13 + <% unless profile.already_request_membership?(user) %>
  14 + <%= button(:delete, content_tag('span', _('Leave community')), profile.leave_url,
  15 + :class => 'leave-community',
  16 + :title => _("Leave community"),
  17 + :style => 'position: relative; display: none;') %>
  18 + <%= button(:add, content_tag('span', _('Join')), profile.join_url,
  19 + :class => 'join-community',
  20 + :title => _("Join community"),
  21 + :style => 'position: relative;') %>
  22 + <% end %>
  23 + <% end %>
  24 + <% else %>
  25 + <%= link_to content_tag('span', _('Join')), profile.join_not_logged_url,
  26 + :class => 'button with-text icon-add',
  27 + :title => _('Join this community') %>
  28 + <% end %>
  29 +</div>
app/views/blocks/profile_info_actions/community.html.erb
1 <ul> 1 <ul>
  2 + <li>
  3 + <%= render "blocks/profile_info_actions/join_leave_community" %>
  4 + </li>
2 <% if logged_in? %> 5 <% if logged_in? %>
3 - <% if profile.members.include?(user) %>  
4 - <li>  
5 - <%= button(:delete, content_tag('span', _('Leave community')), profile.leave_url,  
6 - :class => 'leave-community',  
7 - :title => _("Leave community"),  
8 - :style => 'position: relative;') %>  
9 - <%= button(:add, content_tag('span', _('Join')), profile.join_url,  
10 - :class => 'join-community',  
11 - :title => _("Join community"),  
12 - :style => 'position: relative; display: none;') %>  
13 - </li>  
14 - <% else %>  
15 - <% unless profile.already_request_membership?(user) %>  
16 - <li>  
17 - <%= button(:delete, content_tag('span', _('Leave community')), profile.leave_url,  
18 - :class => 'leave-community',  
19 - :title => _("Leave community"),  
20 - :style => 'position: relative; display: none;') %>  
21 - <%= button(:add, content_tag('span', _('Join')), profile.join_url,  
22 - :class => 'join-community',  
23 - :title => _("Join community"),  
24 - :style => 'position: relative;') %>  
25 - </li>  
26 - <% end %>  
27 - <% end %>  
28 -  
29 <% if profile.enable_contact? %> 6 <% if profile.enable_contact? %>
30 <li> 7 <li>
31 <%= link_to content_tag('span', _('Send an e-mail')), 8 <%= link_to content_tag('span', _('Send an e-mail')),
@@ -39,11 +16,5 @@ @@ -39,11 +16,5 @@
39 <li><%= report_abuse(profile, :button) %></li> 16 <li><%= report_abuse(profile, :button) %></li>
40 17
41 <%= render_environment_features(:profile_actions) %> 18 <%= render_environment_features(:profile_actions) %>
42 - <% else %>  
43 - <li>  
44 - <%= link_to content_tag('span', _('Join')), profile.join_not_logged_url,  
45 - :class => 'button with-text icon-add',  
46 - :title => _('Join this community') %>  
47 - </li>  
48 <% end %> 19 <% end %>
49 </ul> 20 </ul>
app/views/box_organizer/_link_list_block.html.erb
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 content_tag('td', text_field_tag('block[links][][name]', '', :maxlength => 20)) + 23 content_tag('td', text_field_tag('block[links][][name]', '', :maxlength => 20)) +
24 content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'link-address'), :class => 'cel-address') + 24 content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'link-address'), :class => 'cel-address') +
25 content_tag('td', select_tag('block[links][][target]', 25 content_tag('td', select_tag('block[links][][target]',
26 -options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target]))) 26 +options_for_select(LinkListBlock::TARGET_OPTIONS, '_self')))
27 ) + 27 ) +
28 javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight")) 28 javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight"))
29 end %> 29 end %>
app/views/box_organizer/_profile_list_block.html.erb
@@ -2,5 +2,8 @@ @@ -2,5 +2,8 @@
2 <%= labelled_form_field _('Limit of items'), text_field(:block, :limit, :size => 3) %> 2 <%= labelled_form_field _('Limit of items'), text_field(:block, :limit, :size => 3) %>
3 <%= check_box(:block, :prioritize_profiles_with_image) %> 3 <%= check_box(:block, :prioritize_profiles_with_image) %>
4 <label for="block_prioritize_profiles_with_image"><%= _('Prioritize profiles with image') %></label> 4 <label for="block_prioritize_profiles_with_image"><%= _('Prioritize profiles with image') %></label>
5 -</div>  
6 5
  6 + <div>
  7 + <%= extra_option_checkbox(@block.extra_option) %>
  8 + </div>
  9 +</div>
7 \ No newline at end of file 10 \ No newline at end of file
app/views/box_organizer/edit.html.erb
1 <div class="block-config-options <%= @block.class.name %>-options"> 1 <div class="block-config-options <%= @block.class.name %>-options">
2 - <h2 class="title"><%= _('Editing block') %></h2> 2 + <h2 class="title"><%= _(@block.class.description) %></h2>
3 3
4 <%= form_tag(:action => 'save', :id => @block.id) do %> 4 <%= form_tag(:action => 'save', :id => @block.id) do %>
5 5
@@ -9,17 +9,11 @@ @@ -9,17 +9,11 @@
9 9
10 <%= labelled_form_field _('Display this block:'), '' %> 10 <%= labelled_form_field _('Display this block:'), '' %>
11 <div style='margin-left: 10px'> 11 <div style='margin-left: 10px'>
12 - <%= radio_button(:block, :display, 'always') %>  
13 - <%= label_tag('block_display_always', _('In all pages')) %>  
14 - <br/>  
15 - <%= radio_button(:block, :display, 'home_page_only') %>  
16 - <%= label_tag('block_display_home_page_only', _('Only in the homepage')) %>  
17 - <br/>  
18 - <%= radio_button(:block, :display, 'except_home_page') %>  
19 - <%= label_tag('block_display_except_home_page', _('In all pages, except in the homepage')) %>  
20 - <br/>  
21 - <%= radio_button(:block, :display, 'never') %>  
22 - <%= label_tag('block_display_never', _("Don't display")) %> 12 + <% @block.display_options.each do |option| %>
  13 + <%= radio_button(:block, :display, option) %>
  14 + <%= label_tag("block_display_#{option}", _(@block.display_option_label(option))) %>
  15 + <br/>
  16 + <% end %>
23 </div> 17 </div>
24 18
25 <%= labelled_form_field(_('Show for:'), select(:block, :language, [ [ _('all languages'), 'all']] + environment.locales.map {|key, value| [value, key]} )) %> 19 <%= labelled_form_field(_('Show for:'), select(:block, :language, [ [ _('all languages'), 'all']] + environment.locales.map {|key, value| [value, key]} )) %>
app/views/catalog/index.html.erb
1 <% extra_content = [] %> 1 <% extra_content = [] %>
2 <% extra_content_list = [] %> 2 <% extra_content_list = [] %>
3 3
4 -<h1><%= _('Products/Services') %></h1>  
5 -  
6 -<% if @categories %>  
7 - <%= breadcrumb(@category) if params[:level] %>  
8 -  
9 - <div class='l-sidebar-left-bar'>  
10 - <ul>  
11 - <%= content_tag('li', link_to(_('Homepage'), profile.url), :class => 'catalog-categories-link') %>  
12 - <%= content_tag('li', link_to(_('Catalog start'), profile.catalog_url), :class => 'catalog-categories-link') %>  
13 - <% if @categories.present? %>  
14 - <% @categories.each do |category| %>  
15 - <%= category_link(category) %>  
16 - <%= category_sub_links(category) %>  
17 - <% end %>  
18 - <% elsif @category.present? %>  
19 - <%= content_tag('li', _('There are no sub-categories for %s') % @category.name, :id => 'catalog-categories-notice') %>  
20 - <% else %>  
21 - <%= content_tag('li', _('There are no categories available.'), :id => 'catalog-categories-notice') %>  
22 - <% end %>  
23 - </ul> 4 +<div id="product-catalog">
  5 +<% if !user.nil? && ( user.is_admin?(profile.environment) || user.is_admin?(profile) ) %>
  6 + <div class="product-catalog-ctrl">
  7 + <%= button :product, _('Manage Products/Services'), :controller => 'manage_products' %>
24 </div> 8 </div>
25 <% end %> 9 <% end %>
26 10
27 -<ul id="product-list" class="<%="l-sidebar-left-content" if @categories %>"> 11 +<h1><%= _('Products/Services') %></h1>
  12 +
  13 +<%= breadcrumb(@category) if params[:level] %>
  14 +
  15 +<ul id="product-list">
28 <% @products.each do |product| %> 16 <% @products.each do |product| %>
29 <% extra_content = @plugins.dispatch(:catalog_item_extras, product).collect { |content| instance_eval(&content) } %> 17 <% extra_content = @plugins.dispatch(:catalog_item_extras, product).collect { |content| instance_eval(&content) } %>
30 <% extra_content_list = @plugins.dispatch(:catalog_list_item_extras, product).collect { |content| instance_eval(&content) } %> 18 <% extra_content_list = @plugins.dispatch(:catalog_list_item_extras, product).collect { |content| instance_eval(&content) } %>
@@ -132,10 +120,11 @@ @@ -132,10 +120,11 @@
132 </ul> 120 </ul>
133 </li> 121 </li>
134 <% end %> 122 <% end %>
135 -</ul> 123 +</ul><!-- end id="product-list" -->
136 124
137 <%= pagination_links @products, :params => {:controller => :catalog, :action => :index, :profile => profile.identifier} %> 125 <%= pagination_links @products, :params => {:controller => :catalog, :action => :index, :profile => profile.identifier} %>
138 126
139 <%= add_zoom_to_images %> 127 <%= add_zoom_to_images %>
140 128
141 <br style="clear:both"/> 129 <br style="clear:both"/>
  130 +</div><!-- end id="product-catalog" -->
app/views/cms/_forum.html.erb
@@ -11,3 +11,9 @@ @@ -11,3 +11,9 @@
11 <%= labelled_form_field(_('Description:'), text_area(:article, :body, :cols => 64, :rows => 10)) %> 11 <%= labelled_form_field(_('Description:'), text_area(:article, :body, :cols => 64, :rows => 10)) %>
12 12
13 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Forum.posts_per_page_options)) %> 13 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Forum.posts_per_page_options)) %>
  14 +
  15 +<%= labelled_form_field(_('Has terms of use:'), check_box(:article, :has_terms_of_use))%>
  16 +
  17 +<div id="text_area_terms_of_use">
  18 + <%= labelled_form_field(_('Terms of use:'), text_area(:article, :terms_of_use, :class => 'mceEditor',:cols => 64, :rows => 10)) %>
  19 +</div>
app/views/cms/edit.html.erb
1 <%= error_messages_for 'article' %> 1 <%= error_messages_for 'article' %>
  2 +<%= javascript_include_tag "article.js" %>
2 3
3 <div class='<%= (environment.enabled?('media_panel') ? 'with_media_panel' : 'no_media_panel') %>'> 4 <div class='<%= (environment.enabled?('media_panel') ? 'with_media_panel' : 'no_media_panel') %>'>
4 <%= labelled_form_for 'article', @article, :html => { :multipart => true, :class => @type } do |f| %> 5 <%= labelled_form_for 'article', @article, :html => { :multipart => true, :class => @type } do |f| %>
@@ -9,6 +10,8 @@ @@ -9,6 +10,8 @@
9 10
10 <%= hidden_field_tag('back_to', @back_to) %> 11 <%= hidden_field_tag('back_to', @back_to) %>
11 12
  13 + <%= hidden_field_tag('success_back_to', @success_back_to) %>
  14 +
12 <%= render :partial => partial_for_class(@article.class), :locals => { :f => f } %> 15 <%= render :partial => partial_for_class(@article.class), :locals => { :f => f } %>
13 16
14 <% if environment.is_portal_community?(profile) %> 17 <% if environment.is_portal_community?(profile) %>
@@ -33,11 +36,12 @@ @@ -33,11 +36,12 @@
33 <%= content_tag( 'small', _('Separate tags with commas') ) %> 36 <%= content_tag( 'small', _('Separate tags with commas') ) %>
34 37
35 <div id='edit-article-options'> 38 <div id='edit-article-options'>
36 - <%= options_for_article(@article) %> 39 + <%= options_for_article(@article, @tokenized_children) %>
37 </div> 40 </div>
38 41
39 <% button_bar do %> 42 <% button_bar do %>
40 <%= submit_button :save, _('Save') %> 43 <%= submit_button :save, _('Save') %>
  44 +
41 <% if @back_to %> 45 <% if @back_to %>
42 <%= button :cancel, _('Cancel'), @back_to %> 46 <%= button :cancel, _('Cancel'), @back_to %>
43 <% elsif @parent_id || @article.parent %> 47 <% elsif @parent_id || @article.parent %>
@@ -45,6 +49,11 @@ @@ -45,6 +49,11 @@
45 <% else %> 49 <% else %>
46 <%= button :cancel, _('Cancel'), :action => 'index' %> 50 <%= button :cancel, _('Cancel'), :action => 'index' %>
47 <% end %> 51 <% end %>
  52 +
  53 + <% unless @article.new_record? %>
  54 + <%= button :delete, _('Delete'), {:controller => :cms, :action => :destroy, :id => @article},
  55 + :method => :post, :confirm => delete_article_message(@article) %>
  56 + <% end %>
48 <% end %> 57 <% end %>
49 <% end %> 58 <% end %>
50 </div> 59 </div>
app/views/comment/_comment.html.erb
@@ -32,19 +32,7 @@ @@ -32,19 +32,7 @@
32 32
33 <div class="comment-details"> 33 <div class="comment-details">
34 <div class="comment-header"> 34 <div class="comment-header">
35 - <ul>  
36 - <div class="comment-actions">  
37 - <%= comment_actions(comment) %>  
38 - </div>  
39 - </ul>  
40 - <% unless comment.spam? %>  
41 - <%= link_to_function '',  
42 - "var f = add_comment_reply_form(this, %s); f.find('comment_title, textarea').val(''); return false" % comment.id,  
43 - :class => 'comment-footer comment-footer-link comment-footer-hide comment-actions-reply button',  
44 - :id => 'comment-reply-to-' + comment.id.to_s,  
45 - :title => _('Reply')  
46 - %>  
47 - <% end %> 35 + <%= comment_actions(comment) %>
48 </div> 36 </div>
49 37
50 <div class="comment-created-at"> 38 <div class="comment-created-at">
app/views/comment/_comment_actions.rhtml 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +<ul>
  2 + <% if !links_submenu.empty? %>
  3 + <div class="comment-actions">
  4 + <li class="vcard">
  5 + <%= link_to(content_tag(:span, _('Contents menu')), '#', :onclick => "toggleSubmenu(this,'',#{j links_submenu.to_json}); return false", :class => 'menu-submenu-trigger comment-trigger', :url => url) %>
  6 + </li>
  7 + </div>
  8 + <% end %>
  9 +</ul>
  10 +<div class="comments-action-bar">
  11 + <% unless comment.spam? %>
  12 + <%= link_to_function '',
  13 + "var f = add_comment_reply_form(this, %s); f.find('comment_title, textarea').val(''); return false" % comment.id,
  14 + :class => 'comment-footer comment-footer-link comment-footer-hide comment-actions-reply button',
  15 + :id => 'comment-reply-to-' + comment.id.to_s,
  16 + :title => _('Reply')
  17 + %>
  18 + <% end %>
  19 + <% links_action_bar.collect do |link| %>
  20 + <%= link[:link] %>
  21 + <% end %>
  22 +</div>
app/views/content_viewer/_article_toolbar.html.erb
@@ -48,6 +48,10 @@ @@ -48,6 +48,10 @@
48 <%= expirable_button @page, :suggest, content, url, options %> 48 <%= expirable_button @page, :suggest, content, url, options %>
49 <% end %> 49 <% end %>
50 50
  51 + <% if @page.display_versions? %>
  52 + <%= button(:clock, _('All versions'), {:controller => 'content_viewer', :profile => profile.identifier, :action => 'article_versions'}, :id => 'article-versions-link') %>
  53 + <% end %>
  54 +
51 <%= report_abuse(profile, :link, @page) %> 55 <%= report_abuse(profile, :link, @page) %>
52 56
53 </div> 57 </div>
@@ -56,6 +60,7 @@ @@ -56,6 +60,7 @@
56 <div class="blog-cover"><%= image_tag(@page.image.public_filename())%></div> 60 <div class="blog-cover"><%= image_tag(@page.image.public_filename())%></div>
57 <% end %> 61 <% end %>
58 <%= link_to(image_tag('icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %> 62 <%= link_to(image_tag('icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %>
  63 + <%= @plugins.dispatch(:article_header_extra_contents, @page).collect { |content| instance_eval(&content) }.join("") %>
59 <%= article_title(@page, :no_link => true) %> 64 <%= article_title(@page, :no_link => true) %>
60 <%= article_translations(@page) %> 65 <%= article_translations(@page) %>
61 </div> 66 </div>
app/views/content_viewer/article_versions.rhtml 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +<%= article_title(@page, :no_link => true) %>
  2 +
  3 +<p><%= _('This is the list of all versions of this content. Select a version to see it and then revert to it.') %>.</p>
  4 +
  5 +<ul class='article-versions'>
  6 + <% @versions.each do |v| %>
  7 + <li>
  8 + <%= link_to(_("Version #{v.version}"), @page.url.merge(:version => v.version)) %>
  9 + <%= @page.version == v.version ? _('(current)') : '' %>
  10 + <span class='updated-by'><%= _('by %{author}') % {:author => link_to(@page.author_name(v.version), @page.author_url)} %></span>
  11 + <div class='updated-on'><%= show_time(v.updated_at) %></div>
  12 + </li>
  13 + <% end %>
  14 +</ul>
  15 +
  16 +<%= pagination_links @versions, :param_name => 'npage' %>
app/views/content_viewer/forum_page.html.erb
1 <% add_rss_feed_to_head(@page.name, @page.feed.url) if @page.forum? && @page.feed %> 1 <% add_rss_feed_to_head(@page.name, @page.feed.url) if @page.forum? && @page.feed %>
  2 +<% if @page.agrees_with_terms?(user) %>
2 3
3 -<div>  
4 - <div class='forum-description'>  
5 - <%= @page.body %> 4 + <div>
  5 + <div class='forum-description'>
  6 + <%= @page.body %>
  7 + </div>
6 </div> 8 </div>
7 -</div>  
8 -<hr class="pre-posts"/>  
9 -<div class="forum-posts">  
10 - <%= (@posts.compact.empty? ? content_tag('em', _('(no posts)')) : list_forum_posts(@posts)) %>  
11 -</div> 9 + <hr class="pre-posts"/>
  10 + <div class="forum-posts">
  11 + <%= (@posts.compact.empty? ? content_tag('em', _('(no posts)')) : list_forum_posts(@posts)) %>
  12 + </div>
  13 +
  14 +<% else %>
  15 +
  16 + <p><%= @page.terms_of_use %></p>
  17 +
  18 + <% form_tag @page.url.merge(:terms_accepted => true) do %>
  19 + <% button_bar do %>
  20 + <% if user %>
  21 + <%= submit_button :save, _("Accept") %>
  22 + <% else %>
  23 + <%= button :save, _("Accept"), login_url %>
  24 + <% end %>
  25 + <%= button :cancel, _("Cancel"), profile.url %>
  26 + <% end %>
  27 + <% end %>
  28 +<% end %>
app/views/content_viewer/versioned_article.rhtml 0 → 100644
@@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
  1 +<div id="article" class="<%= @page.css_class_name %>">
  2 +
  3 + <div id="article-actions">
  4 + <%= button(:clock, _('All versions'), {:controller => 'content_viewer', :profile => profile.identifier, :action => 'article_versions'}, :id => 'article-versions-link') %>
  5 +
  6 + <% if @page.allow_edit?(user) && !remove_content_button(:edit) %>
  7 + <% content = content_tag('span', _('Revert to this version')) %>
  8 + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id, :version => @version }) %>
  9 + <%= expirable_button @page, :edit, content, url, :id => 'article-revert-version-link' %>
  10 + <% end %>
  11 + </div>
  12 +
  13 + <div id="article-header">
  14 + <h1 class='title'><%= @versioned_article.name %></h1>
  15 + <%= _("Version %{version} - %{author} on %{date}") % {:version => @version, :author => @page.author_name(@version), :date => show_time(@versioned_article.updated_at) } %>
  16 + </div>
  17 +
  18 + <% version_license = @page.version_license(@version) %>
  19 + <%# This seemingly doubled verification exists because the article-sub-header
  20 + div must appear only if at least one content inside it will appeart.
  21 + Although we have only one content now, we might have others in the future.
  22 + So we're keeping it like that to avoid mistakes. %>
  23 + <% if version_license.present? %>
  24 + <div id='article-sub-header'>
  25 + <% if version_license.present? %>
  26 + <div id="article-license">
  27 + <%= _('Licensed under %s') % (version_license.url.present? ? link_to(version_license.name, version_license.url, :target => '_blank') : version_license.name) %>
  28 + </div>
  29 + <% end %>
  30 + </div>
  31 + <% end %>
  32 +
  33 + <% cache(@page.cache_key(params, user, language)) do %>
  34 + <div class="<%="article-body article-body-" + @page.css_class_name %>">
  35 + <%= @versioned_article.body %>
  36 + <br style="clear:both" />
  37 + </div> <!-- end class="article-body" -->
  38 + <% end %>
  39 +
  40 + <%= display_source_info(@page) %>
  41 +
  42 +</div><!-- end id="article" -->
  43 +<%= add_zoom_to_article_images %>
app/views/content_viewer/view_page.html.erb
@@ -15,16 +15,14 @@ @@ -15,16 +15,14 @@
15 </script> 15 </script>
16 16
17 <% if @page.parent && !@page.parent.path.blank? %> 17 <% if @page.parent && !@page.parent.path.blank? %>
18 -<div id="article-parent">  
19 - <%= button(:back, _('Go back to %s') % @page.parent.short_title, @page.parent.url) %>  
20 -</div> 18 + <div id="article-parent">
  19 + <%= button(:back, _('Go back to %s') % @page.parent.short_title, @page.parent.url) %>
  20 + </div>
21 <% end %> 21 <% end %>
22 22
23 -<div id="article-toolbar"></div>  
24 -  
25 -<script type="text/javascript">  
26 - <%= remote_function :update => "article-toolbar", :url => @page.url.merge({ :toolbar => true, :only_path => true }) %>  
27 -</script> 23 +<div id="article-toolbar">
  24 + <%= render :partial => 'article_toolbar' %>
  25 +</div>
28 26
29 <% if @page.display_hits? || @page.license.present? %> 27 <% if @page.display_hits? || @page.license.present? %>
30 <div id='article-sub-header'> 28 <div id='article-sub-header'>
app/views/layouts/application-ng.html.erb
@@ -24,7 +24,7 @@ @@ -24,7 +24,7 @@
24 24
25 <%= csrf_meta_tag %> 25 <%= csrf_meta_tag %>
26 </head> 26 </head>
27 - <body class="<%= body_classes %>"> 27 + <body class="<%= h body_classes %>">
28 <a href="#content" id="link-go-content"><span><%= _("Go to the content") %></span></a> 28 <a href="#content" id="link-go-content"><span><%= _("Go to the content") %></span></a>
29 29
30 <%= 30 <%=
app/views/profile/_create_article.html.erb
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <div class='profile-activity-description profile-activity-article-<%= activity.target.class.icon_name %>'> 4 <div class='profile-activity-description profile-activity-article-<%= activity.target.class.icon_name %>'>
5 <p class='profile-activity-text'> 5 <p class='profile-activity-text'>
6 <%= link_to activity.user.short_name(20), activity.user.url %> 6 <%= link_to activity.user.short_name(20), activity.user.url %>
7 - <%= _("on community %s") % link_to(activity.target.profile.short_name(20), activity.target.profile.url) if activity.target.profile.is_a?(Community) %> 7 + <%= _("has published on community %s") % link_to(activity.target.profile.short_name(20), activity.target.profile.url) if activity.target.profile.is_a?(Community) %>
8 </p> 8 </p>
9 <div class='profile-activity-lead'> 9 <div class='profile-activity-lead'>
10 <div class='article-name'><%= link_to(activity.params['name'], activity.params['url']) %></div> 10 <div class='article-name'><%= link_to(activity.params['name'], activity.params['url']) %></div>
app/views/profile/index.html.erb
@@ -19,22 +19,21 @@ @@ -19,22 +19,21 @@
19 <table class='profile'> 19 <table class='profile'>
20 <tr> 20 <tr>
21 <td colspan='2'> 21 <td colspan='2'>
22 - <% plugins_tabs = @plugins.dispatch(:profile_tabs).  
23 - map { |tab| {:title => tab[:title], :id => tab[:id], :content => instance_eval(&tab[:content]), :start => tab[:title]} }%> 22 + <% plugins_tabs = @plugins.dispatch(:profile_tabs).map { |tab| {:title => tab[:title], :id => tab[:id], :content => instance_eval(&tab[:content]), :start => tab[:title]} }%>
24 23
25 <% tabs = plugins_tabs.select { |tab| tab[:start] } %> 24 <% tabs = plugins_tabs.select { |tab| tab[:start] } %>
26 25
27 <% if @profile.organization? %> 26 <% if @profile.organization? %>
28 - <% tabs << {:title => _('Profile'), :id => 'organization-profile', :content => (render :partial => 'organization_profile')} %>  
29 <% if logged_in? && current_person.follows?(@profile) %> 27 <% if logged_in? && current_person.follows?(@profile) %>
30 <% tabs << {:title => _('Wall'), :id => 'profile-wall', :content => (render :partial => 'profile_wall')} %> 28 <% tabs << {:title => _('Wall'), :id => 'profile-wall', :content => (render :partial => 'profile_wall')} %>
31 <% end %> 29 <% end %>
  30 + <% tabs << {:title => _('Profile'), :id => 'organization-profile', :content => (render :partial => 'organization_profile')} %>
32 <% elsif @profile.person? %> 31 <% elsif @profile.person? %>
33 - <% tabs << {:title => _('Profile'), :id => 'person-profile', :content => (render :partial => 'person_profile')} %>  
34 <% if logged_in? && current_person.follows?(@profile) %> 32 <% if logged_in? && current_person.follows?(@profile) %>
35 <% tabs << {:title => _('Wall'), :id => 'profile-wall', :content => (render :partial => 'profile_wall')} %> 33 <% tabs << {:title => _('Wall'), :id => 'profile-wall', :content => (render :partial => 'profile_wall')} %>
36 <% tabs << {:title => _('Network'), :id => 'profile-network', :content => (render :partial => 'profile_network')} %> 34 <% tabs << {:title => _('Network'), :id => 'profile-network', :content => (render :partial => 'profile_network')} %>
37 <% end %> 35 <% end %>
  36 + <% tabs << {:title => _('Profile'), :id => 'person-profile', :content => (render :partial => 'person_profile')} %>
38 <% end %> 37 <% end %>
39 38
40 <% tabs += plugins_tabs.select { |tab| !tab[:start] } %> 39 <% tabs += plugins_tabs.select { |tab| !tab[:start] } %>
app/views/profile/profile_info.rjs
@@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
1 -if !user.nil? and user.has_permission?('edit_profile', profile)  
2 - page.replace_html "profile-admin-url-#{@block.id}", link_to(_('Control panel'), @block.owner.admin_url)  
3 -else  
4 - page.hide "profile-admin-url-#{@block.id}"  
5 -end  
6 -page.replace_html "profile-info-options-#{@block.id}", :file => view_for_profile_actions(@block.owner.class)  
app/views/profile_editor/_organization.html.erb
@@ -65,3 +65,8 @@ @@ -65,3 +65,8 @@
65 <%= labelled_check_box(_('Enable "contact us"'), 'profile_data[enable_contact_us]', "1", @profile.enable_contact_us) if @profile.enterprise? %> 65 <%= labelled_check_box(_('Enable "contact us"'), 'profile_data[enable_contact_us]', "1", @profile.enable_contact_us) if @profile.enterprise? %>
66 66
67 <%= render :partial => 'moderation', :locals => { :profile => @profile } %> 67 <%= render :partial => 'moderation', :locals => { :profile => @profile } %>
  68 +
  69 +<% if profile.enterprise? && profile.environment.enabled?('products_for_enterprises') %>
  70 + <h2><%=_('Products/Services catalog')%></h2>
  71 + <%= labelled_form_field(_('Number of products/services displayed per page on catalog'), text_field(:profile_data, :products_per_catalog_page, :size => 3)) %>
  72 +<% end %>
app/views/profile_editor/_person_form.html.erb
@@ -13,6 +13,8 @@ @@ -13,6 +13,8 @@
13 <%= optional_field(@person, 'contact_phone', labelled_form_field(_('Home phone'), text_field(:profile_data, :contact_phone, :rel => _('Contact phone')))) %> 13 <%= optional_field(@person, 'contact_phone', labelled_form_field(_('Home phone'), text_field(:profile_data, :contact_phone, :rel => _('Contact phone')))) %>
14 <%= optional_field(@person, 'cell_phone', f.text_field(:cell_phone, :rel => _('Cell phone'))) %> 14 <%= optional_field(@person, 'cell_phone', f.text_field(:cell_phone, :rel => _('Cell phone'))) %>
15 <%= optional_field(@person, 'comercial_phone', f.text_field(:comercial_phone, :rel => _('Comercial phone'))) %> 15 <%= optional_field(@person, 'comercial_phone', f.text_field(:comercial_phone, :rel => _('Comercial phone'))) %>
  16 +<%= optional_field(@person, 'jabber_id', f.text_field(:jabber_id, :rel => _('Jabber'))) %>
  17 +<%= optional_field(@person, 'personal_website', f.text_field(:personal_website, :rel => _('Personal website'))) %>
16 <%= optional_field(@person, 'sex', f.radio_group(:profile_data, :sex, [ ['male',_('Male')], ['female',_('Female')] ])) %> 18 <%= optional_field(@person, 'sex', f.radio_group(:profile_data, :sex, [ ['male',_('Male')], ['female',_('Female')] ])) %>
17 <%= optional_field(@person, 'birth_date', labelled_form_field(_('Birth date'), '<div class="select-birth-date">' + pick_date(:profile_data, :birth_date, {:start_year => (Date.today.year - 100), :end_year => (Date.today.year - 5)}) + '</div>')) %> 19 <%= optional_field(@person, 'birth_date', labelled_form_field(_('Birth date'), '<div class="select-birth-date">' + pick_date(:profile_data, :birth_date, {:start_year => (Date.today.year - 100), :end_year => (Date.today.year - 5)}) + '</div>')) %>
18 <%= optional_field(@person, 'nationality', f.text_field(:nationality, :rel => _('Nationality'))) %> 20 <%= optional_field(@person, 'nationality', f.text_field(:nationality, :rel => _('Nationality'))) %>
config/initializers/dependencies.rb
@@ -14,3 +14,4 @@ require &#39;white_list_filter&#39; @@ -14,3 +14,4 @@ require &#39;white_list_filter&#39;
14 # third-party libraries 14 # third-party libraries
15 require 'will_paginate' 15 require 'will_paginate'
16 require 'will_paginate/array' 16 require 'will_paginate/array'
  17 +require 'nokogiri'
config/routes.rb
@@ -125,11 +125,14 @@ Noosfero::Application.routes.draw do @@ -125,11 +125,14 @@ Noosfero::Application.routes.draw do
125 # cache stuff - hack 125 # cache stuff - hack
126 match 'public/:action/:id', :controller => 'public' 126 match 'public/:action/:id', :controller => 'public'
127 127
  128 + match ':profile/*page/versions', :controller => 'content_viewer', :action => 'article_versions', :profile => /#{Noosfero.identifier_format}/, :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } }
  129 + match '*page/versions', :controller => 'content_viewer', :action => 'article_versions'
  130 +
128 # match requests for profiles that don't have a custom domain 131 # match requests for profiles that don't have a custom domain
129 match ':profile(/*page)', :controller => 'content_viewer', :action => 'view_page', :profile => /#{Noosfero.identifier_format}/, :constraints => EnvironmentDomainConstraint.new 132 match ':profile(/*page)', :controller => 'content_viewer', :action => 'view_page', :profile => /#{Noosfero.identifier_format}/, :constraints => EnvironmentDomainConstraint.new
130 133
131 -  
132 # match requests for content in domains hosted for profiles 134 # match requests for content in domains hosted for profiles
133 match '/(*page)', :controller => 'content_viewer', :action => 'view_page' 135 match '/(*page)', :controller => 'content_viewer', :action => 'view_page'
134 136
  137 +
135 end 138 end
db/migrate/20130626152300_add_product_categories_block_to_enterprises.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +class AddProductCategoriesBlockToEnterprises < ActiveRecord::Migration
  2 + def self.up
  3 + Enterprise.find_each do |enterprise|
  4 + enterprise.boxes << Box.new while enterprise.boxes.length < 2
  5 + enterprise.boxes[1].blocks << ProductCategoriesBlock.new
  6 + end
  7 + end
  8 +
  9 + def self.down
  10 + Enterprise.find_each do |enterprise|
  11 + enterprise.boxes.each do |box|
  12 + box.blocks.each do |block|
  13 + block.destroy if block.class == ProductCategoriesBlock
  14 + end
  15 + end
  16 + end
  17 + end
  18 +end
db/migrate/20130702135550_add_sti_and_serialized_data_to_products.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class AddStiAndSerializedDataToProducts < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :products, :type, :string
  4 + add_column :products, :data, :text
  5 + rename_column :products, :enterprise_id, :profile_id
  6 + end
  7 +
  8 + def self.down
  9 + remove_column :products, :type
  10 + remove_column :products, :data
  11 + rename_column :products, :profile_id, :enterprise_id
  12 + end
  13 +end
db/migrate/20130703124306_add_archived_field_to_products.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class AddArchivedFieldToProducts < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :products, :archived, :boolean, :default => false
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :products, :archived
  8 + end
  9 +end
db/migrate/20130801182659_add_extra_fields_for_person.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +class AddExtraFieldsForPerson < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :profiles, :personal_website, :string
  4 + add_column :profiles, :jabber_id, :string
  5 + end
  6 +
  7 + def self.down
  8 + remove_column :profiles, :personal_website
  9 + remove_column :profiles, :jabber_id
  10 + end
  11 +end
db/migrate/20131128161159_terms_forum_people.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +class TermsForumPeople < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :terms_forum_people, :id => false do |t|
  4 + t.integer :forum_id
  5 + t.integer :person_id
  6 + end
  7 + add_index :terms_forum_people, [:forum_id, :person_id]
  8 + end
  9 +
  10 + def self.down
  11 + remove_index :terms_forum_people, [:forum_id, :person_id]
  12 + drop_table :terms_forum_people
  13 + end
  14 +
  15 +end
0 \ No newline at end of file 16 \ No newline at end of file
db/migrate/20140102151000_vote_fu_migration.rb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +class VoteFuMigration < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :votes, :force => true do |t|
  4 + t.integer :vote, :null => false
  5 + t.references :voteable, :polymorphic => true, :null => false
  6 + t.references :voter, :polymorphic => true
  7 + t.timestamps
  8 + end
  9 +
  10 + add_index :votes, ["voter_id", "voter_type"], :name => "fk_voters"
  11 + add_index :votes, ["voteable_id", "voteable_type"], :name => "fk_voteables"
  12 +
  13 + # If you want to enfore "One Person, One Vote" rules in the database, uncomment the index below
  14 + # add_index :votes, ["voter_id", "voter_type", "voteable_id", "voteable_type"], :unique => true, :name => "uniq_one_vote_only"
  15 + end
  16 +
  17 + def self.down
  18 + drop_table :votes
  19 + end
  20 +
  21 +end
db/migrate/20140108132730_article_privacy_exceptions.rb 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +class ArticlePrivacyExceptions < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :article_privacy_exceptions, :id => false do |t|
  4 + t.integer :article_id
  5 + t.integer :person_id
  6 + end
  7 + end
  8 +
  9 + def self.down
  10 + drop_table :article_privacy_exceptions
  11 + end
  12 +end
@@ -48,6 +48,11 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do @@ -48,6 +48,11 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do
48 add_index "action_tracker_notifications", ["profile_id", "action_tracker_id"], :name => "index_action_tracker_notif_on_prof_id_act_tracker_id", :unique => true 48 add_index "action_tracker_notifications", ["profile_id", "action_tracker_id"], :name => "index_action_tracker_notif_on_prof_id_act_tracker_id", :unique => true
49 add_index "action_tracker_notifications", ["profile_id"], :name => "index_action_tracker_notifications_on_profile_id" 49 add_index "action_tracker_notifications", ["profile_id"], :name => "index_action_tracker_notifications_on_profile_id"
50 50
  51 + create_table "article_privacy_exceptions", :id => false, :force => true do |t|
  52 + t.integer "article_id"
  53 + t.integer "person_id"
  54 + end
  55 +
51 create_table "article_versions", :force => true do |t| 56 create_table "article_versions", :force => true do |t|
52 t.integer "article_id" 57 t.integer "article_id"
53 t.integer "version" 58 t.integer "version"
@@ -413,7 +418,7 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do @@ -413,7 +418,7 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do
413 end 418 end
414 419
415 create_table "products", :force => true do |t| 420 create_table "products", :force => true do |t|
416 - t.integer "enterprise_id" 421 + t.integer "profile_id"
417 t.integer "product_category_id" 422 t.integer "product_category_id"
418 t.string "name" 423 t.string "name"
419 t.decimal "price" 424 t.decimal "price"
@@ -425,10 +430,13 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do @@ -425,10 +430,13 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do
425 t.boolean "highlighted", :default => false 430 t.boolean "highlighted", :default => false
426 t.integer "unit_id" 431 t.integer "unit_id"
427 t.integer "image_id" 432 t.integer "image_id"
  433 + t.string "type"
  434 + t.text "data"
  435 + t.boolean "archived", :default => false
428 end 436 end
429 437
430 - add_index "products", ["enterprise_id"], :name => "index_products_on_enterprise_id"  
431 add_index "products", ["product_category_id"], :name => "index_products_on_product_category_id" 438 add_index "products", ["product_category_id"], :name => "index_products_on_product_category_id"
  439 + add_index "products", ["profile_id"], :name => "index_products_on_profile_id"
432 440
433 create_table "profiles", :force => true do |t| 441 create_table "profiles", :force => true do |t|
434 t.string "name" 442 t.string "name"
@@ -463,6 +471,8 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do @@ -463,6 +471,8 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do
463 t.boolean "is_template", :default => false 471 t.boolean "is_template", :default => false
464 t.integer "template_id" 472 t.integer "template_id"
465 t.string "redirection_after_login" 473 t.string "redirection_after_login"
  474 + t.string "personal_website"
  475 + t.string "jabber_id"
466 end 476 end
467 477
468 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id" 478 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id"
@@ -574,6 +584,13 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do @@ -574,6 +584,13 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do
574 584
575 add_index "tasks", ["spam"], :name => "index_tasks_on_spam" 585 add_index "tasks", ["spam"], :name => "index_tasks_on_spam"
576 586
  587 + create_table "terms_forum_people", :id => false, :force => true do |t|
  588 + t.integer "forum_id"
  589 + t.integer "person_id"
  590 + end
  591 +
  592 + add_index "terms_forum_people", ["forum_id", "person_id"], :name => "index_terms_forum_people_on_forum_id_and_person_id"
  593 +
577 create_table "thumbnails", :force => true do |t| 594 create_table "thumbnails", :force => true do |t|
578 t.integer "size" 595 t.integer "size"
579 t.string "content_type" 596 t.string "content_type"
@@ -618,4 +635,17 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do @@ -618,4 +635,17 @@ ActiveRecord::Schema.define(:version =&gt; 20140115190132) do
618 t.integer "organization_id" 635 t.integer "organization_id"
619 end 636 end
620 637
  638 + create_table "votes", :force => true do |t|
  639 + t.integer "vote", :null => false
  640 + t.integer "voteable_id", :null => false
  641 + t.string "voteable_type", :null => false
  642 + t.integer "voter_id"
  643 + t.string "voter_type"
  644 + t.datetime "created_at"
  645 + t.datetime "updated_at"
  646 + end
  647 +
  648 + add_index "votes", ["voteable_id", "voteable_type"], :name => "fk_voteables"
  649 + add_index "votes", ["voter_id", "voter_type"], :name => "fk_voters"
  650 +
621 end 651 end
debian/changelog
  1 +noosfero (0.45.2) unstable; urgency=low
  2 +
  3 + * Small release with a critical bugfix
  4 +
  5 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Tue, 14 Jan 2014 13:25:18 +0000
  6 +
1 noosfero (0.45.1) unstable; urgency=low 7 noosfero (0.45.1) unstable; urgency=low
2 8
3 * Bugfix release 9 * Bugfix release
debian/control
@@ -38,6 +38,7 @@ Depends: @@ -38,6 +38,7 @@ Depends:
38 thin, 38 thin,
39 tango-icon-theme, 39 tango-icon-theme,
40 ruby-hpricot, 40 ruby-hpricot,
  41 + ruby-nokogiri,
41 memcached, 42 memcached,
42 debconf, 43 debconf,
43 dbconfig-common, 44 dbconfig-common,
features/activate_enterprise.feature
@@ -86,9 +86,9 @@ Feature: activate enterprise @@ -86,9 +86,9 @@ Feature: activate enterprise
86 Given enterprise template must be replaced after enable 86 Given enterprise template must be replaced after enable
87 And feature "enterprise_activation" is enabled on environment 87 And feature "enterprise_activation" is enabled on environment
88 And the following enterprises 88 And the following enterprises
89 - | identifier | name | enabled | foundation_year |  
90 - | services-provider-2 | Services Provider 2 | false | 2000 |  
91 - | active-template | Active Template | false | 2000 | 89 + | identifier | name | enabled | foundation_year | is_template |
  90 + | services-provider-2 | Services Provider 2 | false | 2000 | false |
  91 + | active-template | Active Template | false | 2000 | true |
92 And "Active Template" is the active enterprise template 92 And "Active Template" is the active enterprise template
93 And "Services Provider 2" doesnt have "Active Template" as template 93 And "Services Provider 2" doesnt have "Active Template" as template
94 And I go to joaosilva's control panel 94 And I go to joaosilva's control panel
@@ -108,9 +108,9 @@ Feature: activate enterprise @@ -108,9 +108,9 @@ Feature: activate enterprise
108 Given enterprise template must not be replaced after enable 108 Given enterprise template must not be replaced after enable
109 And feature "enterprise_activation" is enabled on environment 109 And feature "enterprise_activation" is enabled on environment
110 And the following enterprises 110 And the following enterprises
111 - | identifier | name | enabled | foundation_year |  
112 - | services-provider-3 | Services Provider 3 | false | 2000 |  
113 - | active-template | Active Template | false | 2000 | 111 + | identifier | name | enabled | foundation_year | is_template |
  112 + | services-provider-3 | Services Provider 3 | false | 2000 | false |
  113 + | active-template | Active Template | false | 2000 | true |
114 And "Active Template" is the active enterprise template 114 And "Active Template" is the active enterprise template
115 And "Services Provider 3" doesnt have "Active Template" as template 115 And "Services Provider 3" doesnt have "Active Template" as template
116 When I go to joaosilva's control panel 116 When I go to joaosilva's control panel
features/article_versioning.feature 0 → 100644
@@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
  1 +Feature: article versioning
  2 + As a user
  3 + I want to see article versions
  4 + In order to be able to change between versions of an article
  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 | display_versions |
  13 + | joaosilva | Sample Article | This is the first version of the article | true |
  14 + And the article "Sample Article" is updated with
  15 + | name | body |
  16 + | Edited Article | This is the second version of the article |
  17 + And I am logged in as "joaosilva"
  18 +
  19 + @selenium
  20 + Scenario: enabling visualization of versions
  21 + Given the following articles
  22 + | owner | name | body |
  23 + | joaosilva | New Article | Article to enable versions |
  24 + Given I am on article "New Article"
  25 + Then I should not see "All versions"
  26 + When I follow "Edit" within "#article-actions"
  27 + And I check "I want this article to display a link to older versions"
  28 + And I press "Save"
  29 + Then I should be on article "New Article"
  30 + And I should see "All versions"
  31 +
  32 + @selenium
  33 + Scenario: list versions of an article
  34 + Given I am on article "Edited Article"
  35 + When I follow "All versions"
  36 + Then I should be on /joaosilva/edited-article/versions
  37 + And I should see "Version 1" within ".article-versions"
  38 + And I should see "Version 2" within ".article-versions"
  39 +
  40 + @selenium
  41 + Scenario: see specific version
  42 + Given I go to article "Edited Article"
  43 + Then I should see "Edited Article" within ".title"
  44 + And I should see "This is the second version of the article" within ".article-body"
  45 + When I follow "All versions"
  46 + And I follow "Version 1"
  47 + Then I should see "Sample Article" within ".title"
  48 + And I should see "This is the first version of the article" within ".article-body"
  49 +
  50 + @selenium
  51 + Scenario: revert to a specific version generates a new version
  52 + Given I go to article "Edited Article"
  53 + When I follow "All versions"
  54 + Then I should not see "Version 3" within ".article-versions"
  55 + And I follow "Version 1"
  56 + And I follow "Revert to this version"
  57 + And I press "Save"
  58 + Then I should see "Sample Article" within ".title"
  59 + When I follow "All versions"
  60 + Then I should see "Version 3" within ".article-versions"
  61 +
  62 + Scenario: try to access versions of unexistent article
  63 + Given I go to /joaosilva/unexistent-article/versions
  64 + Then I should see "There is no such page"
  65 +
  66 + Scenario: deny access to versions when disabled on article
  67 + Given the following articles
  68 + | owner | name | body | display_versions |
  69 + | joaosilva | Versions disabled | Versions can't be displayed | false |
  70 + And I go to /joaosilva/versions-disabled/versions
  71 + Then I should see "Access denied"
  72 +
  73 + Scenario: deny access to specific version when disabled on article and not logged
  74 + Given the article "Edited Article" is updated with
  75 + | display_versions |
  76 + | false |
  77 + And I am not logged in
  78 + And I go to /joaosilva/edited-article?version=1
  79 + Then I should see "Access denied"
  80 +
  81 + Scenario: deny access to specific version when disabled, private and not logged
  82 + Given the article "Edited Article" is updated with
  83 + | display_versions | published |
  84 + | false | false |
  85 + And I am not logged in
  86 + And I go to /joaosilva/edited-article?version=1
  87 + Then I should see "Access denied"
features/categories_block.feature
@@ -25,9 +25,14 @@ Feature: categories_block @@ -25,9 +25,14 @@ Feature: categories_block
25 | environment | CategoriesBlock | 25 | environment | CategoriesBlock |
26 And I am logged in as admin 26 And I am logged in as admin
27 27
  28 + # Note that this @ignore-hidden-elements only works for seeing hidden
  29 + # elements. It actually doesn't work for following hidden link or pressing
  30 + # hidden buttons. That's why it's necessary to use this display hack to show
  31 + # the link.
28 @selenium @ignore-hidden-elements 32 @selenium @ignore-hidden-elements
29 Scenario: List just product categories 33 Scenario: List just product categories
30 Given I go to /admin/environment_design 34 Given I go to /admin/environment_design
  35 + And display ".categories-block .button-bar"
31 And I follow "Edit" within ".categories-block" 36 And I follow "Edit" within ".categories-block"
32 And I check "Product" 37 And I check "Product"
33 When I press "Save" 38 When I press "Save"
@@ -40,6 +45,7 @@ Feature: categories_block @@ -40,6 +45,7 @@ Feature: categories_block
40 @selenium @ignore-hidden-elements 45 @selenium @ignore-hidden-elements
41 Scenario: Show submenu if it exists 46 Scenario: Show submenu if it exists
42 Given I go to /admin/environment_design 47 Given I go to /admin/environment_design
  48 + And display ".categories-block .button-bar"
43 And I follow "Edit" within ".categories-block" 49 And I follow "Edit" within ".categories-block"
44 And I check "Product" 50 And I check "Product"
45 And I press "Save" 51 And I press "Save"
@@ -58,6 +64,7 @@ Feature: categories_block @@ -58,6 +64,7 @@ Feature: categories_block
58 @selenium @ignore-hidden-elements 64 @selenium @ignore-hidden-elements
59 Scenario: Show only one submenu per time 65 Scenario: Show only one submenu per time
60 Given I go to /admin/environment_design 66 Given I go to /admin/environment_design
  67 + And display ".categories-block .button-bar"
61 And I follow "Edit" within ".categories-block" 68 And I follow "Edit" within ".categories-block"
62 And I check "Product" 69 And I check "Product"
63 And I press "Save" 70 And I press "Save"
@@ -69,6 +76,7 @@ Feature: categories_block @@ -69,6 +76,7 @@ Feature: categories_block
69 @selenium @ignore-hidden-elements 76 @selenium @ignore-hidden-elements
70 Scenario: List just general categories 77 Scenario: List just general categories
71 Given I go to /admin/environment_design 78 Given I go to /admin/environment_design
  79 + And display ".categories-block .button-bar"
72 And I follow "Edit" within ".categories-block" 80 And I follow "Edit" within ".categories-block"
73 And I check "Generic category" 81 And I check "Generic category"
74 When I press "Save" 82 When I press "Save"
@@ -77,6 +85,7 @@ Feature: categories_block @@ -77,6 +85,7 @@ Feature: categories_block
77 @selenium @ignore-hidden-elements 85 @selenium @ignore-hidden-elements
78 Scenario: List just regions 86 Scenario: List just regions
79 Given I go to /admin/environment_design 87 Given I go to /admin/environment_design
  88 + And display ".categories-block .button-bar"
80 And I follow "Edit" within ".categories-block" 89 And I follow "Edit" within ".categories-block"
81 And I check "Region" 90 And I check "Region"
82 When I press "Save" 91 When I press "Save"
features/comment_reply.feature
@@ -23,7 +23,7 @@ Feature: comment @@ -23,7 +23,7 @@ Feature: comment
23 Scenario: show error messages when make a blank comment reply 23 Scenario: show error messages when make a blank comment reply
24 Given I am logged in as "booking" 24 Given I am logged in as "booking"
25 And I go to /booking/article-to-comment 25 And I go to /booking/article-to-comment
26 - And I follow "Reply" within ".comment-balloon" 26 + And I follow "Reply" within ".comments-action-bar"
27 When I press "Post comment" within ".comment-balloon" 27 When I press "Post comment" within ".comment-balloon"
28 Then I should see "Title can't be blank" within "div.comment_reply" 28 Then I should see "Title can't be blank" within "div.comment_reply"
29 And I should see "Body can't be blank" within "div.comment_reply" 29 And I should see "Body can't be blank" within "div.comment_reply"
@@ -31,30 +31,30 @@ Feature: comment @@ -31,30 +31,30 @@ Feature: comment
31 @selenium 31 @selenium
32 Scenario: render reply form 32 Scenario: render reply form
33 Given I am on /booking/article-to-comment 33 Given I am on /booking/article-to-comment
34 - When I follow "Reply" within ".comment-balloon" 34 + When I follow "Reply" within ".comments-action-bar"
35 Then I should see "Enter your comment" within "div.comment_reply.opened" 35 Then I should see "Enter your comment" within "div.comment_reply.opened"
36 36
37 # The text is hidden but the detector gets it anyway 37 # The text is hidden but the detector gets it anyway
38 @selenium-fixme 38 @selenium-fixme
39 Scenario: cancel comment reply 39 Scenario: cancel comment reply
40 Given I am on /booking/article-to-comment 40 Given I am on /booking/article-to-comment
41 - When I follow "Reply" within ".comment-balloon"  
42 - And I follow "Cancel" within ".comment-balloon" 41 + And I follow "Reply" within ".comments-action-bar"
  42 + When I follow "Cancel" within ".comment-balloon"
43 Then I should not see "Enter your comment" within "div.comment_reply.closed" 43 Then I should not see "Enter your comment" within "div.comment_reply.closed"
44 44
45 @selenium-fixme 45 @selenium-fixme
46 Scenario: not render same reply form twice 46 Scenario: not render same reply form twice
47 Given I am on /booking/article-to-comment 47 Given I am on /booking/article-to-comment
48 - When I follow "Reply" within ".comment-balloon" 48 + And I follow "Reply" within ".comments-action-bar"
49 And I follow "Cancel" within ".comment-balloon" 49 And I follow "Cancel" within ".comment-balloon"
50 - And I follow "Reply" within ".comment-balloon" 50 + When I follow "Reply" within ".comments-action-bar"
51 Then there should be 1 "comment_form" within "comment_reply" 51 Then there should be 1 "comment_form" within "comment_reply"
52 And I should see "Enter your comment" within "div.comment_reply.opened" 52 And I should see "Enter your comment" within "div.comment_reply.opened"
53 53
54 @selenium-fixme 54 @selenium-fixme
55 Scenario: reply a comment 55 Scenario: reply a comment
56 Given I go to /booking/another-article 56 Given I go to /booking/another-article
57 - And I follow "Reply" within ".comment-balloon" 57 + And I follow "Reply" within ".comments-action-bar"
58 And I fill in "Name" within "comment-balloon" with "Joey" 58 And I fill in "Name" within "comment-balloon" with "Joey"
59 And I fill in "e-mail" within "comment-balloon" with "joey@ramones.com" 59 And I fill in "e-mail" within "comment-balloon" with "joey@ramones.com"
60 And I fill in "Title" within "comment-balloon" with "Hey ho, let's go!" 60 And I fill in "Title" within "comment-balloon" with "Hey ho, let's go!"
@@ -73,7 +73,7 @@ Feature: comment @@ -73,7 +73,7 @@ Feature: comment
73 | rails.png | booking | root comment | this comment is not a reply | 73 | rails.png | booking | root comment | this comment is not a reply |
74 Given I am logged in as "booking" 74 Given I am logged in as "booking"
75 And I go to /booking/rails.png?view=true 75 And I go to /booking/rails.png?view=true
76 - And I follow "Reply" within ".comment-balloon" 76 + And I follow "Reply" within ".comments-action-bar"
77 And I fill in "Title" within "comment-balloon" with "Hey ho, let's go!" 77 And I fill in "Title" within "comment-balloon" with "Hey ho, let's go!"
78 And I fill in "Enter your comment" within "comment-balloon" with "Hey ho, let's go!" 78 And I fill in "Enter your comment" within "comment-balloon" with "Hey ho, let's go!"
79 When I press "Post comment" within ".comment-balloon" 79 When I press "Post comment" within ".comment-balloon"
features/edit_article.feature
@@ -22,6 +22,70 @@ Feature: edit article @@ -22,6 +22,70 @@ Feature: edit article
22 And I go to joaosilva's control panel 22 And I go to joaosilva's control panel
23 Then I should see "My Folder" 23 Then I should see "My Folder"
24 24
  25 + @selenium
  26 + Scenario: denied access folder for a not logged user
  27 + Given the following communities
  28 + | name | identifier | owner |
  29 + | Free Software | freesoftware | joaosilva |
  30 + And the following users
  31 + | login | name |
  32 + | mario | Mario Souto |
  33 + | maria | Maria Silva |
  34 + And "Mario Souto" is a member of "Free Software"
  35 + And "Maria Silva" is a member of "Free Software"
  36 + And I am on freesoftware's control panel
  37 + And I follow "Manage Content"
  38 + And I follow "New content"
  39 + When I follow "Folder"
  40 + And I fill in "Title" with "My Folder"
  41 + And I choose "article_published_false"
  42 + And I press "Save"
  43 + And I log off
  44 + And I go to /freesoftware/my-folder
  45 + Then I should see "Access denied"
  46 +
  47 + @selenium
  48 + Scenario: show exception users field when you choose the private option
  49 + Given the following communities
  50 + | name | identifier | owner |
  51 + | Free Software | freesoftware | joaosilva |
  52 + And the following users
  53 + | login | name |
  54 + | mario | Mario Souto |
  55 + | maria | Maria Silva |
  56 + And "Mario Souto" is a member of "Free Software"
  57 + And "Maria Silva" is a member of "Free Software"
  58 + And I am on freesoftware's control panel
  59 + And I follow "Manage Content"
  60 + And I follow "New content"
  61 + When I follow "Folder"
  62 + And I fill in "Title" with "My Folder"
  63 + And I choose "article_published_false"
  64 + Then I should see "Fill in the search field to add the exception users to see this content"
  65 +
  66 + @selenium
  67 + Scenario: allowed user should see the content of a folder
  68 + Given the following communities
  69 + | name | identifier | owner |
  70 + | Free Software | freesoftware | joaosilva |
  71 + And the following users
  72 + | login | name |
  73 + | mario | Mario Souto |
  74 + | maria | Maria Silva |
  75 + And the following articles
  76 + | owner | name | body |
  77 + | freesoftware | My Folder | ... |
  78 + And "Mario Souto" is a member of "Free Software"
  79 + And "Maria Silva" is a member of "Free Software"
  80 + And I go to /freesoftware/my-folder
  81 + When I follow "Edit"
  82 + And I choose "article_published_false"
  83 + And I press "Save"
  84 + And I add to "My Folder" the following exception "Maria Silva"
  85 + And I am logged in as "maria"
  86 + And I go to /freesoftware/my-folder
  87 + Then I should see "My Folder"
  88 +
25 Scenario: redirect to the created folder 89 Scenario: redirect to the created folder
26 Given I am on joaosilva's control panel 90 Given I am on joaosilva's control panel
27 And I follow "Manage Content" 91 And I follow "Manage Content"
@@ -40,6 +104,7 @@ Feature: edit article @@ -40,6 +104,7 @@ Feature: edit article
40 When I follow "Cancel" within ".main-block" 104 When I follow "Cancel" within ".main-block"
41 Then I should be on joaosilva's cms 105 Then I should be on joaosilva's cms
42 106
  107 + @selenium
43 Scenario: display tag list field when creating event 108 Scenario: display tag list field when creating event
44 Given I am on joaosilva's control panel 109 Given I am on joaosilva's control panel
45 And I follow "Manage Content" 110 And I follow "Manage Content"
features/forum.feature
@@ -70,28 +70,99 @@ Feature: forum @@ -70,28 +70,99 @@ Feature: forum
70 When I follow "Configure forum" 70 When I follow "Configure forum"
71 Then I should be on edit "Forum One" by joaosilva 71 Then I should be on edit "Forum One" by joaosilva
72 72
  73 + @selenium
  74 + Scenario: show forum with terms of use for owner
  75 + Given the following forums
  76 + | owner | name |
  77 + | joaosilva | Forum One |
  78 + And I go to /joaosilva/forum-one
  79 + When I follow "Configure forum"
  80 + And I fill in "Description" with "My description"
  81 + And I check "Has terms of use:"
  82 + And I press "Save"
  83 + Then I should see "Forum One"
  84 + And I should see "My description"
  85 +
  86 + @selenium
  87 + Scenario: accept terms in topics page
  88 + Given the following forums
  89 + | owner | name |
  90 + | joaosilva | Forum One |
  91 + And the following users
  92 + | login | name |
  93 + | mariasilva | Maria Silva |
  94 + And I go to /joaosilva/forum-one
  95 + When I follow "Configure forum"
  96 + And I fill in "Description" with "My description"
  97 + And I check "Has terms of use:"
  98 + And I press "Save"
  99 + When I follow "New discussion topic"
  100 + And I follow "Text article with visual editor"
  101 + And I fill in "Title" with "Topic"
  102 + And I press "Save"
  103 + And I am logged in as "mariasilva"
  104 + And I go to /joaosilva/forum-one/topic
  105 + And I press "Accept"
  106 + Then I should see "Topic"
  107 +
  108 + @selenium
  109 + Scenario: accept terms of use of a forum for others users
  110 + Given the following forums
  111 + | owner | name |
  112 + | joaosilva | Forum One |
  113 + And the following users
  114 + | login | name |
  115 + | mariasilva | Maria Silva |
  116 + And I go to /joaosilva/forum-one
  117 + When I follow "Configure forum"
  118 + And I fill in "Description" with "My description"
  119 + And I check "Has terms of use:"
  120 + And I press "Save"
  121 + When I follow "Logout"
  122 + And I am logged in as "mariasilva"
  123 + And I go to /joaosilva/forum-one?terms=terms
  124 + When I press "Accept"
  125 + Then I should see "Forum One"
  126 + And I should see "My description"
  127 +
  128 + @selenium
  129 + Scenario: redirect user not logged
  130 + Given the following forums
  131 + | owner | name |
  132 + | joaosilva | Forum One |
  133 + And I go to /joaosilva/forum-one
  134 + When I follow "Configure forum"
  135 + And I fill in "Description" with "My description"
  136 + And I check "Has terms of use:"
  137 + And I press "Save"
  138 + When I follow "Logout"
  139 + And I go to /joaosilva/forum-one?terms=terms
  140 + When I follow "Accept"
  141 + Then I should see "Login" within ".login-box"
  142 +
  143 + @selenium
73 Scenario: last topic update by unautenticated user should not link 144 Scenario: last topic update by unautenticated user should not link
74 Given the following forums 145 Given the following forums
75 - | owner | name | 146 + | owner | name |
76 | joaosilva | Forum | 147 | joaosilva | Forum |
77 And the following articles 148 And the following articles
78 - | owner | name | parent |  
79 - | joaosilva | Post one | Forum | 149 + | owner | name | parent |
  150 + | joaosilva | Post one | Forum |
80 And the following comments 151 And the following comments
81 - | article | name | email | title | body | 152 + | article | name | email | title | body |
82 | Post one | Joao | joao@example.com | Hi all | Hi all | 153 | Post one | Joao | joao@example.com | Hi all | Hi all |
83 When I go to /joaosilva/forum 154 When I go to /joaosilva/forum
84 Then I should not see "Joao" link 155 Then I should not see "Joao" link
85 156
86 Scenario: last topic update by autenticated user should link to profile url 157 Scenario: last topic update by autenticated user should link to profile url
87 Given the following forums 158 Given the following forums
88 - | owner | name | 159 + | owner | name |
89 | joaosilva | Forum | 160 | joaosilva | Forum |
90 And the following articles 161 And the following articles
91 - | owner | name | parent |  
92 - | joaosilva | Post one | Forum | 162 + | owner | name | parent |
  163 + | joaosilva | Post one | Forum |
93 And the following comments 164 And the following comments
94 - | article | author | title | body | 165 + | article | author | title | body |
95 | Post one | joaosilva | Hi all | Hi all | 166 | Post one | joaosilva | Hi all | Hi all |
96 When I go to /joaosilva/forum 167 When I go to /joaosilva/forum
97 Then I should see "Joao" linking to "http://localhost/joaosilva" 168 Then I should see "Joao" linking to "http://localhost/joaosilva"
features/manage_enterprises.feature
@@ -10,6 +10,7 @@ Feature: manage enterprises @@ -10,6 +10,7 @@ Feature: manage enterprises
10 And the following enterprise 10 And the following enterprise
11 | identifier | name | owner | 11 | identifier | name | owner |
12 | tangerine-dream | Tangerine Dream | joaosilva | 12 | tangerine-dream | Tangerine Dream | joaosilva |
  13 + And feature "display_my_enterprises_on_user_menu" is enabled on environment
13 14
14 @selenium 15 @selenium
15 Scenario: seeing my enterprises on menu 16 Scenario: seeing my enterprises on menu
features/members_block.feature 0 → 100644
@@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
  1 +Feature:
  2 + In order to enter in a community
  3 + As a logged user
  4 + I want to enter in a community by 'join leave' button in members block
  5 +
  6 + Background:
  7 + Given the following users
  8 + | login | name |
  9 + | joaosilva | Joao Silva |
  10 + | mariasilva | Maria Silva |
  11 + And the following communities
  12 + | owner | identifier | name |
  13 + | joaosilva | sample-community | Sample Community |
  14 + And the following blocks
  15 + | owner | type |
  16 + | sample-community | MembersBlock |
  17 + And I am logged in as "joaosilva"
  18 + And I go to sample-community's control panel
  19 + And I follow "Edit sideboxes"
  20 + And I follow "Edit" within ".members-block"
  21 + And I check "Show join leave button"
  22 + And I press "Save"
  23 +
  24 + Scenario: a user can join in a community by members block's button
  25 + Given I am logged in as "mariasilva"
  26 + And I go to sample-community's homepage
  27 + When I follow "Join" within ".members-block"
  28 + And I go to mariasilva's control panel
  29 + And I follow "Manage my groups"
  30 + Then I should see "Sample Community"
  31 +
  32 + Scenario: a user can leave a community by members block's button
  33 + Given "Maria Silva" is a member of "Sample Community"
  34 + And I am logged in as "mariasilva"
  35 + When I go to sample-community's homepage
  36 + And I follow "Leave community" within ".members-block"
  37 + And I go to mariasilva's control panel
  38 + And I follow "Manage my groups"
  39 + Then I should not see "Sample Community"
  40 +
  41 + Scenario: a not logged in user can log in by members block's button
  42 + Given I am not logged in
  43 + When I go to sample-community's homepage
  44 + And I follow "Join" within ".members-block"
  45 + Then I should see "Username / Email"
  46 +
  47 + Scenario: the join-leave button do not appear if the checkbox show-join-leave-button is not checked
  48 + And I go to sample-community's control panel
  49 + And I follow "Edit sideboxes"
  50 + And I follow "Edit" within ".members-block"
  51 + And I uncheck "Show join leave button"
  52 + And I press "Save"
  53 + When I go to sample-community's homepage
  54 + Then I should not see "Join" within ".members-block"
  55 + And I should not see "Leave community" within ".members-block"
features/new_content_on_cms.feature
@@ -50,6 +50,7 @@ Feature: create content on cms @@ -50,6 +50,7 @@ Feature: create content on cms
50 Given I follow "New content" 50 Given I follow "New content"
51 When I follow "Blog" 51 When I follow "Blog"
52 And I fill in "Title" with "My blog" 52 And I fill in "Title" with "My blog"
  53 + And I fill in "Address" with "my-blog"
53 And I press "Save" 54 And I press "Save"
54 And I go to joaosilva's cms 55 And I go to joaosilva's cms
55 Then I should see "My blog" 56 Then I should see "My blog"
features/publish_article.feature
@@ -25,9 +25,8 @@ Feature: publish article @@ -25,9 +25,8 @@ Feature: publish article
25 And I follow "Spread" 25 And I follow "Spread"
26 And I check "Sample Community" 26 And I check "Sample Community"
27 And I press "Spread this" 27 And I press "Spread this"
28 - And I go to sample-community's sitemap  
29 - When I follow "Sample Article"  
30 - Then I should see "This is the first published article" 28 + When I go to sample-community's sitemap
  29 + Then I should see "Sample Article"
31 30
32 Scenario: publishing an article with a different name 31 Scenario: publishing an article with a different name
33 Given I am logged in as "joaosilva" 32 Given I am logged in as "joaosilva"
features/search_enterprises.feature
@@ -3,14 +3,12 @@ Feature: search enterprises @@ -3,14 +3,12 @@ Feature: search enterprises
3 I want to search enterprises 3 I want to search enterprises
4 In order to find ones that interest me 4 In order to find ones that interest me
5 5
6 - Background:  
7 - And the following enterprises  
8 - | identifier | name | img |  
9 - | shop1 | Shoes shop | shoes |  
10 - | shop2 | Fruits shop | fruits |  
11 -  
12 Scenario: show recent enterprises on index 6 Scenario: show recent enterprises on index
13 - Given there are no pending jobs 7 + Given the following enterprises
  8 + | identifier | name | img |
  9 + | shop1 | Shoes shop | shoes |
  10 + | shop2 | Fruits shop | fruits |
  11 + And there are no pending jobs
14 When I go to the search enterprises page 12 When I go to the search enterprises page
15 Then I should see "Shoes shop" within "#search-results" 13 Then I should see "Shoes shop" within "#search-results"
16 And I should see Shoes shop's profile image 14 And I should see Shoes shop's profile image
@@ -18,10 +16,18 @@ Feature: search enterprises @@ -18,10 +16,18 @@ Feature: search enterprises
18 And I should see Fruits shop's profile image 16 And I should see Fruits shop's profile image
19 17
20 Scenario: show empty search results 18 Scenario: show empty search results
  19 + Given the following enterprises
  20 + | identifier | name |
  21 + | shop1 | Shoes shop |
  22 + | shop2 | Fruits shop |
21 When I search enterprises for "something unrelated" 23 When I search enterprises for "something unrelated"
22 Then I should see "None" within ".search-results-type-empty" 24 Then I should see "None" within ".search-results-type-empty"
23 25
24 Scenario: simple search for enterprise 26 Scenario: simple search for enterprise
  27 + Given the following enterprises
  28 + | identifier | name | img |
  29 + | shop1 | Shoes shop | shoes |
  30 + | shop2 | Fruits shop | fruits |
25 When I go to the search enterprises page 31 When I go to the search enterprises page
26 And I fill in "search-input" with "shoes" 32 And I fill in "search-input" with "shoes"
27 And I press "Search" 33 And I press "Search"
@@ -31,12 +37,21 @@ Feature: search enterprises @@ -31,12 +37,21 @@ Feature: search enterprises
31 And I should not see Fruits shop's profile image 37 And I should not see Fruits shop's profile image
32 38
33 Scenario: link to enterprise homepage on search results 39 Scenario: link to enterprise homepage on search results
34 - Given I search enterprises for "shoes" 40 + Given the following enterprises
  41 + | identifier | name |
  42 + | shop1 | Shoes shop |
  43 + And the following articles
  44 + | owner | name | body | homepage |
  45 + | 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 |
  46 + And I search enterprises for "shoes"
35 When I follow "Shoes shop" 47 When I follow "Shoes shop"
36 - Then I should be on shop1's profile 48 + Then I should be on shop1's homepage
37 49
38 Scenario: show clean enterprise homepage on search results 50 Scenario: show clean enterprise homepage on search results
39 - Given the following articles 51 + Given the following enterprises
  52 + | identifier | name |
  53 + | shop1 | Shoes shop |
  54 + And the following articles
40 | owner | name | body | homepage | 55 | owner | name | body | homepage |
41 | 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 | 56 | 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 |
42 When I search enterprises for "shoes" 57 When I search enterprises for "shoes"
@@ -47,7 +62,7 @@ Feature: search enterprises @@ -47,7 +62,7 @@ Feature: search enterprises
47 Scenario: show clean enterprise description on search results 62 Scenario: show clean enterprise description on search results
48 Given the following enterprises 63 Given the following enterprises
49 | identifier | name | description | 64 | identifier | name | description |
50 - | shop3 | 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. | 65 + | 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. |
51 When I search enterprises for "clothes" 66 When I search enterprises for "clothes"
52 And I follow "Full" 67 And I follow "Full"
53 And I should see "This clothes shop" within ".search-enterprise-description" 68 And I should see "This clothes shop" within ".search-enterprise-description"