Commit 1e0d9a57a82715de413e761eef7acfe380dce483
Exists in
master
and in
22 other branches
Merge branch 'master' into rails-2.3.5
Conflicts: app/controllers/application.rb lib/noosfero.rb lib/noosfero/i18n.rb test/test_helper.rb test/unit/consumption_test.rb
Showing
1595 changed files
with
105574 additions
and
92651 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 1595 files displayed.
.gitignore.example
... | ... | @@ -1,28 +0,0 @@ |
1 | -.gitignore | |
2 | -vendor/rails | |
3 | -doc/api | |
4 | -doc/plugins | |
5 | -tmp | |
6 | -config/database.yml | |
7 | -config/session.secret | |
8 | -config/mail.yml | |
9 | -config/ferret_server.yml | |
10 | -config/mongrel_cluster.yml | |
11 | -index | |
12 | -locale | |
13 | -log | |
14 | -public/articles | |
15 | -public/images/0000 | |
16 | -public/thumbnails | |
17 | -public/user_themes | |
18 | -public/designs/themes/default | |
19 | -public/javascripts/cache*.js | |
20 | -public/stylesheets/cache*.css | |
21 | -db/development.db | |
22 | -db/production.db | |
23 | -db/test.db | |
24 | -doc/noosfero/*.xhtml | |
25 | -doc/noosfero/*/*.xhtml | |
26 | -tags | |
27 | -pkg | |
28 | -*~ |
.mailmap
1 | 1 | Antonio Terceiro <terceiro@colivre.coop.br> <AntonioTerceiro@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
2 | 2 | Antonio Terceiro <terceiro@colivre.coop.br> <terceiro@softwarelivre.org> |
3 | 3 | Aurelio A. Heckert <aurelio@colivre.coop.br> <AurelioAHeckert@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
4 | +Aurelio A. Heckert <aurelio@colivre.coop.br> <aurelio@colivre.coop.br> | |
5 | +Aurelio A. Heckert <aurelio@colivre.coop.br> <aurium@gmail.com> | |
6 | +Caio SBA <caiosba@gmail.com> <caiosba@safernet.org.br> | |
4 | 7 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <DanielaFeitosa@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
5 | 8 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <danielafeitosa@colivre.coop.br> |
6 | 9 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <daniela@sede.colivre.coop.br> |
7 | 10 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <dani@lindinha.(none)> |
11 | +Daniel Cunha <daniel@colivre.coop.br> <daniel.ccunha@gmail.com> | |
12 | +Grazieno Pellegrino <grazieno@gmail.com> <GrazienoPellegrino@3f533792-8f58-4932-b0fe-aaf55b0a4547> | |
8 | 13 | Joenio Costa <joenio@colivre.coop.br> <JoenioCosta@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
9 | 14 | Joenio Costa <joenio@colivre.coop.br> <joenio@perl.org.br> |
10 | -Grazieno Pellegrino <grazieno@gmail.com> <GrazienoPellegrino@3f533792-8f58-4932-b0fe-aaf55b0a4547> | |
11 | 15 | Leandro Nunes dos Santos <leandronunes@gmail.com> <LeandroNunes@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
12 | 16 | Moises Machado <moises@colivre.coop.br> <MoisesMachado@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
13 | 17 | Valessio Brito <valessio@gmail.com> <ValessioBrito@3f533792-8f58-4932-b0fe-aaf55b0a4547> | ... | ... |
AUTHORS
... | ... | @@ -8,13 +8,18 @@ Developers |
8 | 8 | |
9 | 9 | Antonio Terceiro <terceiro@colivre.coop.br> |
10 | 10 | Aurelio A. Heckert <aurelio@colivre.coop.br> |
11 | +Bráulio Bhavamitra <brauliobo@gmail.com> | |
12 | +Caio SBA <caiosba@gmail.com> | |
11 | 13 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> |
14 | +Daniel Cunha <daniel@colivre.coop.br> | |
12 | 15 | Fernanda Lopes <nanda.listas+psl@gmail.com> |
13 | 16 | Grazieno Pellegrino <grazieno@gmail.com> |
14 | 17 | Italo Valcy <italo@dcc.ufba.br> |
15 | 18 | Joenio Costa <joenio@colivre.coop.br> |
16 | 19 | Josef Spillner <josef.spillner@tu-dresden.de> |
20 | +Keilla Menezes <keilla@colivre.coop.br> | |
17 | 21 | Leandro Nunes dos Santos <leandronunes@gmail.com> |
22 | +LinguÁgil 2010 <linguagil.bahia@gmail.com> | |
18 | 23 | Martín Olivera <molivera@solar.org.ar> |
19 | 24 | Moises Machado <moises@colivre.coop.br> |
20 | 25 | Nanda Lopes <nanda.listas+psl@gmail.com> |
... | ... | @@ -22,6 +27,7 @@ Raphaël Rousseau <raph@r4f.org> |
22 | 27 | Raquel Lira <raquel.lira@gmail.com> |
23 | 28 | Rodrigo Souto <rodrigo@colivre.coop.br> |
24 | 29 | Ronny Kursawe <kursawe.ronny@googlemail.com> |
30 | +Samuel R. C. Vale <srcvale@holoscopio.com> | |
25 | 31 | Valessio Brito <valessio@gmail.com> |
26 | 32 | Yann Lugrin <yann.lugrin@liquid-concept.ch> |
27 | 33 | ... | ... |
HACKING
... | ... | @@ -61,3 +61,26 @@ The above command makes the server available at http://localhost:9999/ |
61 | 61 | |
62 | 62 | The sample-data data scripts creates one administrator user with login "ze" and |
63 | 63 | password "test". |
64 | + | |
65 | +Note that some operations, like generating image thumbnails, sending e-mails, | |
66 | +etc, are done in background in the context of a service independent from the | |
67 | +Rails application server. To have those tasks performed in a development | |
68 | +environment, you must run the delayed_job server like this: | |
69 | + | |
70 | + ./script/delayed_job run | |
71 | + | |
72 | +This will block your terminal. To stop the delayed_job server, hit Control-C. | |
73 | + | |
74 | +== Enabling exceptions notification | |
75 | + | |
76 | +By default, exception notifications are disabled in development environment. If | |
77 | +you want to enable it then you need to change some files: | |
78 | + | |
79 | +1) Add in config/environments/development.rb: | |
80 | + config.action_controller.consider_all_requests_local = false | |
81 | + | |
82 | +2) Add in app/controller/application.rb: | |
83 | + local_addresses.clear | |
84 | + | |
85 | +3) Add in config/noosfero.yml at development section: | |
86 | + exception_recipients: [admin@example.com] | ... | ... |
INSTALL
... | ... | @@ -13,13 +13,14 @@ You need to install some packages Noosfero depends on. On Debian GNU/Linux or |
13 | 13 | Debian-based systems, all of these packages are available through the Debian |
14 | 14 | archive. You can install them with the following command: |
15 | 15 | |
16 | - # aptitude install ruby rake libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby libferret-ruby libdaemons-ruby mongrel mongrel-cluster tango-icon-theme | |
16 | + # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby-data libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby libferret-ruby libdaemons-ruby mongrel mongrel-cluster tango-icon-theme libhpricot-ruby | |
17 | 17 | |
18 | 18 | On other systems, they may or may not be available through your regular package |
19 | 19 | management system. Below are the links to their homepages. |
20 | 20 | |
21 | 21 | * Ruby: http://www.ruby-lang.org/ |
22 | 22 | * Rake: http://rake.rubyforge.org/ |
23 | +* po4a: http://po4a.alioth.debian.org/ | |
23 | 24 | * Ruby-GetText: http://www.yotabanana.com/hiki/ruby-gettext.html?ruby-gettext (at least version 1.9.0) |
24 | 25 | * Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby |
25 | 26 | * rcov: http://eigenclass.org/hiki/rcov |
... | ... | @@ -32,79 +33,268 @@ management system. Below are the links to their homepages. |
32 | 33 | * Daemons - http://daemons.rubyforge.org/ |
33 | 34 | * Mongrel: http://mongrel.rubyforge.org/ |
34 | 35 | * tango-icon-theme: http://tango.freedesktop.org/Tango_Icon_Library |
36 | +* Hpricot: http://hpricot.com/ | |
35 | 37 | |
36 | -If you manage to install Noosfero successfully, please feel free to contact the | |
37 | -Noosfero development mailing with the instructions for doing so, and we'll | |
38 | -include it here. | |
38 | +Note: the tango-icon-theme package is not available in Debian Lenny's main | |
39 | +repository, because back then it was not DFSG-free (Debian Squeeze will have it | |
40 | +in main). You can either add the non-free repository to your sources.list file, | |
41 | +or download the .deb directly and install it manually with `dpkg -i`. In this | |
42 | +case will need do install hicolor-icon-theme as well bacause tango-icon-theme | |
43 | +depends on it. After that you can try the command line above again, but | |
44 | +without "tango-icon-theme". | |
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. | |
39 | 49 | |
40 | 50 | === Setting up a production environment |
41 | 51 | |
52 | +DISCLAIMER: this installation procedure is tested with Debian stable, which is | |
53 | +currently the only recommended operating system for production usage. It is | |
54 | +possible that you can install it on other systems, and if you do so, please | |
55 | +report it on one of the Noosfero mailing lists, and please send a patch | |
56 | +updating these instructions. | |
57 | + | |
58 | +As root user | |
59 | +============ | |
60 | + | |
42 | 61 | NOTE: these instructions are for seting up a *production* environment. If you |
43 | -are going to do Noosfero development, you don't need to do these steps. See the | |
44 | -HACKING file instead. | |
62 | +are going to do Noosfero development, you don't need to do these steps. Stop | |
63 | +here and see the HACKING file instead. | |
64 | + | |
65 | +Install memcached. On Debian: | |
66 | + | |
67 | +# apt-get install memcached | |
68 | + | |
69 | +Study whether you need to raise the ammount of memory it uses for caching, | |
70 | +depending on the demand you expect for your site. If you are going to run a | |
71 | +high-traffic site, you will want to raise the ammount of memory reserved for | |
72 | +caching. | |
73 | + | |
74 | +It is recommended that you run noosfero with its own user account. To create | |
75 | +such an account, please do the following: | |
76 | + | |
77 | +# adduser --system --group noosfero --shell /bin/sh --home /var/lib/noosfero | |
78 | + | |
79 | +(note that you can change the $HOME directory of the user if you wish, here we | |
80 | +are using /var/lib/noosfero) | |
81 | + | |
82 | +The --system option will tell adduser to create a system user, i.e. this user | |
83 | +will not have a password and cannot login to the system directly. To become | |
84 | +this user, you have to use sudo: | |
85 | + | |
86 | +# sudo -u noosfero -i | |
87 | + | |
88 | +or | |
89 | + | |
90 | +# su - noosfero | |
91 | + | |
92 | +As noosfero user | |
93 | +================ | |
94 | + | |
95 | +downloading from git | |
96 | +-------------------- | |
97 | + | |
98 | +Here we are cloning the noosfero repository from git. Note: you will need to | |
99 | +install git before. | |
100 | + | |
101 | +$ git clone git://git.colivre.coop.br/noosfero.git current | |
102 | +$ cd current | |
103 | +$ git checkout -b stable origin/stable | |
104 | + | |
105 | +downloading tarball | |
106 | +------------------- | |
107 | + | |
108 | +Note: replace 0.27.1 below from the latest stable version. | |
109 | + | |
110 | +$ wget http://noosfero.org/pub/Development/NoosferoVersion00x27x01/noosfero-0.27.1.tar.gz | |
111 | +$ tar -zxvf noosfero-0.27.1.tar.gz | |
112 | +$ ln -s noosfero-0.27.1 current | |
113 | +$ cd current | |
114 | + | |
115 | +Copy config/ferret_server.yml.dist to config/ferret_server.yml. You will | |
116 | +probably not need to customize this configuration, but have a look at it. | |
117 | + | |
118 | +Create the mongrel configuration file: | |
119 | + | |
120 | +$ mongrel_rails cluster::configure | |
121 | + | |
122 | +Edit config/mongrel_cluster.yml to suit your needs. Make sure your apache | |
123 | +configuration matches the mongrel cluster configuration, specially in respect | |
124 | +to the ports and numbers of mongrel instances. | |
125 | + | |
126 | +Note: currently Noosfero only supports Rails 2.1.0, which is the version in | |
127 | +Debian Lenny. If you have a Rails version newer than that, Noosfero will | |
128 | +probably not work. You can install Rails 2.1.0 into your Noosfero installation | |
129 | +with the following procedure: | |
130 | + | |
131 | +$ cd /var/lib/noosfero/current/vendor | |
132 | +$ wget http://ftp.de.debian.org/debian/pool/main/r/rails/rails_2.1.0.orig.tar.gz | |
133 | +$ tar xzf rails_2.1.0.orig.tar.gz | |
134 | +$ ln -s rails-2.1.0 rails | |
135 | + | |
136 | +As root user | |
137 | +============ | |
138 | + | |
139 | +Setup Noosfero log and tmp directories: | |
140 | + | |
141 | +# cd /var/lib/noosfero/current | |
142 | +# ./etc/init.d/noosfero setup | |
143 | + | |
144 | +Now it's time to setup the database. In this example we are using PostgreSQL, | |
145 | +so if you are planning to use a different database this steps won't apply. | |
146 | + | |
147 | +# apt-get install postgresql libpgsql-ruby | |
148 | +# su postgres -c 'createuser noosfero -S -d -R' | |
149 | + | |
150 | +By default Rails will try to connect on postgresql through 5432 port, but | |
151 | +Debian start postgresql on port 5433, then is needed to change postgresql to | |
152 | +start on port 5432 in /etc/postgresql/8.3/main/postgresql.conf file. | |
153 | + | |
154 | +Restart postgresql: | |
155 | + | |
156 | +# invoke-rc.d postgresql-8.3 restart | |
157 | + | |
158 | +Noosfero needs a functional e-mail setup to work: the local mail system should | |
159 | +be able to deliver e-mail to the internet, either directly or through an | |
160 | +external SMTP server. | |
161 | + | |
162 | +If you know mail systems well, you just need to make sure thet the local MTA, | |
163 | +listening on localhost:25, is able to deliver e-mails to the internet. Any mail | |
164 | +server will do it. | |
165 | + | |
166 | +If you are not a mail specialist, we suggest that you use the Postfix mail | |
167 | +server, since it is easy to configure and very reliable. Just follow the | |
168 | +instructions below. | |
169 | + | |
170 | +To install Postfix: | |
171 | + | |
172 | +# apt-get install postfix | |
173 | + | |
174 | +During the installation process, you will be asked a few questions. Your answer | |
175 | +to them will vary in 2 cases: | |
176 | + | |
177 | +Case 1: you can send e-mails directly to the internet. This will be the case | |
178 | +for most commercial private servers. Your answers should be: | |
179 | + | |
180 | + General type of mail configuration: Internet site | |
181 | + System mail name: the name of your domain, e.g. "mysocialnetwork.com" | |
182 | + | |
183 | +Case 2: you cannot, or don't want to, send e-mail directly to the internet. | |
184 | +This happens for example if your server is not allowed to make outbound | |
185 | +connections on port 25, or if you want to concentrate all your outbound mail | |
186 | +through a single SMTP server. Your answers in this case should be: | |
187 | + | |
188 | + General type of mail configuration: Internet with smarthost | |
189 | + System mail name: the name of your domain, e.g. "mysocialnetwork.com" | |
190 | + SMTP relay host: smtp.yourprovider.com | |
191 | + | |
192 | +Note that smtp.yourprovider.com must allow your server to deliver e-mails | |
193 | +through it. You should probably ask your servive provider about this. | |
194 | + | |
195 | +There is another possibility: if you are installing on a shared server, and | |
196 | +don't have permission to configure the local MTA, you can instruct Noosfero to | |
197 | +send e-mails directly through an external server. Please note that this should | |
198 | +be your last option, since contacting an external SMTP server directly may slow | |
199 | +down your Noosfero application server. To configure Noosfero to send e-mails | |
200 | +through an external SMTP server, follow the instructions on | |
201 | +http://noosfero.org/Development/SMTPMailSending | |
45 | 202 | |
46 | -* install memcached. Study whether you need to raise the ammount of memory it uses for caching, depending on the demand you expect for your site. | |
47 | -* enter the directory where you unpacked noosfero | |
48 | -* copy config/ferret_server.yml.dist to config/ferret_server.yml | |
49 | -* create the mongrel configuration file: `mongrel_rails cluster::configure` | |
50 | -** then edit config/mongrel_cluster.yml to suit your environment. Make sure your apache configuration matches the mongrel cluster configuration, specially in respect to the ports and numbers of mongrel instances. | |
51 | -* create needed temporary directories: | |
52 | - mkdir tmp | |
53 | - mkdir tmp/pids | |
54 | - mkdir log | |
55 | -* create database (example using PostgreSQL, YMMV) | |
203 | +As noosfero user | |
204 | +================ | |
56 | 205 | |
57 | - root user | |
58 | - ========= | |
59 | - # sudo apt-get install postgresql libpgsql-ruby | |
60 | - # su - postgres | |
206 | +Now create the databases: | |
61 | 207 | |
62 | - postgres user | |
63 | - ============= | |
64 | - postgres@HOST:~$ createuser noosfero | |
65 | - Shall the new role be a superuser? (y/n) n | |
66 | - Shall the new role be allowed to create databases? (y/n) y | |
67 | - Shall the new role be allowed to create more new roles? (y/n) n | |
208 | +$ cd /var/lib/noosfero/current | |
209 | +$ createdb noosfero_production | |
210 | +$ createdb noosfero_development | |
211 | +$ createdb noosfero_test | |
68 | 212 | |
69 | - noosfero_user | |
70 | - ============= | |
71 | - createdb noosfero_production | |
72 | - createdb noosfero_development | |
73 | - createdb noosfero_test | |
213 | +The development and test databases are actually optional. If you are creating a | |
214 | +stricly production server, you will probably not need them. | |
74 | 215 | |
75 | -* configure database access in config/database.yml | |
216 | +Now we want to configure Noosfero for accessing the database we just created. | |
217 | +To do that, you can 1) copy config/database.yml.pgsql to config/database.yml, | |
218 | +or create config/database.yml from scratch with the following content: | |
76 | 219 | |
77 | -* test database access: | |
78 | -** first create the development database | |
79 | - rake db:schema:load | |
80 | -** if everything goes right, then create the production database: | |
81 | - RAILS_ENV=production rake db:schema:load | |
220 | + production: | |
221 | + adapter: postgresql | |
222 | + encoding: unicode | |
223 | + database: noosfero_production | |
224 | + username: noosfero | |
82 | 225 | |
83 | -* create sample data: | |
84 | - RAILS_ENV=production rake db:populate | |
226 | +Now, to test the database access, you can fire the Rails database console: | |
85 | 227 | |
86 | -* Add the domain name you will be using for your noosfero site to the list of | |
87 | - environment domain, like this: | |
228 | +$ ./script/dbconsole production | |
88 | 229 | |
89 | - ./script/runner "Environment.default.domains << Domain.new(:name => 'your.domain.com')" | |
230 | +If it connects to your database, then everything is fine. If you got an error | |
231 | +message, then you have to check your database configuration. | |
90 | 232 | |
91 | - (of course, replace your.domain.com with your real domain) | |
233 | +Create the database structure: | |
92 | 234 | |
93 | -* compile the translations: | |
94 | - rake makemo | |
235 | +$ RAILS_ENV=production rake db:schema:load | |
95 | 236 | |
96 | -* start the server: | |
97 | - ./script/production start | |
237 | +Now we have to create some initial data. To create your default environment | |
238 | +(the first one), run the command below: | |
98 | 239 | |
99 | -* to stop the server: | |
240 | +$ RAILS_ENV=production ./script/runner 'Environment.create!(:name => "My environment", :is_default => true)' | |
100 | 241 | |
101 | - ./script/production stop | |
242 | +(of course, replace "My environment" with your environment's name!) | |
102 | 243 | |
103 | -* to restart the server: | |
244 | +And now you have to add the domain name you will be using for your noosfero | |
245 | +site to the list of domains of that default environment you just created: | |
104 | 246 | |
105 | - ./script/production restart | |
247 | +$ RAILS_ENV=production ./script/runner "Environment.default.domains << Domain.new(:name => 'your.domain.com')" | |
106 | 248 | |
107 | -* enable the following apache modules: | |
249 | +(replace "your.domain.com" with your actual domain name) | |
250 | + | |
251 | +Add at least one user as admin of environment: | |
252 | + | |
253 | +$ RAILS_ENV=production ./script/runner "User.create(:login => 'adminuser', :email => 'admin@example.com', :password => 'admin', :password_confirmation => 'admin', :environment => Environment.default)" | |
254 | + | |
255 | +(replace "adminuser", "admin@example.com", "admin" with the login, email | |
256 | +and password of your environment admin) | |
257 | + | |
258 | +Compile the translations: | |
259 | + | |
260 | +$ RAILS_ENV=production rake noosfero:translations:compile | |
261 | + | |
262 | +To start the Noosfero application servers: | |
263 | + | |
264 | +$ ./script/production start | |
265 | + | |
266 | +At this point you have a functional Noosfero installation running, the only | |
267 | +thing left is to configure your webserver as a reverse proxy to pass requests | |
268 | +to them. | |
269 | + | |
270 | +Enabling exception notifications | |
271 | +================================ | |
272 | + | |
273 | +This is an optional step. You will need it only if you want to receive e-mail | |
274 | +notifications when some exception occurs on Noosfero. | |
275 | + | |
276 | +First, install this version of the gem. | |
277 | +Others versions may not be compatible with Noosfero: | |
278 | + | |
279 | +# gem install exception_notification -v 1.0.20090728 | |
280 | + | |
281 | +You can configure the e-mails that will receive the notifications. | |
282 | +Change the file config/noosfero.yml as the following example, replacing the | |
283 | +e-mails by real ones: | |
284 | + | |
285 | + production: | |
286 | + exception_recipients: [admin@example.com, you@example.com] | |
287 | + | |
288 | +================== | |
289 | +Apache instalation | |
290 | +================== | |
291 | + | |
292 | +# apt-get install apache2 | |
293 | + | |
294 | +Apache configuration | |
295 | +-------------------- | |
296 | + | |
297 | +First you have to enable the following some apache modules: | |
108 | 298 | |
109 | 299 | deflate |
110 | 300 | expires |
... | ... | @@ -113,26 +303,32 @@ HACKING file instead. |
113 | 303 | proxy_http |
114 | 304 | rewrite |
115 | 305 | |
116 | - On Debian GNU/Linux system, these modules can be enabled with the following | |
117 | - command line, as root: | |
306 | +On Debian GNU/Linux system, these modules can be enabled with the following | |
307 | +command line, as root: | |
118 | 308 | |
119 | - a2enmod deflate expires proxy proxy_balancer proxy_http rewrite | |
309 | +# a2enmod deflate expires proxy proxy_balancer proxy_http rewrite | |
120 | 310 | |
121 | - In other systems the way by which you enable apache modules may be different. | |
311 | +In other systems the way by which you enable apache modules may be different. | |
122 | 312 | |
123 | -* Configure apache web server as a reverse proxy. You can use the template | |
124 | - below, replacing /path/to/noosfero with the directory in which your noosfero | |
125 | - installation is, your.domain.com with the domain name of your noosfero site. | |
126 | - We are assuming that you are running two mongrel instances on ports 4000 and | |
127 | - 4001. If your setup is different you'll need to adjust <Proxy> section. If | |
128 | - you don't understand something in the configuration, please refer to the | |
129 | - apache documentation. | |
313 | +Now with the Apache configuration. You can use the template below, replacing | |
314 | +/var/lib/noosfero/current with the directory in which your noosfero | |
315 | +installation is, your.domain.com with the domain name of your noosfero site. | |
316 | +We are assuming that you are running two mongrel instances on ports 3000 and | |
317 | +3001. If your setup is different you'll need to adjust <Proxy> section. If you | |
318 | +don't understand something in the configuration, please refer to the apache | |
319 | +documentation. | |
320 | + | |
321 | +Add a file called "mysite" (or whatever name you want to give to your noosfero | |
322 | +site) to /etc/apache2/sites-available with the following content, and customize | |
323 | +as needed (as usual, make sure you replace "your.domain.com" with you actual | |
324 | +domain name, and "/var/lib/noosfero/current" with the directory where Noosfero | |
325 | +is installed): | |
130 | 326 | |
131 | 327 | <VirtualHost *:80> |
132 | 328 | ServerName your.domain.com |
133 | 329 | |
134 | - DocumentRoot "/path/to/noosfero/public" | |
135 | - <Directory "/path/to/noosfero/public"> | |
330 | + DocumentRoot "/var/lib/noosfero/current/public" | |
331 | + <Directory "/var/lib/noosfero/current/public"> | |
136 | 332 | Options FollowSymLinks |
137 | 333 | AllowOverride None |
138 | 334 | Order Allow,Deny |
... | ... | @@ -156,18 +352,110 @@ HACKING file instead. |
156 | 352 | LogLevel warn |
157 | 353 | CustomLog /var/log/apache2/noosfero.access.log combined |
158 | 354 | |
159 | - Include /path/to/noosfero/etc/noosfero/apache/cache.conf | |
355 | + Include /var/lib/noosfero/current/etc/noosfero/apache/cache.conf | |
160 | 356 | |
161 | 357 | </VirtualHost> |
162 | 358 | |
163 | 359 | <Proxy balancer://noosfero> |
164 | - BalancerMember http://127.0.0.1:4000 | |
165 | - BalancerMember http://127.0.0.1:4001 | |
360 | + BalancerMember http://127.0.0.1:3000 | |
361 | + BalancerMember http://127.0.0.1:3001 | |
166 | 362 | Order Allow,Deny |
167 | 363 | Allow from All |
168 | 364 | </Proxy> |
169 | 365 | |
170 | - The cache.conf file included in the end of the <VirtualHost> section is | |
171 | - important, since it will tell apache to pass expiration and cache headers to | |
172 | - clients so that the site feels faster for users. Do we need to say that using | |
173 | - that configuration is strongly recommended? | |
366 | +The cache.conf file included in the end of the <VirtualHost> section is | |
367 | +important, since it will tell apache to pass expiration and cache headers to | |
368 | +clients so that the site feels faster for users. Do we need to say that using | |
369 | +that configuration is strongly recommended? | |
370 | + | |
371 | +Enable that site with (as root, replace "mysite" with the actual name you gave | |
372 | +to your site configuration): | |
373 | + | |
374 | +# a2ensite mysite | |
375 | + | |
376 | +Now restart your apache server (as root): | |
377 | + | |
378 | +# invoke-rc.d apache2 restart | |
379 | + | |
380 | +============ | |
381 | +Maintainance | |
382 | +============ | |
383 | + | |
384 | +To ease the maintainance, install a symbolic link for the Noosfero startup | |
385 | +script in your server and add it to the system initialization and shutdown | |
386 | +sequences (as root): | |
387 | + | |
388 | +# ln -s /var/lib/noosfero/current/etc/init.d/noosfero /etc/init.d/noosfero | |
389 | +# update-rc.d noosfero defaults | |
390 | + Adding system startup for /etc/init.d/noosfero ... | |
391 | + /etc/rc0.d/K20noosfero -> ../init.d/noosfero | |
392 | + /etc/rc1.d/K20noosfero -> ../init.d/noosfero | |
393 | + /etc/rc6.d/K20noosfero -> ../init.d/noosfero | |
394 | + /etc/rc2.d/S20noosfero -> ../init.d/noosfero | |
395 | + /etc/rc3.d/S20noosfero -> ../init.d/noosfero | |
396 | + /etc/rc4.d/S20noosfero -> ../init.d/noosfero | |
397 | + /etc/rc5.d/S20noosfero -> ../init.d/noosfero | |
398 | + | |
399 | +Now to start Noosfero, you do as root: | |
400 | + | |
401 | +# invoke-rc.d noosfero start | |
402 | + | |
403 | +To stop Noosfero: | |
404 | + | |
405 | +# invoke-rc.d noosfero start | |
406 | + | |
407 | +To restart Noosfero: | |
408 | + | |
409 | +# invoke-rc.d noosfero restart | |
410 | + | |
411 | +Noosfero will be automatically started during system boot, and automatically | |
412 | +stopped if the system shuts down for some reason (or during the shutdown part | |
413 | +of a reboot). | |
414 | + | |
415 | +============= | |
416 | +Rotating logs | |
417 | +============= | |
418 | + | |
419 | +Noosfero provides an example logrotate configuation to rotate its logs. To use | |
420 | +it, create a symbolic link in /etc/logrotate.d/: | |
421 | + | |
422 | +# cd /etc/logrotate.d/ | |
423 | +# ln -s /var/lib/noosfero/current/etc/logrotate.d/noosfero | |
424 | + | |
425 | +Note that the provided file assumes Noosfero logging is being done in | |
426 | +/var/log/noosfero (which is the case if you followed the instructions above | |
427 | +correctly). If the logs are stored elsewhere, it's recommended that you copy | |
428 | +the file over to /etc/logrotate.d/ and modify it to point to your local log | |
429 | +directly. | |
430 | + | |
431 | +========= | |
432 | +Upgrading | |
433 | +========= | |
434 | + | |
435 | +If you followed the steps in this document and installed Noosfero from the git | |
436 | +repository, then upgrading is easy. First, you need to allow the noosfero user | |
437 | +to restart the memcached server with sudo, by adding the following line in | |
438 | +/etc/sudoers: | |
439 | + | |
440 | +noosfero ALL=NOPASSWD: /etc/init.d/memcached | |
441 | + | |
442 | +Then, to perform an upgrade, do the following as the noosfero user: | |
443 | + | |
444 | +$ cd /var/lib/noosfero/current | |
445 | +$ ./script/git-upgrade | |
446 | + | |
447 | +The git-upgrade script will take care of everything for you. It will first stop | |
448 | +the service, then fetch the current source code, upgrade database, compile | |
449 | +translations, and then start the service again. | |
450 | + | |
451 | +Note 1: make sure your local git repository is following the "stable" branch, | |
452 | +just like the instructions above. The "master" branch is not recommended for | |
453 | +use in production environments. | |
454 | + | |
455 | +Note 2: always read the release notes before upgrading. Sometimes there will be | |
456 | +steps that must be performed manually. If that is the case, you can invoke the | |
457 | +git-upgrade script with the special parameter "--shell" that will give you a | |
458 | +shell after the upgrade, which you can use to perform any manual steps | |
459 | +required: | |
460 | + | |
461 | +$ ./script/git-upgrade --shell | ... | ... |
... | ... | @@ -0,0 +1,255 @@ |
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 Lenny: | |
10 | + | |
11 | +# apt-get install librestclient-ruby (from backports) | |
12 | +# apt-get install pidgin-data | |
13 | +# apt-get install ruby1.8-dev | |
14 | +# gem install SystemTimer | |
15 | + | |
16 | +Take a look at util/chat directory to see samples of config file to configure a | |
17 | +XMPP/BOSH server with ejabberd, postgresql and apache2. | |
18 | + | |
19 | +== XMPP/Chat Server Setup | |
20 | + | |
21 | +This is a step-by-step guide to get a XMPP service working, in a Debian system. | |
22 | + | |
23 | +1. Install the required packages | |
24 | + | |
25 | +# apt-get -t lenny-backports install ejabberd | |
26 | +# apt-get install odbc-postgresql | |
27 | + | |
28 | +2. Ejabberd configuration | |
29 | + | |
30 | +All the following changes must be done in config file: | |
31 | + | |
32 | + /etc/ejabberd/ejabberd.cfg | |
33 | + | |
34 | + 2.1. Set the default admin user | |
35 | + | |
36 | +{ acl, admin, { user, "john", "www.example.com" } }. | |
37 | +{ acl, admin, { user, "bart", "www.example.com" } }. | |
38 | + | |
39 | + 2.2. Set the default host | |
40 | + | |
41 | +{ hosts, [ "www.example.com" ] }. | |
42 | + | |
43 | + 2.3. Http-Bind activation | |
44 | + | |
45 | +{ 5280, ejabberd_http, [ | |
46 | + http_bind, | |
47 | + web_admin | |
48 | + ] | |
49 | +} | |
50 | + | |
51 | +(...) | |
52 | + | |
53 | +{ modules, [ | |
54 | + {mod_http_bind, []}, | |
55 | + ... | |
56 | +] }. | |
57 | + | |
58 | +Ejabberd creates semi-anonymous rooms by default, but Noosfero's Jabber client | |
59 | +needs non-anonymous room, then we need to change default params of creation | |
60 | +rooms in ejabberd to create non-anonymous rooms. | |
61 | + | |
62 | +In non-anonymous rooms the jabber service sends the new occupant's full JID to | |
63 | +all occupants in the room[1]. | |
64 | + | |
65 | +Add option "{default_room_options, [{anonymous, false}]}" to | |
66 | +/etc/ejabberd/ejabberd.cfg in mod_muc session. See below: | |
67 | + | |
68 | +{ mod_muc, [ | |
69 | + %%{host, "conference.@HOST@"}, | |
70 | + {access, muc}, | |
71 | + {access_create, muc}, | |
72 | + {access_persistent, muc}, | |
73 | + {access_admin, muc_admin}, | |
74 | + {max_users, 500}, | |
75 | + {default_room_options, [{anonymous, false}]} | |
76 | +]}, | |
77 | + | |
78 | +[1] - http://xmpp.org/extensions/xep-0045.html#enter-nonanon | |
79 | + | |
80 | + | |
81 | + 2.4. Authentication method | |
82 | + | |
83 | +To use Postgresql through ODBC, the following modifications must be done: | |
84 | + | |
85 | + * Disable the default method: | |
86 | + | |
87 | +{auth_method, internal}. | |
88 | + | |
89 | + * Enable autheticantion through ODBC: | |
90 | + | |
91 | +{auth_method, odbc}. | |
92 | + | |
93 | + * Set database server name | |
94 | + | |
95 | +{odbc_server, "DSN=PostgreSQLEjabberdNoosfero"}. | |
96 | + | |
97 | + | |
98 | + 2.5. Increase the shaper traffic limit | |
99 | + | |
100 | +{ shaper, normal, { maxrate, 10000000 } }. | |
101 | + | |
102 | + | |
103 | + 2.6. Disable unused modules | |
104 | + | |
105 | +Unused modules can be disabled, for example: | |
106 | + | |
107 | + * s2s | |
108 | + * web_admin | |
109 | + * mod_pubsub | |
110 | + * mod_irc | |
111 | + * mod_offine | |
112 | + * mod_admin_extra | |
113 | + * mod_register | |
114 | + | |
115 | + | |
116 | + 2.7. Enable ODBC modules | |
117 | + | |
118 | + * mod_privacy -> mod_privacy_odbc | |
119 | + * mod_private -> mod_private_odbc | |
120 | + * mod_roster -> mod_roster_odbc | |
121 | + | |
122 | +3. Configuring Postgresql | |
123 | + | |
124 | +Login as noosfero user, and execute: | |
125 | + | |
126 | + $ psql noosfero < /path/to/noosfero/util/chat/postgresql/ejabberd.sql | |
127 | + | |
128 | +Where 'noosfero' may need to be replace by the name of the database used for | |
129 | +Noosfero. | |
130 | + | |
131 | +This will create a new schema inside the noosfero database, called 'ejabberd'. | |
132 | + | |
133 | +Note 'noosfero' user should have permission to create Postgresql schemas. Also, | |
134 | +there should be at least one domain with 'is_default = true' in 'domains' | |
135 | +table, otherwise people couldn't see your friends online. | |
136 | + | |
137 | + | |
138 | +4. ODBC configuration | |
139 | + | |
140 | +The following files must be created: | |
141 | + | |
142 | + * /etc/odbc.ini | |
143 | + | |
144 | +[PostgreSQLEjabberdNoosfero] | |
145 | +Description = PostgreSQL Noosfero ejabberd database | |
146 | +Driver = PostgreSQL Unicode | |
147 | +Trace = No | |
148 | +TraceFile = /tmp/psqlodbc.log | |
149 | +Database = noosfero | |
150 | +Servername = localhost | |
151 | +UserName = <DBUSER> | |
152 | +Password = <DBPASS> | |
153 | +Port = | |
154 | +ReadOnly = No | |
155 | +RowVersioning = No | |
156 | +ShowSystemTables = No | |
157 | +ShowOidColumn = No | |
158 | +FakeOidIndex = No | |
159 | +ConnSettings = SET search_path TO ejabberd | |
160 | + | |
161 | + * /etc/odbcinst.ini | |
162 | + | |
163 | +[PostgreSQL Unicode] | |
164 | +Description = PostgreSQL ODBC driver (Unicode version) | |
165 | +Driver = /usr/lib/odbc/psqlodbcw.so | |
166 | +Setup = /usr/lib/odbc/libodbcpsqlS.so | |
167 | +Debug = 0 | |
168 | +CommLog = 1 | |
169 | +UsageCount = 3 | |
170 | + | |
171 | + * testing all: | |
172 | + | |
173 | +# isql 'PostgreSQLEjabberdNoosfero' DBUSER | |
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 | + | |
225 | +8. DNS configuration | |
226 | + | |
227 | + * /etc/bind/db.colivre: | |
228 | + | |
229 | +_xmpp-client._tcp SRV 5 100 5222 master | |
230 | +(...) | |
231 | +conference CNAME master | |
232 | +_xmpp-client._tcp.conference SRV 5 100 5222 master | |
233 | + | |
234 | + | |
235 | +9. Testing this Setup | |
236 | + | |
237 | +Adjust shell limits to proceed with some benchmarks and load tests: | |
238 | + | |
239 | +# ulimit −s 256 | |
240 | +# ulimit −n 8192 | |
241 | +# echo 10 > /proc/sys/net/ipv4/tcp_syn_retries | |
242 | + | |
243 | +To measure the bandwidth between server and client: | |
244 | + | |
245 | + * at server side: | |
246 | + | |
247 | +# iperf −s | |
248 | + | |
249 | + * at client side: | |
250 | + | |
251 | +# iperf −c server_ip | |
252 | + | |
253 | +For heavy load tests, clone and use this software: | |
254 | + | |
255 | +git clone http://git.holoscopio.com/git/metal/tester.git | ... | ... |
... | ... | @@ -0,0 +1,67 @@ |
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. | |
9 | + | |
10 | +2) install Varnish | |
11 | + | |
12 | + # apt-get install varnish | |
13 | + | |
14 | +Noosfero was tested with Varnish 2.x. If you are using a Debian Lenny (and you | |
15 | +should, unless Debian already released Squeeze by now), make sure you install | |
16 | +varnish from the lenny-backports suite. | |
17 | + | |
18 | +3) Enable varnish logging: | |
19 | + | |
20 | +3a) Edit /etc/default/varnishncsa and uncomment the line that contains: | |
21 | + | |
22 | +VARNISHNCSA_ENABLED=1 | |
23 | + | |
24 | +The varnish log will be written to /var/log/varnish/varnishncsa.log in an | |
25 | +apache-compatible format. You should change your statistics generation software | |
26 | +(e.g. awstats) to use that instead of apache logs. | |
27 | + | |
28 | +3b) Restart Varnish Logging service | |
29 | + | |
30 | + # invoke-rc.d varnishncsa start | |
31 | + | |
32 | +4) Change Apache to listen on port 8080 instead of 80 | |
33 | + | |
34 | +4a) Edit /etc/apache2/ports.conf, and: | |
35 | + | |
36 | + * change 'Listen 80' to 'Listen 127.0.0.1:8080' | |
37 | + * change 'NameVirtualHost *:80' to 'NameVirtualHost *:8080' | |
38 | + | |
39 | +4b) Edit /etc/apache2/sites-enabled/*, and change '<VirtualHost *:80>' to '<VirtualHost *:8080>' | |
40 | + | |
41 | +4c) Restart apache | |
42 | + | |
43 | + # invoke-rc.d apache2 restart | |
44 | + | |
45 | +5) Change Varnish to listen on port 80 | |
46 | + | |
47 | +5a) Edit /etc/default/varnish and change '-a :6081' to '-a :80' | |
48 | + | |
49 | +5b) Restart Varnish | |
50 | + | |
51 | + # invoke-rc.d varnish restart | |
52 | + | |
53 | +6) Configure varnish to store separate caches for each language | |
54 | + | |
55 | +6a) Add the following line to your /etc/varnish/default.vcl file (assuming | |
56 | +Noosfero is installed in /var/lib/noosfero): | |
57 | + | |
58 | + include "/var/lib/noosfero/etc/noosfero/varnish-accept-language.vcl"; | |
59 | + | |
60 | +6b) Restart Varnish | |
61 | + | |
62 | + # invoke-rc.d varnish restart | |
63 | + | |
64 | +Thanks to Cosimo Streppone for varnish-accept-language. See | |
65 | +http://github.com/cosimo/varnish-accept-language for more information. | |
66 | + | |
67 | + -- Antonio Terceiro <terceiro@colivre.coop.br> Sat, 04 Sep 2010 17:29:27 -0300 | ... | ... |
Rakefile.pkg
... | ... | @@ -1,101 +0,0 @@ |
1 | -require(File.join(File.dirname(__FILE__), 'config', 'boot')) | |
2 | - | |
3 | -require 'rake' | |
4 | -require 'rake/testtask' | |
5 | -require 'rake/rdoctask' | |
6 | -require 'rake/packagetask' | |
7 | -require 'noosfero' | |
8 | - | |
9 | -Rake::PackageTask.new(Noosfero::PROJECT, Noosfero::VERSION) do |p| | |
10 | - p.need_tar_gz = true | |
11 | - | |
12 | - # cleaning temporary files | |
13 | - FileUtils.rm_rf('tmp') | |
14 | - FileUtils.mkdir_p('tmp/cache') | |
15 | - FileUtils.mkdir_p('tmp/sessions') | |
16 | - FileUtils.mkdir_p('tmp/sockets') | |
17 | - | |
18 | - # application files | |
19 | - p.package_files.include('app/**/*.{rb,rhtml,rjs,rxml,erb}') | |
20 | - p.package_files.include('config/**/*.{rb,sqlite3}') | |
21 | - p.package_files.include('config/ferret_server.yml.dist') | |
22 | - p.package_files.include('db/migrate/*.rb') | |
23 | - p.package_files.include('db/schema.rb') | |
24 | - p.package_files.include('doc/README_FOR_APP') | |
25 | - p.package_files.include('lib/**/*.{rake,rb}') | |
26 | - p.package_files.include('Rakefile') | |
27 | - p.package_files.include('Rakefile.pkg') | |
28 | - | |
29 | - # translation files | |
30 | - p.package_files.include('po/*/*.po') | |
31 | - p.package_files.include('po/noosfero.pot') | |
32 | - | |
33 | - # templates | |
34 | - p.package_files.include('public/designs/templates/**/*') | |
35 | - | |
36 | - # icon sets | |
37 | - p.package_files.include('public/designs/icons/tango/**/*') | |
38 | - p.package_files.exclude('public/designs/icons/tango/Tango') | |
39 | - p.package_files.exclude('public/designs/icons/default') | |
40 | - | |
41 | - # themes | |
42 | - p.package_files.include('public/designs/themes/noosfero/**/*') | |
43 | - p.package_files.include('public/designs/themes/base/**/*') | |
44 | - p.package_files.exclude('public/designs/themes/default') | |
45 | - | |
46 | - # static files | |
47 | - p.package_files.include('public/dispatch.*') | |
48 | - p.package_files.include('public/favicon.ico') | |
49 | - p.package_files.include('public/*.html') | |
50 | - p.package_files.include('public/*.html.erb') | |
51 | - p.package_files.include('public/images/**/*') | |
52 | - p.package_files.include('public/javascripts/**/*') | |
53 | - p.package_files.include('public/robots.txt') | |
54 | - p.package_files.include('public/stylesheets/**/*') | |
55 | - | |
56 | - # top-level docs | |
57 | - p.package_files.include('README') | |
58 | - p.package_files.include('COPYING') | |
59 | - p.package_files.include('COPYRIGHT') | |
60 | - p.package_files.include('INSTALL') | |
61 | - p.package_files.include('HACKING') | |
62 | - | |
63 | - # scripts | |
64 | - p.package_files.include('script/**/*') | |
65 | - | |
66 | - # test files | |
67 | - p.package_files.include('test/**/*.{rb,yml}') | |
68 | - p.package_files.include('test/fixtures/files/*') | |
69 | - p.package_files.include('features/**/*') | |
70 | - p.package_files.include('config/cucumber.yml') | |
71 | - | |
72 | - # empty directories that must exist | |
73 | - p.package_files.include('tmp/cache') | |
74 | - p.package_files.include('tmp/sessions') | |
75 | - p.package_files.include('tmp/sockets') | |
76 | - p.package_files.include('log') | |
77 | - | |
78 | - # symbolic links | |
79 | - p.package_files.include('app/views/profile_design/*') | |
80 | - p.package_files.include('app/views/environment_design/*') | |
81 | - | |
82 | - # util | |
83 | - p.package_files.include('util/**/*') | |
84 | - | |
85 | - # external resources | |
86 | - p.package_files.include('vendor/**/*') | |
87 | - p.package_files.exclude('vendor/rails') | |
88 | - | |
89 | - # online documentation | |
90 | - p.package_files.include('doc/noosfero/**/*') | |
91 | - | |
92 | - # do not install locally generated files | |
93 | - p.package_files.exclude('coverage/**/*') | |
94 | - p.package_files.exclude('public/images/[0-9][0-9][0-9][0-9]/**/*') | |
95 | - | |
96 | -end | |
97 | - | |
98 | -task :default => :package | |
99 | -task :clean => :clobber_package | |
100 | - | |
101 | -# vim: ft=ruby |
UPGRADE
... | ... | @@ -1,49 +0,0 @@ |
1 | -0 - All modified code have the tag UPGRADE on it. | |
2 | - | |
3 | -1 - I Changed acts_as_versioned plugin code to the new one: | |
4 | - | |
5 | - http://github.com/technoweenie/acts_as_versioned/tree/master | |
6 | - | |
7 | -2 - I removed the 'build_person' calls after create the user on UserTest | |
8 | - | |
9 | -3 - The settings_items method generated a lot of bugs. Theses bugs are explained here: | |
10 | - Solution 1: http://casperfabricius.com/site/2008/06/12/serialize-doesnt-play-nice-with-rails-21-partial-updates/ | |
11 | - Solution 2: http://www.kalzumeus.com/2008/06/28/rails-fails-to-update-serialized-columns-on-save/ | |
12 | - | |
13 | -The problem is how rails is working with ActiveRecord. The serialization data was not save on database. | |
14 | - | |
15 | -I add this code | |
16 | - | |
17 | - send(self.class.settings_field.to_s + '_will_change!') | |
18 | - | |
19 | -on lib/acts_as_having_settings | |
20 | - | |
21 | -I used the solution 1. | |
22 | - | |
23 | -I choose solution number 2 for Environment model. I think it is more clear in this situation. | |
24 | -I put the code: | |
25 | - | |
26 | - self.partial_updates = false | |
27 | - | |
28 | -on Environment model. | |
29 | - | |
30 | - | |
31 | -4 - acts_as_searchable -> find_by_contents | |
32 | - | |
33 | -I solved an incompatibility between paginate and geokit with an work around like this the one explained on the link: | |
34 | - | |
35 | - http://groups.google.com/group/will_paginate/browse_thread/thread/1bbb1f0c5810f1c1 | |
36 | - | |
37 | -5 - The test of this method 'theme_include' of application helper was not passing. | |
38 | - | |
39 | -I changed the tests | |
40 | - - "should 'render theme footer' do" | |
41 | - - " should 'ignore unexisting theme footer' do" | |
42 | - | |
43 | - to make the test usefull | |
44 | - | |
45 | - | |
46 | -6 - I changed the way as the product is created on ManageProducts controller. I removed the line: | |
47 | - @product.build_image unless @product.image | |
48 | - | |
49 | -because the image was never saved. It don't have valid values to be saved. |
app/controllers/admin/admin_panel_controller.rb
... | ... | @@ -40,7 +40,7 @@ class AdminPanelController < AdminController |
40 | 40 | end |
41 | 41 | redirect_to :action => 'set_portal_folders' |
42 | 42 | else |
43 | - flash[:notice] = __('Community not found. You must insert the identifier of a community from this environment') | |
43 | + session[:notice] = __('Community not found. You must insert the identifier of a community from this environment') | |
44 | 44 | end |
45 | 45 | end |
46 | 46 | end |
... | ... | @@ -54,7 +54,7 @@ class AdminPanelController < AdminController |
54 | 54 | folders = params[:folders].map{|fid| Folder.find(:first, :conditions => {:profile_id => env.portal_community, :id => fid})} if params[:folders] |
55 | 55 | env.portal_folders = folders |
56 | 56 | if env.save |
57 | - flash[:notice] = _('Saved the portal folders') | |
57 | + session[:notice] = _('Saved the portal folders') | |
58 | 58 | redirect_to :action => 'set_portal_news_amount' |
59 | 59 | end |
60 | 60 | end |
... | ... | @@ -63,7 +63,7 @@ class AdminPanelController < AdminController |
63 | 63 | def set_portal_news_amount |
64 | 64 | if request.post? |
65 | 65 | if @environment.update_attributes(params[:environment]) |
66 | - flash[:notice] = _('Saved the number of news on folders') | |
66 | + session[:notice] = _('Saved the number of news on folders') | |
67 | 67 | redirect_to :action => 'index' |
68 | 68 | end |
69 | 69 | end | ... | ... |
app/controllers/admin/categories_controller.rb
... | ... | @@ -5,9 +5,9 @@ class CategoriesController < AdminController |
5 | 5 | helper :categories |
6 | 6 | |
7 | 7 | def index |
8 | - # WORKAROUND: restricting the category trees to display. Region and | |
9 | - # ProductCategory have VERY LARGE trees. | |
10 | 8 | @categories = environment.categories.find(:all, :conditions => "parent_id is null AND type is null") |
9 | + @regions = environment.regions.find(:all, :conditions => {:parent_id => nil}) | |
10 | + @product_categories = environment.product_categories.find(:all, :conditions => {:parent_id => nil}) | |
11 | 11 | end |
12 | 12 | |
13 | 13 | ALLOWED_TYPES = CategoriesHelper::TYPES.map {|item| item[1] } | ... | ... |
app/controllers/admin/environment_design_controller.rb
... | ... | @@ -3,7 +3,7 @@ class EnvironmentDesignController < BoxOrganizerController |
3 | 3 | protect 'edit_environment_design', :environment |
4 | 4 | |
5 | 5 | def available_blocks |
6 | - @available_blocks ||= [ ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, PeopleBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock ] | |
6 | + @available_blocks ||= [ ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, PeopleBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, FeaturedProductsBlock, CategoriesBlock, RawHTMLBlock ] | |
7 | 7 | end |
8 | 8 | |
9 | 9 | end | ... | ... |
app/controllers/admin/environment_role_manager_controller.rb
... | ... | @@ -14,9 +14,9 @@ class EnvironmentRoleManagerController < AdminController |
14 | 14 | @roles = params[:roles] ? Role.find(params[:roles]) : [] |
15 | 15 | @person = Person.find(params[:person]) |
16 | 16 | if @person.define_roles(@roles, environment) |
17 | - flash[:notice] = _('Roles successfuly updated') | |
17 | + session[:notice] = _('Roles successfuly updated') | |
18 | 18 | else |
19 | - flash[:notice] = _('Couldnt change the roles') | |
19 | + session[:notice] = _('Couldnt change the roles') | |
20 | 20 | end |
21 | 21 | redirect_to :action => :index |
22 | 22 | end |
... | ... | @@ -42,9 +42,9 @@ class EnvironmentRoleManagerController < AdminController |
42 | 42 | def remove_role |
43 | 43 | @association = RoleAssignment.find(params[:id]) |
44 | 44 | if @association.destroy |
45 | - flash[:notice] = _('Member succefully unassociated') | |
45 | + session[:notice] = _('Member succefully unassociated') | |
46 | 46 | else |
47 | - flash[:notice] = _('Failed to unassociate member') | |
47 | + session[:notice] = _('Failed to unassociate member') | |
48 | 48 | end |
49 | 49 | redirect_to :aciton => 'index' |
50 | 50 | end |
... | ... | @@ -52,9 +52,9 @@ class EnvironmentRoleManagerController < AdminController |
52 | 52 | def unassociate |
53 | 53 | @association = RoleAssignment.find(params[:id]) |
54 | 54 | if @association.destroy |
55 | - flash[:notice] = _('Member succefully unassociated') | |
55 | + session[:notice] = _('Member succefully unassociated') | |
56 | 56 | else |
57 | - flash[:notice] = _('Failed to unassociate member') | |
57 | + session[:notice] = _('Failed to unassociate member') | |
58 | 58 | end |
59 | 59 | redirect_to :aciton => 'index' |
60 | 60 | end | ... | ... |
app/controllers/admin/features_controller.rb
... | ... | @@ -8,7 +8,7 @@ class FeaturesController < AdminController |
8 | 8 | post_only :update |
9 | 9 | def update |
10 | 10 | if @environment.update_attributes(params[:environment]) |
11 | - flash[:notice] = _('Features updated successfully.') | |
11 | + session[:notice] = _('Features updated successfully.') | |
12 | 12 | redirect_to :action => 'index' |
13 | 13 | else |
14 | 14 | render :action => 'index' |
... | ... | @@ -24,7 +24,7 @@ class FeaturesController < AdminController |
24 | 24 | def manage_person_fields |
25 | 25 | environment.custom_person_fields = params[:person_fields] |
26 | 26 | if environment.save! |
27 | - flash[:notice] = _('Person fields updated successfully.') | |
27 | + session[:notice] = _('Person fields updated successfully.') | |
28 | 28 | else |
29 | 29 | flash[:error] = _('Person fields not updated successfully.') |
30 | 30 | end |
... | ... | @@ -34,9 +34,9 @@ class FeaturesController < AdminController |
34 | 34 | def manage_enterprise_fields |
35 | 35 | environment.custom_enterprise_fields = params[:enterprise_fields] |
36 | 36 | if environment.save! |
37 | - flash[:notice] = _('Enterprise fields updated successfully.') | |
37 | + session[:notice] = __('Enterprise fields updated successfully.') | |
38 | 38 | else |
39 | - flash[:error] = _('Enterprise fields not updated successfully.') | |
39 | + flash[:error] = __('Enterprise fields not updated successfully.') | |
40 | 40 | end |
41 | 41 | redirect_to :action => 'manage_fields' |
42 | 42 | end |
... | ... | @@ -44,7 +44,7 @@ class FeaturesController < AdminController |
44 | 44 | def manage_community_fields |
45 | 45 | environment.custom_community_fields = params[:community_fields] |
46 | 46 | if environment.save! |
47 | - flash[:notice] = _('Community fields updated successfully.') | |
47 | + session[:notice] = _('Community fields updated successfully.') | |
48 | 48 | else |
49 | 49 | flash[:error] = _('Community fields not updated successfully.') |
50 | 50 | end | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +class PluginsController < AdminController | |
2 | + | |
3 | + def index | |
4 | + @active_plugins = Noosfero::Plugin.all.map {|plugin_name| plugin_name.constantize }.compact | |
5 | + end | |
6 | + | |
7 | + post_only :update | |
8 | + def update | |
9 | + params[:environment][:enabled_plugins].delete('') | |
10 | + if @environment.update_attributes(params[:environment]) | |
11 | + session[:notice] = _('Plugins updated successfully.') | |
12 | + else | |
13 | + session[:error] = _('Plugins were not updated successfully.') | |
14 | + end | |
15 | + redirect_to :action => 'index' | |
16 | + end | |
17 | + | |
18 | +end | ... | ... |
app/controllers/admin/role_controller.rb
... | ... | @@ -19,7 +19,7 @@ class RoleController < AdminController |
19 | 19 | if @role.save |
20 | 20 | redirect_to :action => 'show', :id => @role |
21 | 21 | else |
22 | - flash[:notice] = _('Failed to create role') | |
22 | + session[:notice] = _('Failed to create role') | |
23 | 23 | render :action => 'new' |
24 | 24 | end |
25 | 25 | end |
... | ... | @@ -33,7 +33,7 @@ class RoleController < AdminController |
33 | 33 | if @role.update_attributes(params[:role]) |
34 | 34 | redirect_to :action => 'show', :id => @role |
35 | 35 | else |
36 | - flash[:notice] = _('Failed to edit role') | |
36 | + session[:notice] = _('Failed to edit role') | |
37 | 37 | render :action => 'edit' |
38 | 38 | end |
39 | 39 | end |
... | ... | @@ -43,7 +43,7 @@ class RoleController < AdminController |
43 | 43 | if @role.destroy |
44 | 44 | redirect_to :action => 'index' |
45 | 45 | else |
46 | - flash[:notice] = _('Failed to edit role') | |
46 | + session[:notice] = _('Failed to edit role') | |
47 | 47 | redirect_to :action => 'index' |
48 | 48 | end |
49 | 49 | end | ... | ... |
... | ... | @@ -0,0 +1,36 @@ |
1 | +class UsersController < AdminController | |
2 | + | |
3 | + protect 'manage_environment_users', :environment | |
4 | + | |
5 | + def index | |
6 | + @users = environment.users | |
7 | + respond_to do |format| | |
8 | + format.html | |
9 | + format.xml do | |
10 | + render :xml => @users.to_xml( | |
11 | + :skip_types => true, | |
12 | + :only => %w[email login created_at updated_at], | |
13 | + :include => { :person => {:only => %w[name updated_at created_at address birth_date contact_phone identifier lat lng] } } | |
14 | + ) | |
15 | + end | |
16 | + format.csv do | |
17 | + render :template => "users/index_csv.rhtml", :content_type => 'text/csv', :layout => false | |
18 | + end | |
19 | + end | |
20 | + end | |
21 | + | |
22 | + def send_mail | |
23 | + @mailing = environment.mailings.build(params[:mailing]) | |
24 | + if request.post? | |
25 | + @mailing.locale = locale | |
26 | + @mailing.person = user | |
27 | + if @mailing.save | |
28 | + session[:notice] = _('The e-mails are being sent') | |
29 | + redirect_to :action => 'index' | |
30 | + else | |
31 | + session[:notice] = _('Could not create the e-mail') | |
32 | + end | |
33 | + end | |
34 | + end | |
35 | + | |
36 | +end | ... | ... |
app/controllers/box_organizer_controller.rb
... | ... | @@ -96,7 +96,7 @@ class BoxOrganizerController < ApplicationController |
96 | 96 | expire_timeout_fragment(@block.cache_keys) |
97 | 97 | redirect_to :action => 'index' |
98 | 98 | else |
99 | - flash[:notice] = _('Failed to remove block') | |
99 | + session[:notice] = _('Failed to remove block') | |
100 | 100 | end |
101 | 101 | end |
102 | 102 | ... | ... |
app/controllers/my_profile/cms_controller.rb
... | ... | @@ -14,7 +14,8 @@ class CmsController < MyProfileController |
14 | 14 | end |
15 | 15 | end |
16 | 16 | |
17 | - protect_if :except => [:set_home_page, :edit, :destroy, :publish] do |c, user, profile| | |
17 | + before_filter :login_required, :except => [:suggest_an_article] | |
18 | + protect_if :except => [:suggest_an_article, :set_home_page, :edit, :destroy, :publish] do |c, user, profile| | |
18 | 19 | user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)) |
19 | 20 | end |
20 | 21 | |
... | ... | @@ -40,15 +41,11 @@ class CmsController < MyProfileController |
40 | 41 | def available_article_types |
41 | 42 | articles = [ |
42 | 43 | TinyMceArticle, |
43 | - TextileArticle | |
44 | + TextileArticle, | |
45 | + Event | |
44 | 46 | ] |
45 | - articles << Event unless profile.environment.enabled?(:disable_asset_events) | |
47 | + articles += special_article_types if params && params[:cms] | |
46 | 48 | parent_id = params ? params[:parent_id] : nil |
47 | - if !parent_id or !Article.find(parent_id).blog? | |
48 | - articles += [ | |
49 | - RssFeed | |
50 | - ] | |
51 | - end | |
52 | 49 | if profile.enterprise? |
53 | 50 | articles << EnterpriseHomepage |
54 | 51 | end |
... | ... | @@ -56,22 +53,31 @@ class CmsController < MyProfileController |
56 | 53 | end |
57 | 54 | |
58 | 55 | def special_article_types |
59 | - [Folder, Blog, UploadedFile] | |
56 | + [Folder, Blog, UploadedFile, Forum, Gallery, RssFeed] | |
60 | 57 | end |
61 | 58 | |
62 | 59 | def view |
63 | 60 | @article = profile.articles.find(params[:id]) |
64 | - @subitems = @article.children.reject {|item| item.folder? } | |
65 | - if @article.blog? | |
66 | - @subitems.reject! {|item| item.class == RssFeed } | |
61 | + conditions = [] | |
62 | + if @article.has_posts? | |
63 | + conditions = ['type != ?', 'RssFeed'] | |
67 | 64 | end |
68 | - @folders = @article.children.select {|item| item.folder? } | |
65 | + | |
66 | + @articles = @article.children.paginate( | |
67 | + :order => "case when type = 'Folder' then 0 when type ='Blog' then 1 else 2 end, updated_at DESC", | |
68 | + :conditions => conditions, | |
69 | + :per_page => per_page, | |
70 | + :page => params[:npage] | |
71 | + ) | |
69 | 72 | end |
70 | 73 | |
71 | 74 | def index |
72 | 75 | @article = nil |
73 | - @subitems = profile.top_level_articles.reject {|item| item.folder? } | |
74 | - @folders = profile.top_level_articles.select {|item| item.folder?} | |
76 | + @articles = profile.top_level_articles.paginate( | |
77 | + :order => "case when type = 'Folder' then 0 when type ='Blog' then 1 else 2 end, updated_at DESC", | |
78 | + :per_page => per_page, | |
79 | + :page => params[:npage] | |
80 | + ) | |
75 | 81 | render :action => 'view' |
76 | 82 | end |
77 | 83 | |
... | ... | @@ -79,17 +85,21 @@ class CmsController < MyProfileController |
79 | 85 | @article = profile.articles.find(params[:id]) |
80 | 86 | @parent_id = params[:parent_id] |
81 | 87 | @type = params[:type] || @article.class.to_s |
88 | + translations if @article.translatable? | |
89 | + continue = params[:continue] | |
82 | 90 | |
83 | 91 | refuse_blocks |
84 | - if !@article.nil? && @article.blog? || !@type.nil? && @type == 'Blog' | |
85 | - @back_url = url_for(:controller => 'profile_editor', :profile => profile.identifier) | |
86 | - end | |
87 | - record_coming_from_public_view | |
92 | + record_coming | |
88 | 93 | if request.post? |
89 | 94 | @article.last_changed_by = user |
90 | 95 | if @article.update_attributes(params[:article]) |
91 | - redirect_back | |
92 | - return | |
96 | + if !continue | |
97 | + if @article.content_type.nil? || @article.image? | |
98 | + redirect_to @article.view_url | |
99 | + else | |
100 | + redirect_to :action => (@article.parent ? 'view' : 'index'), :id => @article.parent | |
101 | + end | |
102 | + end | |
93 | 103 | end |
94 | 104 | end |
95 | 105 | end |
... | ... | @@ -99,6 +109,7 @@ class CmsController < MyProfileController |
99 | 109 | |
100 | 110 | # user must choose an article type first |
101 | 111 | |
112 | + record_coming | |
102 | 113 | @type = params[:type] |
103 | 114 | if @type.blank? |
104 | 115 | @article_types = [] |
... | ... | @@ -110,12 +121,9 @@ class CmsController < MyProfileController |
110 | 121 | }) |
111 | 122 | end |
112 | 123 | @parent_id = params[:parent_id] |
113 | - render :action => 'select_article_type', :layout => false | |
124 | + render :action => 'select_article_type', :layout => false, :back_to => @back_to | |
114 | 125 | return |
115 | 126 | else |
116 | - if @type == 'Blog' | |
117 | - @back_url = url_for(:controller => 'profile_editor', :profile => profile.identifier) | |
118 | - end | |
119 | 127 | refuse_blocks |
120 | 128 | end |
121 | 129 | |
... | ... | @@ -131,13 +139,19 @@ class CmsController < MyProfileController |
131 | 139 | @parent_id = parent.id |
132 | 140 | end |
133 | 141 | |
134 | - record_creating_from_public_view | |
142 | + translations if @article.translatable? | |
135 | 143 | |
136 | 144 | @article.profile = profile |
137 | 145 | @article.last_changed_by = user |
146 | + | |
147 | + continue = params[:continue] | |
138 | 148 | if request.post? |
139 | 149 | if @article.save |
140 | - redirect_back | |
150 | + if continue | |
151 | + redirect_to :action => 'edit', :id => @article | |
152 | + else | |
153 | + redirect_to @article.view_url | |
154 | + end | |
141 | 155 | return |
142 | 156 | end |
143 | 157 | end |
... | ... | @@ -150,7 +164,7 @@ class CmsController < MyProfileController |
150 | 164 | @article = profile.articles.find(params[:id]) |
151 | 165 | profile.home_page = @article |
152 | 166 | profile.save(false) |
153 | - flash[:notice] = _('"%s" configured as home page.') % @article.name | |
167 | + session[:notice] = _('"%s" configured as home page.') % @article.name | |
154 | 168 | redirect_to :action => 'view', :id => @article.id |
155 | 169 | end |
156 | 170 | |
... | ... | @@ -159,29 +173,36 @@ class CmsController < MyProfileController |
159 | 173 | @article = @parent = check_parent(params[:parent_id]) |
160 | 174 | @target = @parent ? ('/%s/%s' % [profile.identifier, @parent.full_name]) : '/%s' % profile.identifier |
161 | 175 | @folders = Folder.find(:all, :conditions => { :profile_id => profile }) |
162 | - record_coming_from_public_view if @article | |
176 | + @media_listing = params[:media_listing] | |
177 | + if @article && !@media_listing | |
178 | + record_coming | |
179 | + end | |
163 | 180 | if request.post? && params[:uploaded_files] |
164 | 181 | params[:uploaded_files].each do |file| |
165 | 182 | @uploaded_files << UploadedFile.create(:uploaded_data => file, :profile => profile, :parent => @parent) unless file == '' |
166 | 183 | end |
167 | 184 | @errors = @uploaded_files.select { |f| f.errors.any? } |
168 | - @back_to = params[:back_to] | |
169 | 185 | if @errors.any? |
170 | - if @back_to && @back_to == 'media_listing' | |
186 | + if @media_listing | |
171 | 187 | flash[:notice] = _('Could not upload all files') |
172 | - redirect_back | |
188 | + redirect_to :action => 'media_listing' | |
173 | 189 | else |
174 | 190 | render :action => 'upload_files', :parent_id => @parent_id |
175 | 191 | end |
176 | 192 | else |
177 | - if params[:back_to] | |
178 | - redirect_back | |
193 | + if @media_listing | |
194 | + flash[:notice] = _('All files were uploaded successfully') | |
195 | + redirect_to :action => 'media_listing' | |
179 | 196 | else |
180 | - redirect_to( if @parent | |
181 | - {:action => 'view', :id => @parent.id} | |
197 | + if @back_to | |
198 | + redirect_to @back_to | |
182 | 199 | else |
183 | - {:action => 'index'} | |
184 | - end) | |
200 | + redirect_to( if @parent | |
201 | + {:action => 'view', :id => @parent.id} | |
202 | + else | |
203 | + {:action => 'index'} | |
204 | + end) | |
205 | + end | |
185 | 206 | end |
186 | 207 | end |
187 | 208 | end |
... | ... | @@ -212,11 +233,11 @@ class CmsController < MyProfileController |
212 | 233 | |
213 | 234 | def publish |
214 | 235 | @article = profile.articles.find(params[:id]) |
215 | - record_coming_from_public_view | |
236 | + record_coming | |
216 | 237 | @groups = profile.memberships - [profile] |
217 | 238 | @marked_groups = [] |
218 | 239 | groups_ids = profile.memberships.map{|m|m.id.to_s} |
219 | - @marked_groups = params[:marked_groups].map do |item| | |
240 | + @marked_groups = params[:marked_groups].map do |key, item| | |
220 | 241 | if groups_ids.include?(item[:group_id]) |
221 | 242 | item.merge :group => Profile.find(item.delete(:group_id)) |
222 | 243 | end |
... | ... | @@ -232,8 +253,47 @@ class CmsController < MyProfileController |
232 | 253 | end |
233 | 254 | end |
234 | 255 | if @failed.blank? |
235 | - flash[:notice] = _("Your publish request was sent successfully") | |
236 | - redirect_back | |
256 | + session[:notice] = _("Your publish request was sent successfully") | |
257 | + if @back_to | |
258 | + redirect_to @back_to | |
259 | + else | |
260 | + redirect_to @article.view_url | |
261 | + end | |
262 | + end | |
263 | + end | |
264 | + end | |
265 | + | |
266 | + def publish_on_portal_community | |
267 | + @article = profile.articles.find(params[:id]) | |
268 | + if request.post? | |
269 | + if environment.portal_community | |
270 | + task = ApproveArticle.create!(:article => @article, :name => params[:name], :target => environment.portal_community, :requestor => user) | |
271 | + begin | |
272 | + task.finish unless environment.portal_community.moderated_articles? | |
273 | + flash[:notice] = _("Your publish request was sent successfully") | |
274 | + rescue | |
275 | + flash[:error] = _("Your publish request couldn't be sent.") | |
276 | + end | |
277 | + else | |
278 | + flash[:notice] = _("There is no portal community to publish your article.") | |
279 | + end | |
280 | + | |
281 | + if @back_to | |
282 | + redirect_to @back_to | |
283 | + else | |
284 | + redirect_to @article.view_url | |
285 | + end | |
286 | + end | |
287 | + end | |
288 | + | |
289 | + def suggest_an_article | |
290 | + @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url) | |
291 | + @task = SuggestArticle.new(params[:task]) | |
292 | + if request.post? | |
293 | + @task.target = profile | |
294 | + if @task.save | |
295 | + session[:notice] = _('Thanks for your suggestion. The community administrators were notified.') | |
296 | + redirect_to @back_to | |
237 | 297 | end |
238 | 298 | end |
239 | 299 | end |
... | ... | @@ -241,24 +301,24 @@ class CmsController < MyProfileController |
241 | 301 | def media_listing |
242 | 302 | if params[:image_folder_id] |
243 | 303 | folder = profile.articles.find(params[:image_folder_id]) if !params[:image_folder_id].blank? |
244 | - @images = (folder ? folder.children : UploadedFile.find(:all, :order => 'created_at desc', :conditions => ["profile_id = ? AND parent_id is NULL", profile ])).select { |c| c.image? } | |
304 | + @images = (folder ? folder.children : profile.top_level_articles).images | |
245 | 305 | elsif params[:document_folder_id] |
246 | 306 | folder = profile.articles.find(params[:document_folder_id]) if !params[:document_folder_id].blank? |
247 | - @documents = (folder ? folder.children : UploadedFile.find(:all, :order => 'created_at desc', :conditions => ["profile_id = ? AND parent_id is NULL", profile ])).select { |c| c.kind_of?(UploadedFile) && !c.image? } | |
307 | + @documents = (folder ? folder.children : profile.top_level_articles) | |
248 | 308 | else |
249 | - @documents = UploadedFile.find(:all, :order => 'created_at desc', :conditions => ["profile_id = ? AND parent_id is NULL", profile ]) | |
250 | - @images = @documents.select(&:image?) | |
309 | + @documents = profile.articles | |
310 | + @images = @documents.images | |
251 | 311 | @documents -= @images |
252 | 312 | end |
253 | 313 | |
254 | - @images = @images.paginate(:per_page => per_page, :page => params[:ipage]) if @images | |
255 | - @documents = @documents.paginate(:per_page => per_page, :page => params[:dpage]) if @documents | |
314 | + @images = @images.paginate(:per_page => per_page, :page => params[:ipage], :order => "updated_at desc") if @images | |
315 | + @documents = @documents.paginate(:per_page => per_page, :page => params[:dpage], :order => "updated_at desc", :conditions => {:is_image => false}) if @documents | |
256 | 316 | |
257 | - @folders = Folder.find(:all, :conditions => { :profile_id => profile }) | |
317 | + @folders = profile.folders | |
258 | 318 | @image_folders = @folders.select {|f| f.children.any? {|c| c.image?} } |
259 | 319 | @document_folders = @folders.select {|f| f.children.any? {|c| !c.image? && c.kind_of?(UploadedFile) } } |
260 | 320 | |
261 | - @back_to = 'media_listing' | |
321 | + @media_listing = true | |
262 | 322 | |
263 | 323 | respond_to do |format| |
264 | 324 | format.html { render :layout => false} |
... | ... | @@ -273,42 +333,11 @@ class CmsController < MyProfileController |
273 | 333 | |
274 | 334 | protected |
275 | 335 | |
276 | - def redirect_back | |
277 | - if params[:back_to] == 'control_panel' | |
278 | - redirect_to :controller => 'profile_editor', :profile => @profile.identifier | |
279 | - elsif params[:back_to] == 'public_view' | |
280 | - redirect_to @article.view_url.merge(Noosfero.url_options) | |
281 | - elsif params[:back_to] == 'media_listing' | |
282 | - redirect_to :action => 'media_listing' | |
283 | - elsif @article.parent | |
284 | - redirect_to :action => 'view', :id => @article.parent | |
285 | - elsif @article.folder? && !@article.blog? && @article.parent | |
286 | - redirect_to :action => 'index' | |
336 | + def record_coming | |
337 | + if request.post? | |
338 | + @back_to = params[:back_to] | |
287 | 339 | else |
288 | - redirect_back_or_default :action => 'index' | |
289 | - end | |
290 | - end | |
291 | - | |
292 | - def record_coming_from_public_view | |
293 | - referer = request.referer | |
294 | - referer.gsub!(/\?.*/, '') unless referer.nil? | |
295 | - if (maybe_ssl(url_for(@article.url)).include?(referer)) || (@article == profile.home_page && maybe_ssl(url_for(profile.url)).include?(referer)) | |
296 | - @back_to = 'public_view' | |
297 | - @back_url = @article.view_url | |
298 | - end | |
299 | - if !request.post? and @article.blog? | |
300 | - store_location(request.referer) | |
301 | - end | |
302 | - end | |
303 | - | |
304 | - def record_creating_from_public_view | |
305 | - referer = request.referer | |
306 | - if (referer =~ Regexp.new("^#{(url_for(profile.url).sub('https:', 'https?:'))}")) || params[:back_to] == 'public_view' | |
307 | - @back_to = 'public_view' | |
308 | - @back_url = referer | |
309 | - end | |
310 | - if !request.post? and @article.blog? | |
311 | - store_location(request.referer) | |
340 | + @back_to = params[:back_to] || request.referer | |
312 | 341 | end |
313 | 342 | end |
314 | 343 | |
... | ... | @@ -341,5 +370,11 @@ class CmsController < MyProfileController |
341 | 370 | def per_page |
342 | 371 | 10 |
343 | 372 | end |
373 | + | |
374 | + def translations | |
375 | + @locales = Noosfero.locales.invert.reject { |name, lang| !@article.possible_translations.include?(lang) } | |
376 | + @selected_locale = @article.language || FastGettext.locale | |
377 | + end | |
378 | + | |
344 | 379 | end |
345 | 380 | ... | ... |
app/controllers/my_profile/friends_controller.rb
... | ... | @@ -8,18 +8,6 @@ class FriendsController < MyProfileController |
8 | 8 | end |
9 | 9 | end |
10 | 10 | |
11 | - def add | |
12 | - @friend = Person.find(params[:id]) | |
13 | - if request.post? && params[:confirmation] | |
14 | - # FIXME this shouldn't be in Person model? | |
15 | - AddFriend.create!(:person => profile, :friend => @friend, :group_for_person => params[:group]) | |
16 | - | |
17 | - flash[:notice] = _('%s still needs to accept being your friend.') % @friend.name | |
18 | - # FIXME shouldn't redirect to the friend's page? | |
19 | - redirect_to :action => 'index' | |
20 | - end | |
21 | - end | |
22 | - | |
23 | 11 | def remove |
24 | 12 | @friend = profile.friends.find(params[:id]) |
25 | 13 | if request.post? && params[:confirmation] | ... | ... |
app/controllers/my_profile/mailconf_controller.rb
... | ... | @@ -20,20 +20,20 @@ class MailconfController < MyProfileController |
20 | 20 | @task = EmailActivation.new(:target => environment, :requestor => profile) |
21 | 21 | begin |
22 | 22 | @task.save! |
23 | - flash[:notice] = _('Please fill your personal information below in order to get your mailbox approved by one of the administrators') | |
23 | + session[:notice] = _('Please fill your personal information below in order to get your mailbox approved by one of the administrators') | |
24 | 24 | redirect_to :controller => 'profile_editor', :action => 'edit' |
25 | 25 | rescue Exception => ex |
26 | - flash[:notice] = _('e-Mail was not enabled successfully.') | |
26 | + session[:notice] = _('e-Mail was not enabled successfully.') | |
27 | 27 | render :action => 'index' |
28 | 28 | end |
29 | 29 | end |
30 | 30 | post_only :disable |
31 | 31 | def disable |
32 | 32 | if profile.user.disable_email! |
33 | - flash[:notice] = _('e-Mail disabled successfully.') | |
33 | + session[:notice] = _('e-Mail disabled successfully.') | |
34 | 34 | redirect_to :controller => 'profile_editor' |
35 | 35 | else |
36 | - flash[:notice] = _('e-Mail was not disabled successfully.') | |
36 | + session[:notice] = _('e-Mail was not disabled successfully.') | |
37 | 37 | redirect_to :action => 'index' |
38 | 38 | end |
39 | 39 | end | ... | ... |
app/controllers/my_profile/manage_products_controller.rb
1 | 1 | class ManageProductsController < ApplicationController |
2 | 2 | needs_profile |
3 | 3 | |
4 | - protect 'manage_products', :profile | |
4 | + protect 'manage_products', :profile, :except => [:show] | |
5 | 5 | before_filter :check_environment_feature |
6 | + before_filter :login_required, :except => [:show] | |
6 | 7 | |
7 | 8 | protected |
9 | + | |
8 | 10 | def check_environment_feature |
9 | 11 | if profile.environment.enabled?('disable_products_for_enterprises') |
10 | 12 | render_not_found |
... | ... | @@ -13,104 +15,147 @@ class ManageProductsController < ApplicationController |
13 | 15 | end |
14 | 16 | |
15 | 17 | public |
18 | + | |
16 | 19 | def index |
17 | - @products = @profile.products | |
18 | - @consumptions = @profile.consumptions | |
20 | + @products = @profile.products.paginate(:per_page => 10, :page => params[:page]) | |
19 | 21 | end |
20 | 22 | |
21 | 23 | def show |
22 | 24 | @product = @profile.products.find(params[:id]) |
25 | + @inputs = @product.inputs | |
26 | + @allowed_user = user && user.has_permission?('manage_products', profile) | |
27 | + end | |
28 | + | |
29 | + def categories_for_selection | |
30 | + @category = Category.find(params[:category_id]) if params[:category_id] | |
31 | + @object_name = params[:object_name] | |
32 | + if @category | |
33 | + @categories = @category.children | |
34 | + @level = @category.leaf? ? @category.level : @categories.first.level | |
35 | + else | |
36 | + @categories = ProductCategory.top_level_for(environment) | |
37 | + @level = 0 | |
38 | + end | |
39 | + render :partial => 'categories_for_selection', :locals => { :categories => @categories, :level => @level } | |
23 | 40 | end |
24 | 41 | |
25 | 42 | def new |
26 | - @object = Product.new | |
27 | - @categories = @current_category.nil? ? ProductCategory.top_level_for(environment) : @current_category.children | |
28 | - @product = @profile.products.build(params[:product]) | |
43 | + @product = @profile.products.build(:product_category_id => params[:selected_category_id]) | |
44 | + @category = @product.product_category | |
45 | + @categories = ProductCategory.top_level_for(environment) | |
46 | + @level = 0 | |
29 | 47 | if request.post? |
30 | 48 | if @product.save |
31 | - flash[:notice] = _('Product succesfully created') | |
32 | - redirect_to :action => 'show', :id => @product | |
49 | + session[:notice] = _('Product succesfully created') | |
50 | + render :partial => 'shared/redirect_via_javascript', | |
51 | + :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) } | |
33 | 52 | else |
34 | - flash[:notice] = _('Could not create the product') | |
53 | + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } | |
35 | 54 | end |
36 | 55 | end |
37 | 56 | end |
38 | 57 | |
39 | 58 | def edit |
40 | 59 | @product = @profile.products.find(params[:id]) |
41 | - @object = @profile.products.find(params[:id]) | |
42 | - @current_category = @product.product_category | |
43 | - @categories = @current_category.nil? ? [] : @current_category.children | |
60 | + field = params[:field] | |
61 | + if request.post? | |
62 | + begin | |
63 | + @product.update_attributes!(params[:product]) | |
64 | + render :partial => "display_#{field}", :locals => {:product => @product} | |
65 | + rescue Exception => e | |
66 | + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => true} | |
67 | + end | |
68 | + else | |
69 | + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => false} | |
70 | + end | |
71 | + end | |
72 | + | |
73 | + def edit_category | |
74 | + @product = @profile.products.find(params[:id]) | |
75 | + @category = @product.product_category | |
76 | + @categories = ProductCategory.top_level_for(environment) | |
77 | + @edit = true | |
78 | + @level = @category.level | |
79 | + if request.post? | |
80 | + if @product.update_attributes(:product_category_id => params[:selected_category_id]) | |
81 | + render :partial => 'shared/redirect_via_javascript', | |
82 | + :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) } | |
83 | + else | |
84 | + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } | |
85 | + end | |
86 | + end | |
87 | + end | |
88 | + | |
89 | + def add_input | |
90 | + @product = @profile.products.find(params[:id]) | |
91 | + @input = @product.inputs.build | |
92 | + @categories = ProductCategory.top_level_for(environment) | |
93 | + @level = 0 | |
44 | 94 | if request.post? |
45 | - if @product.update_attributes(params[:product]) | |
46 | - flash[:notice] = _('Product succesfully updated') | |
47 | - redirect_back_or_default :action => 'show', :id => @product | |
95 | + if @input.update_attributes(:product_category_id => params[:selected_category_id]) | |
96 | + @inputs = @product.inputs | |
97 | + render :partial => 'display_inputs' | |
48 | 98 | else |
49 | - flash[:notice] = _('Could not update the product') | |
99 | + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } | |
50 | 100 | end |
101 | + else | |
102 | + render :partial => 'add_input' | |
51 | 103 | end |
52 | 104 | end |
53 | 105 | |
54 | 106 | def destroy |
55 | 107 | @product = @profile.products.find(params[:id]) |
56 | 108 | if @product.destroy |
57 | - flash[:notice] = _('Product succesfully removed') | |
109 | + session[:notice] = _('Product succesfully removed') | |
58 | 110 | redirect_back_or_default :action => 'index' |
59 | 111 | else |
60 | - flash[:notice] = _('Could not remove the product') | |
112 | + session[:notice] = _('Could not remove the product') | |
61 | 113 | redirect_back_or_default :action => 'show', :id => @product |
62 | 114 | end |
63 | 115 | end |
64 | 116 | |
65 | - def update_categories | |
66 | - @object = params[:id] ? @profile.products.find(params[:id]) : Product.new | |
67 | - if params[:category_id] | |
68 | - @current_category = Category.find(params[:category_id]) | |
69 | - @categories = @current_category.children | |
70 | - else | |
71 | - @current_category = ProductCategory.top_level_for(environment).first | |
72 | - @categories = @current_category.nil? ? [] : @current_category.children | |
117 | + def edit_input | |
118 | + if request.xhr? | |
119 | + @input = @profile.inputs.find_by_id(params[:id]) | |
120 | + if @input | |
121 | + if request.post? | |
122 | + if @input.update_attributes(params[:input]) | |
123 | + render :partial => 'display_input', :locals => {:input => @input} | |
124 | + else | |
125 | + render :partial => 'edit_input' | |
126 | + end | |
127 | + else | |
128 | + render :partial => 'edit_input' | |
129 | + end | |
130 | + else | |
131 | + render :text => _('The input was not found') | |
132 | + end | |
73 | 133 | end |
74 | - render :partial => 'shared/select_categories', :locals => {:object_name => 'product', :multiple => false}, :layout => false | |
75 | 134 | end |
76 | 135 | |
77 | - def update_subcategories | |
78 | - @current_category = ProductCategory.find(params[:id]) if params[:id] | |
79 | - @categories = @current_category ? @current_category.children : ProductCategory.top_level_for(environment) | |
80 | - render :partial => 'subcategories' | |
136 | + def order_inputs | |
137 | + @product = @profile.products.find(params[:id]) | |
138 | + @product.order_inputs!(params[:input]) if params[:input] | |
139 | + render :nothing => true | |
81 | 140 | end |
82 | - | |
83 | - def new_consumption | |
84 | - @consumption = @profile.consumptions.build(params[:consumption]) | |
141 | + | |
142 | + def remove_input | |
143 | + @input = @profile.inputs.find(params[:id]) | |
144 | + @product = @input.product | |
85 | 145 | if request.post? |
86 | - if @consumption.save | |
87 | - flash[:notice] = _('Raw material succesfully created') | |
88 | - redirect_to :action => 'index' | |
146 | + if @input.destroy | |
147 | + @inputs = @product.inputs | |
148 | + render :partial => 'display_inputs' | |
89 | 149 | else |
90 | - flash[:notice] = _('Could not create the raw material') | |
150 | + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'input' } | |
91 | 151 | end |
92 | 152 | end |
93 | 153 | end |
94 | 154 | |
95 | - def destroy_consumption | |
96 | - @consumption = @profile.consumptions.find(params[:id]) | |
97 | - if @consumption.destroy | |
98 | - flash[:notice] = _('Raw material succesfully removed') | |
99 | - else | |
100 | - flash[:notice] = _('Could not remove the raw material') | |
101 | - end | |
102 | - redirect_back_or_default :action => 'index' | |
103 | - end | |
104 | - | |
105 | - def edit_consumption | |
106 | - @consumption = @profile.consumptions.find(params[:id]) | |
107 | - if request.post? | |
108 | - if @consumption.update_attributes(params[:consumption]) | |
109 | - flash[:notice] = _('Raw material succesfully updated') | |
110 | - redirect_back_or_default :action => 'index' | |
111 | - else | |
112 | - flash[:notice] = _('Could not update the raw material') | |
113 | - end | |
155 | + def certifiers_for_selection | |
156 | + @qualifier = Qualifier.exists?(params[:id]) ? Qualifier.find(params[:id]) : nil | |
157 | + render :update do |page| | |
158 | + page.replace_html params[:certifier_area], :partial => 'certifiers_for_selection' | |
114 | 159 | end |
115 | 160 | end |
116 | 161 | ... | ... |
app/controllers/my_profile/maps_controller.rb
... | ... | @@ -8,7 +8,7 @@ class MapsController < MyProfileController |
8 | 8 | begin |
9 | 9 | Profile.transaction do |
10 | 10 | if profile.update_attributes!(params[:profile_data]) |
11 | - flash[:notice] = _('Address was updated successfully!') | |
11 | + session[:notice] = _('Address was updated successfully!') | |
12 | 12 | redirect_to :action => 'edit_location' |
13 | 13 | end |
14 | 14 | end | ... | ... |
app/controllers/my_profile/memberships_controller.rb
... | ... | @@ -7,32 +7,12 @@ class MembershipsController < MyProfileController |
7 | 7 | end |
8 | 8 | |
9 | 9 | def new_community |
10 | - @wizard = params[:wizard].blank? ? false : params[:wizard] | |
11 | 10 | @community = Community.new(params[:community]) |
12 | 11 | @community.environment = environment |
13 | 12 | if request.post? && @community.valid? |
14 | 13 | @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community])) |
15 | - if @wizard | |
16 | - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true | |
17 | - return | |
18 | - else | |
19 | - redirect_to :action => 'index' | |
20 | - return | |
21 | - end | |
22 | - end | |
23 | - if @wizard | |
24 | - render :layout => 'wizard' | |
14 | + redirect_to :action => 'index' | |
15 | + return | |
25 | 16 | end |
26 | 17 | end |
27 | - | |
28 | - def destroy_community | |
29 | - @community = Community.find(params[:id]) | |
30 | - if request.post? | |
31 | - if @community.destroy | |
32 | - flash[:notice] = _('%s was removed.') % @community.short_name | |
33 | - redirect_to :action => 'index' | |
34 | - end | |
35 | - end | |
36 | - end | |
37 | - | |
38 | 18 | end | ... | ... |
app/controllers/my_profile/profile_design_controller.rb
... | ... | @@ -5,7 +5,7 @@ class ProfileDesignController < BoxOrganizerController |
5 | 5 | protect 'edit_profile_design', :profile |
6 | 6 | |
7 | 7 | def available_blocks |
8 | - blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock ] | |
8 | + blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock ] | |
9 | 9 | |
10 | 10 | # blocks exclusive for organizations |
11 | 11 | if profile.has_members? |
... | ... | @@ -24,6 +24,7 @@ class ProfileDesignController < BoxOrganizerController |
24 | 24 | if profile.enterprise? |
25 | 25 | blocks << DisabledEnterpriseMessageBlock |
26 | 26 | blocks << HighlightsBlock |
27 | + blocks << FeaturedProductsBlock | |
27 | 28 | end |
28 | 29 | |
29 | 30 | # product block exclusive for enterprises in environments that permits it | ... | ... |
app/controllers/my_profile/profile_editor_controller.rb
1 | 1 | class ProfileEditorController < MyProfileController |
2 | 2 | |
3 | - protect 'edit_profile', :profile | |
3 | + protect 'edit_profile', :profile, :except => [:destroy_profile] | |
4 | + protect 'destroy_profile', :profile, :only => [:destroy_profile] | |
4 | 5 | |
5 | 6 | def index |
6 | 7 | @pending_tasks = profile.all_pending_tasks.select{|i| user.has_permission?(i.permission, profile)} |
... | ... | @@ -25,7 +26,7 @@ class ProfileEditorController < MyProfileController |
25 | 26 | if profile.identifier.blank? |
26 | 27 | profile.identifier = params[:profile] |
27 | 28 | end |
28 | - flash[:notice] = _('Cannot update profile') | |
29 | + session[:notice] = _('Cannot update profile') | |
29 | 30 | end |
30 | 31 | end |
31 | 32 | end |
... | ... | @@ -34,7 +35,7 @@ class ProfileEditorController < MyProfileController |
34 | 35 | @to_enable = profile |
35 | 36 | if request.post? && params[:confirmation] |
36 | 37 | unless @to_enable.update_attribute('enabled', true) |
37 | - flash[:notice] = _('%s was not enabled.') % @to_enable.name | |
38 | + session[:notice] = _('%s was not enabled.') % @to_enable.name | |
38 | 39 | end |
39 | 40 | redirect_to :action => 'index' |
40 | 41 | end |
... | ... | @@ -44,7 +45,7 @@ class ProfileEditorController < MyProfileController |
44 | 45 | @to_disable = profile |
45 | 46 | if request.post? && params[:confirmation] |
46 | 47 | unless @to_disable.update_attribute('enabled', false) |
47 | - flash[:notice] = _('%s was not disabled.') % @to_disable.name | |
48 | + session[:notice] = _('%s was not disabled.') % @to_disable.name | |
48 | 49 | end |
49 | 50 | redirect_to :action => 'index' |
50 | 51 | end |
... | ... | @@ -72,4 +73,14 @@ class ProfileEditorController < MyProfileController |
72 | 73 | end |
73 | 74 | end |
74 | 75 | |
76 | + def destroy_profile | |
77 | + if request.post? | |
78 | + if @profile.destroy | |
79 | + session[:notice] = _('The profile was deleted.') | |
80 | + redirect_to :controller => 'home' | |
81 | + else | |
82 | + session[:notice] = _('Could not delete profile') | |
83 | + end | |
84 | + end | |
85 | + end | |
75 | 86 | end | ... | ... |
app/controllers/my_profile/profile_members_controller.rb
... | ... | @@ -10,23 +10,34 @@ class ProfileMembersController < MyProfileController |
10 | 10 | def update_roles |
11 | 11 | @roles = params[:roles] ? environment.roles.find(params[:roles].select{|r|!r.to_i.zero?}) : [] |
12 | 12 | @roles = @roles.select{|r| r.has_kind?('Profile') } |
13 | - @person = profile.members.find { |m| m.id == params[:person].to_i } | |
14 | - if @person && @person.define_roles(@roles, profile) | |
15 | - flash[:notice] = _('Roles successfuly updated') | |
13 | + begin | |
14 | + @person = profile.members.find(params[:person]) | |
15 | + rescue ActiveRecord::RecordNotFound | |
16 | + @person = nil | |
17 | + end | |
18 | + if !params[:confirmation] && @person && @person.is_last_admin_leaving?(profile, @roles) | |
19 | + redirect_to :action => :last_admin, :roles => params[:roles], :person => @person | |
16 | 20 | else |
17 | - flash[:notice] = _('Couldn\'t change the roles') | |
21 | + if @person && @person.define_roles(@roles, profile) | |
22 | + session[:notice] = _('Roles successfuly updated') | |
23 | + else | |
24 | + session[:notice] = _('Couldn\'t change the roles') | |
25 | + end | |
26 | + if params[:confirmation] | |
27 | + redirect_to profile.url | |
28 | + else | |
29 | + redirect_to :action => :index | |
30 | + end | |
18 | 31 | end |
19 | - redirect_to :action => :index | |
20 | 32 | end |
21 | 33 | |
22 | - def change_role | |
23 | - @roles = Profile::Roles.organization_member_roles(environment.id) | |
24 | - @member = profile.members.find { |m| m.id == params[:id].to_i } | |
25 | - if @member | |
26 | - @associations = @member.find_roles(@profile) | |
27 | - else | |
28 | - redirect_to :action => :index | |
29 | - end | |
34 | + def last_admin | |
35 | + @person = params[:person] | |
36 | + @roles = params[:roles] || [] | |
37 | + @members = profile.members.select {|member| !profile.admins.include?(member)} | |
38 | + @title = _('Current admins') | |
39 | + @collection = :profile_admins | |
40 | + @remove_action = {:action => 'remove_admin'} | |
30 | 41 | end |
31 | 42 | |
32 | 43 | def add_role |
... | ... | @@ -44,21 +55,35 @@ class ProfileMembersController < MyProfileController |
44 | 55 | def remove_role |
45 | 56 | @association = RoleAssignment.find(:all, :conditions => {:id => params[:id], :target_id => profile.id}) |
46 | 57 | if @association.destroy |
47 | - flash[:notice] = 'Member succefully unassociated' | |
58 | + session[:notice] = 'Member succefully unassociated' | |
48 | 59 | else |
49 | - flash[:notice] = 'Failed to unassociate member' | |
60 | + session[:notice] = 'Failed to unassociate member' | |
50 | 61 | end |
51 | 62 | render :layout => false |
52 | 63 | end |
53 | 64 | |
65 | + def change_role | |
66 | + @roles = Profile::Roles.organization_member_roles(environment.id) | |
67 | + begin | |
68 | + @member = profile.members.find(params[:id]) | |
69 | + rescue ActiveRecord::RecordNotFound | |
70 | + @member = nil | |
71 | + end | |
72 | + if @member | |
73 | + @associations = @member.find_roles(@profile) | |
74 | + else | |
75 | + redirect_to :action => :index | |
76 | + end | |
77 | + end | |
78 | + | |
54 | 79 | def unassociate |
55 | 80 | member = Person.find(params[:id]) |
56 | 81 | associations = member.find_roles(profile) |
57 | 82 | RoleAssignment.transaction do |
58 | 83 | if associations.map(&:destroy) |
59 | - flash[:notice] = 'Member succefully unassociated' | |
84 | + session[:notice] = _('Member succesfully unassociated') | |
60 | 85 | else |
61 | - flash[:notice] = 'Failed to unassociate member' | |
86 | + session[:notice] = _('Failed to unassociate member') | |
62 | 87 | end |
63 | 88 | end |
64 | 89 | render :layout => false |
... | ... | @@ -69,19 +94,61 @@ class ProfileMembersController < MyProfileController |
69 | 94 | |
70 | 95 | def add_member |
71 | 96 | if profile.enterprise? |
72 | - member = Person.find_by_identifier(params[:id]) | |
97 | + member = Person.find(params[:id]) | |
73 | 98 | member.define_roles(Profile::Roles.all_roles(environment), profile) |
74 | 99 | end |
75 | 100 | render :layout => false |
76 | 101 | end |
77 | 102 | |
103 | + def add_admin | |
104 | + @title = _('Current admins') | |
105 | + @collection = :profile_admins | |
106 | + | |
107 | + if profile.community? | |
108 | + member = profile.members.find_by_identifier(params[:id]) | |
109 | + profile.add_admin(member) | |
110 | + end | |
111 | + render :layout => false | |
112 | + end | |
113 | + | |
114 | + def remove_admin | |
115 | + @title = _('Current admins') | |
116 | + @collection = :profile_admins | |
117 | + | |
118 | + if profile.community? | |
119 | + member = profile.members.find_by_identifier(params[:id]) | |
120 | + profile.remove_admin(member) | |
121 | + end | |
122 | + render :layout => false | |
123 | + end | |
124 | + | |
78 | 125 | def find_users |
79 | 126 | if !params[:query] || params[:query].length <= 2 |
80 | 127 | @users_found = [] |
81 | - else | |
82 | - @users_found = Person.find_by_contents(params[:query] + '*') | |
128 | + elsif params[:scope] == 'all_users' | |
129 | + @users_found = Person.find_by_contents(params[:query] + '*').select {|user| !profile.members.include?(user)} | |
130 | + @button_alt = _('Add member') | |
131 | + @add_action = {:action => 'add_member'} | |
132 | + elsif params[:scope] == 'new_admins' | |
133 | + @users_found = Person.find_by_contents(params[:query] + '*').select {|user| profile.members.include?(user) && !profile.admins.include?(user)} | |
134 | + @button_alt = _('Add member') | |
135 | + @add_action = {:action => 'add_admin'} | |
83 | 136 | end |
84 | 137 | render :layout => false |
85 | 138 | end |
86 | 139 | |
140 | + def send_mail | |
141 | + @mailing = profile.mailings.build(params[:mailing]) | |
142 | + if request.post? | |
143 | + @mailing.locale = locale | |
144 | + @mailing.person = user | |
145 | + if @mailing.save | |
146 | + session[:notice] = _('The e-mails are being sent') | |
147 | + redirect_to :action => 'index' | |
148 | + else | |
149 | + session[:notice] = _('Could not create the e-mail') | |
150 | + end | |
151 | + end | |
152 | + end | |
153 | + | |
87 | 154 | end | ... | ... |
app/controllers/my_profile/tasks_controller.rb
... | ... | @@ -4,26 +4,38 @@ class TasksController < MyProfileController |
4 | 4 | |
5 | 5 | def index |
6 | 6 | @tasks = profile.all_pending_tasks.sort_by(&:created_at) |
7 | + @failed = params ? params[:failed] : {} | |
7 | 8 | end |
8 | 9 | |
9 | 10 | def processed |
10 | 11 | @tasks = profile.all_finished_tasks.sort_by(&:created_at) |
11 | 12 | end |
12 | 13 | |
13 | - VALID_DECISIONS = [ 'finish', 'cancel' ] | |
14 | + VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] | |
14 | 15 | |
15 | 16 | def close |
16 | - decision = params[:decision] | |
17 | - if request.post? && VALID_DECISIONS.include?(decision) && params[:id] | |
18 | - task = profile.find_in_all_tasks(params[:id]) | |
19 | - task.update_attributes!(params[:task]) | |
20 | - begin | |
21 | - task.send(decision) | |
22 | - rescue Exception => ex | |
23 | - flash[:notice] = ex.clean_message | |
17 | + failed = {} | |
18 | + | |
19 | + params[:tasks].each do |id, value| | |
20 | + decision = value[:decision] | |
21 | + if request.post? && VALID_DECISIONS.include?(decision) && id && decision != 'skip' | |
22 | + task = profile.find_in_all_tasks(id) | |
23 | + task.update_attributes!(value[:task]) | |
24 | + begin | |
25 | + task.send(decision) | |
26 | + rescue Exception => ex | |
27 | + message = "#{task.title} (#{task.requestor ? task.requestor.name : task.author_name})" | |
28 | + failed[ex.clean_message] ? failed[ex.clean_message] << message : failed[ex.clean_message] = [message] | |
29 | + end | |
24 | 30 | end |
25 | 31 | end |
26 | - redirect_to :action => 'index' | |
32 | + | |
33 | + if failed.blank? | |
34 | + session[:notice] = _("All decisions were applied successfully.") | |
35 | + else | |
36 | + session[:notice] = _("Some decisions couldn't be applied.") | |
37 | + end | |
38 | + redirect_to params.merge!(:action => 'index', :failed => failed) | |
27 | 39 | end |
28 | 40 | |
29 | 41 | def new | ... | ... |
app/controllers/my_profile/themes_controller.rb
... | ... | @@ -8,6 +8,11 @@ class ThemesController < MyProfileController |
8 | 8 | redirect_to :action => 'index' |
9 | 9 | end |
10 | 10 | |
11 | + def unset | |
12 | + profile.update_theme(nil) | |
13 | + redirect_to :action => 'index' | |
14 | + end | |
15 | + | |
11 | 16 | def index |
12 | 17 | @themes = profile.environment.themes + Theme.approved_themes(profile) |
13 | 18 | @current_theme = profile.theme |
... | ... | @@ -18,7 +23,7 @@ class ThemesController < MyProfileController |
18 | 23 | |
19 | 24 | def new |
20 | 25 | if !request.xhr? |
21 | - id = params[:name].to_slug | |
26 | + id = params[:name] ? params[:name].to_slug : 'my-theme' | |
22 | 27 | t = Theme.new(id, :name => params[:name], :owner => profile, :public => false) |
23 | 28 | t.save |
24 | 29 | redirect_to :action => 'index' | ... | ... |
app/controllers/public/account_controller.rb
1 | 1 | class AccountController < ApplicationController |
2 | 2 | |
3 | + no_design_blocks | |
4 | + | |
3 | 5 | inverse_captcha :field => 'e_mail' |
4 | 6 | |
5 | - require_ssl :except => [ :login_popup, :logout_popup, :wizard, :profile_details ] | |
7 | + require_ssl :except => [ :login_popup, :logout_popup, :profile_details ] | |
6 | 8 | |
7 | 9 | before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise] |
8 | 10 | before_filter :redirect_if_logged_in, :only => [:login, :signup] |
... | ... | @@ -28,10 +30,10 @@ class AccountController < ApplicationController |
28 | 30 | end |
29 | 31 | if redirect? |
30 | 32 | go_to_initial_page |
31 | - flash[:notice] = _("Logged in successfully") | |
33 | + session[:notice] = _("Logged in successfully") | |
32 | 34 | end |
33 | 35 | else |
34 | - flash[:notice] = _('Incorrect username or password') if redirect? | |
36 | + session[:notice] = _('Incorrect username or password') if redirect? | |
35 | 37 | redirect_to :back if redirect? |
36 | 38 | end |
37 | 39 | end |
... | ... | @@ -48,8 +50,6 @@ class AccountController < ApplicationController |
48 | 50 | # action to register an user to the application |
49 | 51 | def signup |
50 | 52 | @invitation_code = params[:invitation_code] |
51 | - @wizard = params[:wizard].blank? ? false : params[:wizard] | |
52 | - @step = 1 | |
53 | 53 | begin |
54 | 54 | @user = User.new(params[:user]) |
55 | 55 | @user.terms_of_use = environment.terms_of_use |
... | ... | @@ -68,38 +68,17 @@ class AccountController < ApplicationController |
68 | 68 | invitation.update_attributes!({:friend => @user.person}) |
69 | 69 | invitation.finish |
70 | 70 | end |
71 | - flash[:notice] = _("Thanks for signing up!") | |
72 | - if @wizard | |
73 | - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true | |
74 | - return | |
75 | - else | |
76 | - go_to_initial_page if redirect? | |
77 | - end | |
71 | + session[:notice] = _("Thanks for signing up!") | |
72 | + go_to_initial_page if redirect? | |
78 | 73 | end |
79 | - if @wizard | |
80 | - render :layout => 'wizard' | |
81 | - end | |
82 | 74 | rescue ActiveRecord::RecordInvalid |
83 | 75 | @person.valid? |
84 | 76 | @person.errors.delete(:identifier) |
85 | 77 | @person.errors.delete(:user_id) |
86 | - if @wizard | |
87 | - render :action => 'signup', :layout => 'wizard' | |
88 | - else | |
89 | - render :action => 'signup' | |
90 | - end | |
78 | + render :action => 'signup' | |
91 | 79 | end |
92 | 80 | end |
93 | 81 | |
94 | - def wizard | |
95 | - render :layout => false | |
96 | - end | |
97 | - | |
98 | - def profile_details | |
99 | - @profile = Profile.find_by_identifier(params[:profile]) | |
100 | - render :partial => 'profile_details', :layout => 'wizard' | |
101 | - end | |
102 | - | |
103 | 82 | # action to perform logout from the application |
104 | 83 | def logout |
105 | 84 | if logged_in? |
... | ... | @@ -107,7 +86,7 @@ class AccountController < ApplicationController |
107 | 86 | end |
108 | 87 | cookies.delete :auth_token |
109 | 88 | reset_session |
110 | - flash[:notice] = _("You have been logged out.") | |
89 | + session[:notice] = _("You have been logged out.") | |
111 | 90 | redirect_to :controller => 'home', :action => 'index' |
112 | 91 | end |
113 | 92 | |
... | ... | @@ -118,10 +97,10 @@ class AccountController < ApplicationController |
118 | 97 | @user.change_password!(params[:current_password], |
119 | 98 | params[:new_password], |
120 | 99 | params[:new_password_confirmation]) |
121 | - flash[:notice] = _('Your password has been changed successfully!') | |
100 | + session[:notice] = _('Your password has been changed successfully!') | |
122 | 101 | redirect_to :action => 'index' |
123 | 102 | rescue User::IncorrectPassword => e |
124 | - flash[:notice] = _('The supplied current password is incorrect.') | |
103 | + session[:notice] = _('The supplied current password is incorrect.') | |
125 | 104 | render :action => 'change_password' |
126 | 105 | end |
127 | 106 | else |
... | ... | @@ -233,6 +212,21 @@ class AccountController < ApplicationController |
233 | 212 | render :partial => 'identifier_status' |
234 | 213 | end |
235 | 214 | |
215 | + def user_data | |
216 | + user_data = | |
217 | + if logged_in? | |
218 | + current_user.data_hash | |
219 | + else | |
220 | + { } | |
221 | + end | |
222 | + if session[:notice] | |
223 | + user_data['notice'] = session[:notice] | |
224 | + session[:notice] = nil # consume the notice | |
225 | + end | |
226 | + | |
227 | + render :text => user_data.to_json, :layout => false, :content_type => "application/javascript" | |
228 | + end | |
229 | + | |
236 | 230 | protected |
237 | 231 | |
238 | 232 | def redirect? | ... | ... |
... | ... | @@ -0,0 +1,58 @@ |
1 | +class BrowseController < PublicController | |
2 | + | |
3 | + no_design_blocks | |
4 | + | |
5 | + FILTERS = %w( | |
6 | + more_recent | |
7 | + more_active | |
8 | + more_popular | |
9 | + ) | |
10 | + | |
11 | + def people | |
12 | + @filter = filter | |
13 | + @title = self.filter_description(params[:action] + '_' + @filter ) | |
14 | + | |
15 | + @results = @environment.people.visible.send(@filter) | |
16 | + | |
17 | + if params[:query].blank? | |
18 | + @results = @results.paginate(:per_page => 27, :page => params[:page]) | |
19 | + else | |
20 | + @results = @results.find_by_contents(params[:query]).paginate(:per_page => 27, :page => params[:page]) | |
21 | + end | |
22 | + end | |
23 | + | |
24 | + def communities | |
25 | + @filter = filter | |
26 | + @title = self.filter_description(params[:action] + '_' + @filter ) | |
27 | + | |
28 | + @results = @environment.communities.visible.send(@filter) | |
29 | + | |
30 | + if params[:query].blank? | |
31 | + @results = @results.paginate(:per_page => 27, :page => params[:page]) | |
32 | + else | |
33 | + @results = @results.find_by_contents(params[:query]).paginate(:per_page => 27, :page => params[:page]) | |
34 | + end | |
35 | + end | |
36 | + | |
37 | + protected | |
38 | + | |
39 | + def filter | |
40 | + if FILTERS.include?(params[:filter]) | |
41 | + params[:filter] | |
42 | + else | |
43 | + 'more_recent' | |
44 | + end | |
45 | + end | |
46 | + | |
47 | + def filter_description(str) | |
48 | + { | |
49 | + 'people_more_recent' => _('More recent people'), | |
50 | + 'people_more_active' => _('More active people'), | |
51 | + 'people_more_popular' => _('More popular people'), | |
52 | + 'communities_more_recent' => _('More recent communities'), | |
53 | + 'communities_more_active' => _('More active communities'), | |
54 | + 'communities_more_popular' => _('More popular communities'), | |
55 | + }[str] || str | |
56 | + end | |
57 | + | |
58 | +end | ... | ... |
app/controllers/public/catalog_controller.rb
1 | 1 | class CatalogController < PublicController |
2 | 2 | needs_profile |
3 | + | |
3 | 4 | before_filter :check_enterprise_and_environment |
4 | 5 | |
5 | 6 | def index |
6 | - @products = @profile.products | |
7 | + @products = @profile.products.paginate(:per_page => 10, :page => params[:page]) | |
7 | 8 | end |
8 | 9 | |
9 | - def show | |
10 | - @product = @profile.products.find(params[:id]) | |
11 | - end | |
12 | - | |
13 | 10 | protected |
14 | 11 | def check_enterprise_and_environment |
15 | 12 | unless @profile.kind_of?(Enterprise) && !@profile.environment.enabled?('disable_products_for_enterprises') | ... | ... |
... | ... | @@ -0,0 +1,52 @@ |
1 | +class ChatController < PublicController | |
2 | + | |
3 | + before_filter :login_required | |
4 | + before_filter :check_environment_feature | |
5 | + | |
6 | + def start_session | |
7 | + login = user.jid | |
8 | + password = current_user.crypted_password | |
9 | + begin | |
10 | + jid, sid, rid = RubyBOSH.initialize_session(login, password, "http://#{environment.default_hostname}/http-bind", | |
11 | + :wait => 30, :hold => 1, :window => 5) | |
12 | + session_data = { :jid => jid, :sid => sid, :rid => rid } | |
13 | + render :text => session_data.to_json, :layout => false, :content_type => 'application/javascript' | |
14 | + rescue | |
15 | + render :action => 'start_session_error', :layout => false, :status => 500 | |
16 | + end | |
17 | + end | |
18 | + | |
19 | + def avatar | |
20 | + profile = environment.profiles.find_by_identifier(params[:id]) | |
21 | + filename, mimetype = profile_icon(profile, :minor, true) | |
22 | + data = File.read(File.join(RAILS_ROOT, 'public', filename)) | |
23 | + render :text => data, :layout => false, :content_type => mimetype | |
24 | + expires_in 24.hours | |
25 | + end | |
26 | + | |
27 | + def index | |
28 | + presence = current_user.last_chat_status | |
29 | + if presence.blank? or presence == 'chat' | |
30 | + render :action => 'auto_connect_online' | |
31 | + else | |
32 | + render :action => 'auto_connect_busy' | |
33 | + end | |
34 | + end | |
35 | + | |
36 | + def update_presence_status | |
37 | + if request.xhr? | |
38 | + current_user.update_attributes({:chat_status_at => DateTime.now}.merge(params[:status] || {})) | |
39 | + end | |
40 | + render :nothing => true | |
41 | + end | |
42 | + | |
43 | + protected | |
44 | + | |
45 | + def check_environment_feature | |
46 | + unless environment.enabled?('xmpp_chat') | |
47 | + render_not_found | |
48 | + return | |
49 | + end | |
50 | + end | |
51 | + | |
52 | +end | ... | ... |
app/controllers/public/contact_controller.rb
... | ... | @@ -12,10 +12,10 @@ class ContactController < PublicController |
12 | 12 | @contact.city = (!params[:city].blank? && City.exists?(params[:city])) ? City.find(params[:city]).name : nil |
13 | 13 | @contact.state = (!params[:state].blank? && State.exists?(params[:state])) ? State.find(params[:state]).name : nil |
14 | 14 | if @contact.deliver |
15 | - flash[:notice] = _('Contact successfully sent') | |
15 | + session[:notice] = _('Contact successfully sent') | |
16 | 16 | redirect_to :action => 'new' |
17 | 17 | else |
18 | - flash[:notice] = _('Contact not sent') | |
18 | + session[:notice] = _('Contact not sent') | |
19 | 19 | end |
20 | 20 | else |
21 | 21 | @contact = user.build_contact(profile) | ... | ... |
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -51,6 +51,8 @@ class ContentViewerController < ApplicationController |
51 | 51 | return |
52 | 52 | end |
53 | 53 | |
54 | + redirect_to_translation | |
55 | + | |
54 | 56 | # At this point the page will be showed |
55 | 57 | @page.hit |
56 | 58 | |
... | ... | @@ -67,9 +69,6 @@ class ContentViewerController < ApplicationController |
67 | 69 | return |
68 | 70 | end |
69 | 71 | |
70 | - # store location if the page is not a download | |
71 | - store_location | |
72 | - | |
73 | 72 | @form_div = params[:form] |
74 | 73 | |
75 | 74 | if request.post? && params[:comment] && params[self.icaptcha_field].blank? && params[:confirm] == 'true' && @page.accept_comments? |
... | ... | @@ -80,22 +79,28 @@ class ContentViewerController < ApplicationController |
80 | 79 | remove_comment |
81 | 80 | end |
82 | 81 | |
83 | - if @page.blog? | |
82 | + if @page.has_posts? | |
84 | 83 | posts = if params[:year] and params[:month] |
85 | 84 | filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01") |
86 | - @page.posts.by_range(filter_date..Article.last_day_of_month(filter_date)) | |
85 | + @page.posts.by_range(filter_date..filter_date.at_end_of_month) | |
87 | 86 | else |
88 | 87 | @page.posts |
89 | 88 | end |
90 | - @posts = available_articles(posts, user).paginate :page => params[:npage], :per_page => @page.posts_per_page | |
89 | + | |
90 | + posts = posts.native_translations if @page.blog? && @page.display_posts_in_current_language? | |
91 | + | |
92 | + @posts = posts.paginate({ :page => params[:npage], :per_page => @page.posts_per_page }.merge(Article.display_filter(user, profile))) | |
93 | + | |
94 | + @posts.map!{ |p| p.get_translation_to(FastGettext.locale) } if @page.blog? && @page.display_posts_in_current_language? | |
91 | 95 | end |
92 | 96 | |
93 | - if @page.folder? && @page.view_as == 'image_gallery' | |
97 | + if @page.folder? && @page.gallery? | |
94 | 98 | @images = @page.images |
95 | 99 | @images = @images.paginate(:per_page => per_page, :page => params[:npage]) unless params[:slideshow] |
96 | 100 | end |
97 | 101 | |
98 | - @comments = @page.comments(true) | |
102 | + @comments = @page.comments(true).as_thread | |
103 | + @comments_count = @page.comments.count | |
99 | 104 | if params[:slideshow] |
100 | 105 | render :action => 'slideshow', :layout => 'slideshow' |
101 | 106 | end |
... | ... | @@ -110,8 +115,9 @@ class ContentViewerController < ApplicationController |
110 | 115 | if @comment.save |
111 | 116 | @page.touch |
112 | 117 | @comment = nil # clear the comment form |
118 | + redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] | |
113 | 119 | else |
114 | - @form_div = 'opened' | |
120 | + @form_div = 'opened' if params[:comment][:reply_of_id].blank? | |
115 | 121 | end |
116 | 122 | end |
117 | 123 | |
... | ... | @@ -119,7 +125,7 @@ class ContentViewerController < ApplicationController |
119 | 125 | @comment = @page.comments.find(params[:remove_comment]) |
120 | 126 | if (user == @comment.author || user == @page.profile || user.has_permission?(:moderate_comments, @page.profile)) |
121 | 127 | @comment.destroy |
122 | - flash[:notice] = _('Comment succesfully deleted') | |
128 | + session[:notice] = _('Comment succesfully deleted') | |
123 | 129 | end |
124 | 130 | redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] |
125 | 131 | end |
... | ... | @@ -127,4 +133,24 @@ class ContentViewerController < ApplicationController |
127 | 133 | def per_page |
128 | 134 | 12 |
129 | 135 | end |
136 | + | |
137 | + def redirect_to_translation | |
138 | + locale = FastGettext.locale | |
139 | + if !@page.language.nil? && @page.language != locale | |
140 | + translations = [@page.native_translation] + @page.native_translation.translations | |
141 | + urls = translations.map{ |t| URI.parse(url_for(t.url)).path } | |
142 | + urls << URI.parse(url_for(profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }))).path | |
143 | + urls << URI.parse(url_for(profile.admin_url.merge(:controller => 'cms', :action => 'new'))).path | |
144 | + referer = URI.parse(url_for(request.referer)).path unless request.referer.blank? | |
145 | + unless urls.include?(referer) | |
146 | + translations.each do |translation| | |
147 | + if translation.language == locale | |
148 | + @page = translation | |
149 | + redirect_to :profile => @page.profile.identifier, :page => @page.explode_path | |
150 | + end | |
151 | + end | |
152 | + end | |
153 | + end | |
154 | + end | |
155 | + | |
130 | 156 | end | ... | ... |
app/controllers/public/enterprise_registration_controller.rb
... | ... | @@ -16,9 +16,9 @@ class EnterpriseRegistrationController < ApplicationController |
16 | 16 | @create_enterprise.target = Profile.find(params[:create_enterprise][:target_id]) |
17 | 17 | end |
18 | 18 | elsif @validation == :admin || @validation == :none |
19 | - @create_enterprise.target = @create_enterprise.environment | |
19 | + @create_enterprise.target = environment | |
20 | 20 | end |
21 | - @create_enterprise.requestor = current_user.person | |
21 | + @create_enterprise.requestor = user | |
22 | 22 | the_action = |
23 | 23 | if request.post? |
24 | 24 | if @create_enterprise.valid_before_selecting_target? | ... | ... |
app/controllers/public/events_controller.rb
... | ... | @@ -13,7 +13,7 @@ class EventsController < PublicController |
13 | 13 | @events_of_the_day = profile.events.by_day(@selected_day) |
14 | 14 | end |
15 | 15 | |
16 | - events = profile.events.by_range(Event.first_day_of_month(date - 1.month)..Event.last_day_of_month(date + 1.month)) | |
16 | + events = profile.events.by_range((date - 1.month).at_beginning_of_month..(date + 1.month).at_end_of_month) | |
17 | 17 | |
18 | 18 | @calendar = populate_calendar(date, events) |
19 | 19 | @previous_calendar = populate_calendar(date - 1.month, events) | ... | ... |
app/controllers/public/home_controller.rb
... | ... | @@ -7,8 +7,8 @@ class HomeController < PublicController |
7 | 7 | @news_cache_key = environment.portal_news_cache_key |
8 | 8 | if !read_fragment(@news_cache_key) |
9 | 9 | portal_community = environment.portal_community |
10 | - @portal_news = portal_community.news(5) | |
11 | 10 | @highlighted_news = portal_community.news(2, true) |
11 | + @portal_news = portal_community.news(7, true) - @highlighted_news | |
12 | 12 | @area_news = environment.portal_folders |
13 | 13 | end |
14 | 14 | end | ... | ... |
app/controllers/public/invite_controller.rb
... | ... | @@ -2,40 +2,57 @@ class InviteController < PublicController |
2 | 2 | |
3 | 3 | needs_profile |
4 | 4 | before_filter :login_required |
5 | - before_filter :check_permissions_to_invite, :only => 'friends' | |
5 | + before_filter :check_permissions_to_invite | |
6 | 6 | |
7 | - def friends | |
8 | - step = params[:step] | |
7 | + def select_address_book | |
8 | + @import_from = params[:import_from] || "manual" | |
9 | 9 | if request.post? |
10 | - if step == '1' | |
11 | - begin | |
12 | - @contacts = Invitation.get_contacts(params[:import_from], params[:login], params[:password]) | |
13 | - rescue | |
14 | - @login = params[:login] | |
15 | - flash.now[:notice] = _('There was an error while looking for your contact list. Did you enter correct login and password?') | |
16 | - end | |
17 | - elsif step == '2' | |
18 | - manual_import_addresses = params[:manual_import_addresses] | |
19 | - webmail_import_addresses = params[:webmail_import_addresses] | |
20 | - contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses) | |
21 | - if !params[:mail_template].match(/<url>/) | |
22 | - flash.now[:notice] = _('<url> is needed in invitation mail.') | |
23 | - elsif !contacts_to_invite.empty? | |
24 | - Invitation.invite(current_user.person, contacts_to_invite, params[:mail_template], profile) | |
25 | - flash[:notice] = _('Your invitations have been sent.') | |
26 | - redirect_back_or_default :controller => 'profile' | |
10 | + contact_list = ContactList.create | |
11 | + Delayed::Job.enqueue GetEmailContactsJob.new(@import_from, params[:login], params[:password], contact_list.id) if @import_from != 'manual' | |
12 | + redirect_to :action => 'select_friends', :contact_list => contact_list.id, :import_from => @import_from | |
13 | + end | |
14 | + end | |
15 | + | |
16 | + def select_friends | |
17 | + @contact_list = ContactList.find(params[:contact_list]) | |
18 | + @mail_template = params[:mail_template] || environment.invitation_mail_template(profile) | |
19 | + @import_from = params[:import_from] || "manual" | |
20 | + if request.post? | |
21 | + manual_import_addresses = params[:manual_import_addresses] | |
22 | + webmail_import_addresses = params[:webmail_import_addresses] | |
23 | + contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses) | |
24 | + if !contacts_to_invite.empty? | |
25 | + Delayed::Job.enqueue InvitationJob.new(current_user.person.id, contacts_to_invite, params[:mail_template], profile.id, @contact_list.id, locale) | |
26 | + session[:notice] = _('Your invitations are being sent.') | |
27 | + if profile.person? | |
28 | + redirect_to :controller => 'profile', :action => 'friends' | |
27 | 29 | else |
28 | - flash.now[:notice] = _('Please enter a valid email address.') | |
30 | + redirect_to :controller => 'profile', :action => 'members' | |
29 | 31 | end |
30 | - @contacts = params[:webmail_friends] ? params[:webmail_friends].map {|e| YAML.load(e)} : [] | |
31 | - @manual_import_addresses = manual_import_addresses || "" | |
32 | - @webmail_import_addresses = webmail_import_addresses || [] | |
32 | + return | |
33 | + else | |
34 | + session[:notice] = _('Please enter a valid email address.') | |
33 | 35 | end |
34 | - else | |
35 | - store_location(request.referer) | |
36 | + @manual_import_addresses = manual_import_addresses || "" | |
37 | + @webmail_import_addresses = webmail_import_addresses || [] | |
36 | 38 | end |
37 | - @import_from = params[:import_from] || "manual" | |
38 | - @mail_template = params[:mail_template] || environment.invitation_mail_template(profile) | |
39 | + end | |
40 | + | |
41 | + def invitation_data | |
42 | + contact_list = ContactList.find(params[:contact_list]) | |
43 | + render :text => contact_list.data.to_json, :layout => false, :content_type => "application/javascript" | |
44 | + end | |
45 | + | |
46 | + def add_contact_list | |
47 | + contact_list = ContactList.find(params[:contact_list]) | |
48 | + contacts = contact_list.list | |
49 | + render :partial => 'invite/contact_list', :locals => {:contacts => contacts} | |
50 | + end | |
51 | + | |
52 | + def cancel_fetching_emails | |
53 | + contact_list = ContactList.find(params[:contact_list]) | |
54 | + contact_list.destroy | |
55 | + redirect_to :action => 'select_address_book' | |
39 | 56 | end |
40 | 57 | |
41 | 58 | protected | ... | ... |
app/controllers/public/profile_controller.rb
1 | 1 | class ProfileController < PublicController |
2 | 2 | |
3 | 3 | needs_profile |
4 | - before_filter :check_access_to_profile, :except => [:join, :refuse_join, :refuse_for_now, :index] | |
5 | - before_filter :store_before_join, :only => [:join] | |
6 | - before_filter :login_required, :only => [:join, :refuse_join, :leave, :unblock] | |
4 | + before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add] | |
5 | + before_filter :store_before_join, :only => [:join, :join_not_logged] | |
6 | + before_filter :login_required, :only => [:add, :join, :join_not_logged, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_scraps, :view_more_activities, :view_more_network_activities] | |
7 | 7 | |
8 | 8 | helper TagsHelper |
9 | 9 | |
10 | 10 | def index |
11 | + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page]) | |
12 | + @wall_items = [] | |
13 | + @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) : [] | |
14 | + if logged_in? && current_person.follows?(@profile) | |
15 | + @network_activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) if @network_activities.empty? | |
16 | + @wall_items = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) | |
17 | + end | |
11 | 18 | @tags = profile.article_tags |
12 | 19 | unless profile.display_info_to?(user) |
13 | 20 | profile.visible? ? private_profile : invisible_profile |
... | ... | @@ -73,20 +80,27 @@ class ProfileController < PublicController |
73 | 80 | end |
74 | 81 | |
75 | 82 | def join |
76 | - @wizard = params[:wizard] | |
77 | - if request.post? && params[:confirmation] | |
78 | - profile.add_member(current_user.person) | |
79 | - flash[:notice] = _('%s administrator still needs to accept you as member.') % profile.name if profile.closed? | |
80 | - if @wizard | |
81 | - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true | |
83 | + if !user.memberships.include?(profile) | |
84 | + profile.add_member(user) | |
85 | + if !profile.members.include?(user) | |
86 | + render :text => {:message => _('%s administrator still needs to accept you as member.') % profile.name}.to_json | |
82 | 87 | else |
83 | - redirect_to_before_join | |
88 | + render :text => {:message => _('You just became a member of %s.') % profile.name}.to_json | |
84 | 89 | end |
85 | 90 | else |
86 | - store_location(request.referer) | |
87 | - if current_user.person.memberships.include?(profile) | |
88 | - flash[:notice] = _('You are already a member of "%s"') % profile.name | |
89 | - redirect_back_or_default profile.url | |
91 | + render :text => {:message => _('You are already a member of %s.') % profile.name}.to_json | |
92 | + end | |
93 | + end | |
94 | + | |
95 | + def join_not_logged | |
96 | + if request.post? | |
97 | + profile.add_member(user) | |
98 | + session[:notice] = _('%s administrator still needs to accept you as member.') % profile.name if profile.closed? | |
99 | + redirect_to_before_join | |
100 | + else | |
101 | + if user.memberships.include?(profile) | |
102 | + session[:notice] = _('You are already a member of %s.') % profile.name | |
103 | + redirect_to profile.url | |
90 | 104 | return |
91 | 105 | end |
92 | 106 | if request.xhr? |
... | ... | @@ -96,47 +110,109 @@ class ProfileController < PublicController |
96 | 110 | end |
97 | 111 | |
98 | 112 | def leave |
99 | - @wizard = params[:wizard] | |
100 | - if request.post? && params[:confirmation] | |
101 | - profile.remove_member(current_user.person) | |
102 | - if @wizard | |
103 | - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true | |
113 | + if current_person.memberships.include?(profile) | |
114 | + if current_person.is_last_admin?(profile) | |
115 | + render :text => {:redirect_to => url_for({:controller => 'profile_members', :action => 'last_admin', :person => current_person.id})}.to_json | |
104 | 116 | else |
105 | - redirect_back_or_default profile.url | |
117 | + render :text => current_person.leave(profile, params[:reload]) | |
106 | 118 | end |
107 | 119 | else |
108 | - store_location(request.referer) | |
109 | - if request.xhr? | |
110 | - render :layout => false | |
111 | - end | |
120 | + render :text => {:message => _('You are not a member of %s.') % profile.name}.to_json | |
112 | 121 | end |
113 | 122 | end |
114 | 123 | |
115 | - def refuse_join | |
116 | - p = current_user.person | |
117 | - p.refused_communities << profile | |
118 | - p.save | |
119 | - redirect_to profile.url | |
124 | + def check_membership | |
125 | + unless logged_in? | |
126 | + render :text => '' | |
127 | + return | |
128 | + end | |
129 | + if user.memberships.include?(profile) | |
130 | + render :text => 'true' | |
131 | + else | |
132 | + render :text => 'false' | |
133 | + end | |
120 | 134 | end |
121 | 135 | |
122 | - def refuse_for_now | |
123 | - session[:no_asking] ||= [] | |
124 | - session[:no_asking].shift if session[:no_asking].size >= 10 | |
125 | - session[:no_asking] << profile.id | |
126 | - render :text => '', :layout => false | |
136 | + def add | |
137 | + # FIXME this shouldn't be in Person model? | |
138 | + if !user.memberships.include?(profile) | |
139 | + AddFriend.create!(:person => user, :friend => profile) | |
140 | + render :text => _('%s still needs to accept being your friend.') % profile.name | |
141 | + else | |
142 | + render :text => _('You are already a friend of %s.') % profile.name | |
143 | + end | |
144 | + end | |
145 | + | |
146 | + def check_friendship | |
147 | + unless logged_in? | |
148 | + render :text => '' | |
149 | + return | |
150 | + end | |
151 | + if user == profile || user.already_request_friendship?(profile) || user.is_a_friend?(profile) | |
152 | + render :text => 'true' | |
153 | + else | |
154 | + render :text => 'false' | |
155 | + end | |
127 | 156 | end |
128 | 157 | |
129 | 158 | def unblock |
130 | 159 | if current_user.person.is_admin?(profile.environment) |
131 | 160 | profile.unblock |
132 | - flash[:notice] = _("You have unblocked %s successfully. ") % profile.name | |
161 | + session[:notice] = _("You have unblocked %s successfully. ") % profile.name | |
133 | 162 | redirect_to :controller => 'profile', :action => 'index' |
134 | 163 | else |
135 | - message = _('You are not allowed to unblock enterprises in this environment.') | |
164 | + message = __('You are not allowed to unblock enterprises in this environment.') | |
136 | 165 | render_access_denied(message) |
137 | 166 | end |
138 | 167 | end |
139 | 168 | |
169 | + def leave_scrap | |
170 | + sender = params[:sender_id].nil? ? current_user.person : Person.find(params[:sender_id]) | |
171 | + receiver = params[:receiver_id].nil? ? @profile : Person.find(params[:receiver_id]) | |
172 | + @scrap = Scrap.new(params[:scrap]) | |
173 | + @scrap.sender= sender | |
174 | + @scrap.receiver= receiver | |
175 | + @tab_action = params[:tab_action] | |
176 | + @message = @scrap.save ? _("Message successfully sent.") : _("You can't leave an empty message.") | |
177 | + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) if params[:not_load_scraps].nil? | |
178 | + render :partial => 'leave_scrap' | |
179 | + end | |
180 | + | |
181 | + def view_more_scraps | |
182 | + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) | |
183 | + render :partial => 'profile_scraps', :locals => {:scraps => @scraps} | |
184 | + end | |
185 | + | |
186 | + def view_more_activities | |
187 | + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page]) | |
188 | + render :partial => 'profile_activities', :locals => {:activities => @activities} | |
189 | + end | |
190 | + | |
191 | + def view_more_network_activities | |
192 | + @activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) | |
193 | + render :partial => 'profile_network_activities', :locals => {:network_activities => @activities} | |
194 | + end | |
195 | + | |
196 | + def remove_scrap | |
197 | + begin | |
198 | + scrap = current_user.person.scraps(params[:scrap_id]) | |
199 | + scrap.destroy | |
200 | + render :text => _('Scrap successfully removed.') | |
201 | + rescue | |
202 | + render :text => _('You could not remove this scrap') | |
203 | + end | |
204 | + end | |
205 | + | |
206 | + def remove_activity | |
207 | + begin | |
208 | + activity = current_person.tracked_actions.find(params[:activity_id]) | |
209 | + activity.destroy | |
210 | + render :text => _('Activity successfully removed.') | |
211 | + rescue | |
212 | + render :text => _('You could not remove this activity') | |
213 | + end | |
214 | + end | |
215 | + | |
140 | 216 | protected |
141 | 217 | |
142 | 218 | def check_access_to_profile |
... | ... | @@ -146,7 +222,9 @@ class ProfileController < PublicController |
146 | 222 | end |
147 | 223 | |
148 | 224 | def store_before_join |
149 | - session[:before_join] = request.referer unless logged_in? | |
225 | + if session[:before_join].nil? | |
226 | + session[:before_join] = request.referer | |
227 | + end | |
150 | 228 | end |
151 | 229 | |
152 | 230 | def redirect_to_before_join |
... | ... | @@ -155,14 +233,14 @@ class ProfileController < PublicController |
155 | 233 | session[:before_join] = nil |
156 | 234 | redirect_to back |
157 | 235 | else |
158 | - redirect_back_or_default profile.url | |
236 | + redirect_to profile.url | |
159 | 237 | end |
160 | 238 | end |
161 | 239 | |
162 | 240 | def private_profile |
163 | 241 | if profile.person? |
164 | 242 | @action = :add_friend |
165 | - @message = _("The content here is available to %s's friends only." % profile.short_name) | |
243 | + @message = _("The content here is available to %s's friends only.") % profile.short_name | |
166 | 244 | else |
167 | 245 | @action = :join |
168 | 246 | @message = _('The contents in this community is available to members only.') | ... | ... |
... | ... | @@ -0,0 +1,28 @@ |
1 | +class ProfileSearchController < PublicController | |
2 | + | |
3 | + include SearchHelper | |
4 | + | |
5 | + needs_profile | |
6 | + before_filter :check_access_to_profile | |
7 | + | |
8 | + def index | |
9 | + @q = params[:q] | |
10 | + unless @q.blank? | |
11 | + @filtered_query = remove_stop_words(@q) | |
12 | + if params[:where] == 'environment' | |
13 | + redirect_to :controller => 'search', :query => @q | |
14 | + else | |
15 | + @results = profile.articles.published.find_by_contents(@filtered_query).paginate(:per_page => 10, :page => params[:page]) | |
16 | + end | |
17 | + end | |
18 | + end | |
19 | + | |
20 | + protected | |
21 | + | |
22 | + def check_access_to_profile | |
23 | + unless profile.display_info_to?(user) | |
24 | + redirect_to :controller => 'profile', :action => 'index' | |
25 | + end | |
26 | + end | |
27 | + | |
28 | +end | ... | ... |
app/controllers/public/search_controller.rb
... | ... | @@ -102,7 +102,7 @@ class SearchController < PublicController |
102 | 102 | |
103 | 103 | if month || year |
104 | 104 | date = Date.new(year.to_i, month.to_i, 1) |
105 | - result[:date_range] = (date - 1.month)..Event.last_day_of_month(date + 1.month) | |
105 | + result[:date_range] = (date - 1.month)..(date + 1.month).at_end_of_month | |
106 | 106 | end |
107 | 107 | |
108 | 108 | result |
... | ... | @@ -148,8 +148,6 @@ class SearchController < PublicController |
148 | 148 | end |
149 | 149 | |
150 | 150 | def index |
151 | - @wizard = params[:wizard].blank? ? false : params[:wizard] | |
152 | - @step = 2 | |
153 | 151 | @query = params[:query] || '' |
154 | 152 | @filtered_query = remove_stop_words(@query) |
155 | 153 | @product_category = ProductCategory.find(params[:product_category]) if params[:product_category] |
... | ... | @@ -174,20 +172,12 @@ class SearchController < PublicController |
174 | 172 | if respond_to?(specific_action) |
175 | 173 | @asset_name = getterm(@names[@results.keys.first]) |
176 | 174 | send(specific_action) |
177 | - if @wizard | |
178 | - render :action => specific_action, :layout => 'wizard' | |
179 | - else | |
180 | - render :action => specific_action | |
181 | - end | |
175 | + render :action => specific_action | |
182 | 176 | return |
183 | 177 | end |
184 | 178 | end |
185 | 179 | |
186 | - if @wizard | |
187 | - render :action => 'index', :layout => 'wizard' | |
188 | - else | |
189 | - render :action => 'index' | |
190 | - end | |
180 | + render :action => 'index' | |
191 | 181 | end |
192 | 182 | |
193 | 183 | alias :assets :index |
... | ... | @@ -223,10 +213,10 @@ class SearchController < PublicController |
223 | 213 | end |
224 | 214 | |
225 | 215 | def tag |
226 | - @tag = environment.tags.find_by_name(params[:tag]) | |
216 | + @tag = params[:tag] | |
227 | 217 | @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_env_#{environment.id.to_s}_page_#{params[:npage]}" |
228 | 218 | if is_cache_expired?(@tag_cache_key, true) |
229 | - @tagged = environment.articles.find_tagged_with(@tag.name).paginate(:per_page => 10, :page => params[:npage]) | |
219 | + @tagged = environment.articles.find_tagged_with(@tag).paginate(:per_page => 10, :page => params[:npage]) | |
230 | 220 | end |
231 | 221 | end |
232 | 222 | ... | ... |
app/helpers/account_helper.rb
1 | 1 | module AccountHelper |
2 | 2 | |
3 | - | |
4 | - def button_to_step(type, step, current_step, html_options = {}) | |
5 | - if current_step == step | |
6 | - the_class = 'active' | |
7 | - if html_options.has_key?(:class) | |
8 | - html_options[:class] << " #{the_class}" | |
9 | - else | |
10 | - html_options[:class] = the_class | |
11 | - end | |
12 | - end | |
13 | - if step == 1 | |
14 | - url = '#' | |
15 | - else | |
16 | - url = send('url_step_' + step.to_s) | |
17 | - end | |
18 | - button(type, step.to_s, url, html_options) | |
19 | - end | |
20 | - | |
21 | - def button_to_step_without_text(type, step, html_options = {}) | |
22 | - url = 'url_step_' + step | |
23 | - button_without_text(type, step, send(url), html_options) | |
24 | - end | |
25 | - | |
26 | - def button_to_previous_step(step, html_options = {}) | |
27 | - step = step - 1 | |
28 | - if step > 1 | |
29 | - button_to_step_without_text(:left, step.to_s, html_options) | |
30 | - end | |
31 | - end | |
32 | - | |
33 | - def button_to_next_step(step, html_options = {}) | |
34 | - step = step + 1 | |
35 | - if step < 4 | |
36 | - button_to_step_without_text(:forward, step.to_s, html_options) | |
37 | - end | |
38 | - end | |
39 | - | |
40 | - def url_step_1 | |
41 | - options = {:controller => 'account', :action => 'signup', :wizard => true} | |
42 | - Noosfero.url_options.merge(options) | |
43 | - end | |
44 | - | |
45 | - def url_step_2 | |
46 | - options = {:controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true} | |
47 | - Noosfero.url_options.merge(options) | |
48 | - end | |
49 | - | |
50 | - def url_step_3 | |
51 | - options = {:controller => 'friends', :action => 'invite', :profile => user.identifier, :wizard => true} | |
52 | - Noosfero.url_options.merge(options) | |
53 | - end | |
54 | - | |
55 | 3 | end | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -27,18 +27,7 @@ module ApplicationHelper |
27 | 27 | include AccountHelper |
28 | 28 | |
29 | 29 | def locale |
30 | - FastGettext.locale | |
31 | - end | |
32 | - | |
33 | - def load_web2_conf | |
34 | - if File.exists?( RAILS_ROOT + '/config/web2.0.yml') | |
35 | - YAML.load_file( RAILS_ROOT + '/config/web2.0.yml' ) | |
36 | - else | |
37 | - {} | |
38 | - end | |
39 | - end | |
40 | - def web2_conf | |
41 | - @web_conf ||= load_web2_conf | |
30 | + (@page && !@page.language.blank?) ? @page.language : FastGettext.locale | |
42 | 31 | end |
43 | 32 | |
44 | 33 | # Displays context help. You can pass the content of the help message as the |
... | ... | @@ -217,7 +206,8 @@ module ApplicationHelper |
217 | 206 | if html_options.has_key?(:class) |
218 | 207 | the_class << ' ' << html_options[:class] |
219 | 208 | end |
220 | - link_to(' '+content_tag('span', label), url, html_options.merge(:class => the_class, :title => label)) | |
209 | + the_title = html_options[:title] || label | |
210 | + link_to(' '+content_tag('span', label), url, html_options.merge(:class => the_class, :title => the_title)) | |
221 | 211 | end |
222 | 212 | |
223 | 213 | def button_to_function(type, label, js_code, html_options = {}, &block) |
... | ... | @@ -278,6 +268,19 @@ module ApplicationHelper |
278 | 268 | end |
279 | 269 | end |
280 | 270 | |
271 | + def partial_for_task_class(klass, action) | |
272 | + if klass.nil? | |
273 | + raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' | |
274 | + end | |
275 | + | |
276 | + name = "#{klass.name.underscore}_#{action.to_s}" | |
277 | + if File.exists?(File.join(RAILS_ROOT, 'app', 'views', params[:controller], "_#{name}.rhtml")) | |
278 | + name | |
279 | + else | |
280 | + partial_for_task_class(klass.superclass, action) | |
281 | + end | |
282 | + end | |
283 | + | |
281 | 284 | def user |
282 | 285 | @controller.send(:user) |
283 | 286 | end |
... | ... | @@ -339,7 +342,7 @@ module ApplicationHelper |
339 | 342 | if ENV['RAILS_ENV'] == 'development' && environment.theme == 'random' |
340 | 343 | @random_theme ||= Dir.glob('public/designs/themes/*').map { |f| File.basename(f) }.rand |
341 | 344 | @random_theme |
342 | - elsif ENV['RAILS_ENV'] == 'development' && params[:theme] | |
345 | + elsif ENV['RAILS_ENV'] == 'development' && params[:theme] && File.exists?(File.join(Rails.root, 'public/designs/themes', params[:theme])) | |
343 | 346 | params[:theme] |
344 | 347 | else |
345 | 348 | if profile && !profile.theme.nil? |
... | ... | @@ -371,6 +374,24 @@ module ApplicationHelper |
371 | 374 | nil |
372 | 375 | end |
373 | 376 | |
377 | + def theme_favicon | |
378 | + return '/designs/themes/' + current_theme + '/favicon.ico' if profile.nil? || profile.theme.nil? | |
379 | + if File.exists?(File.join(RAILS_ROOT, 'public', theme_path, 'favicon.ico')) | |
380 | + '/designs/themes/' + profile.theme + '/favicon.ico' | |
381 | + else | |
382 | + favicon = profile.articles.find_by_path('favicon.ico') | |
383 | + if favicon | |
384 | + favicon.public_filename | |
385 | + else | |
386 | + '/designs/themes/' + environment.theme + '/favicon.ico' | |
387 | + end | |
388 | + end | |
389 | + end | |
390 | + | |
391 | + def theme_site_title | |
392 | + theme_include('site_title') | |
393 | + end | |
394 | + | |
374 | 395 | def theme_header |
375 | 396 | theme_include('header') |
376 | 397 | end |
... | ... | @@ -395,6 +416,7 @@ module ApplicationHelper |
395 | 416 | # |
396 | 417 | # If the profile has no image set yet, then a default image is used. |
397 | 418 | def profile_image(profile, size=:portrait, opt={}) |
419 | + return '' if profile.nil? | |
398 | 420 | opt[:alt] ||= profile.name() |
399 | 421 | opt[:title] ||= '' |
400 | 422 | opt[:class] ||= '' |
... | ... | @@ -402,21 +424,33 @@ module ApplicationHelper |
402 | 424 | image_tag(profile_icon(profile, size), opt ) |
403 | 425 | end |
404 | 426 | |
405 | - def profile_icon( profile, size=:portrait ) | |
427 | + def profile_icon( profile, size=:portrait, return_mimetype=false ) | |
428 | + filename, mimetype = '', 'image/png' | |
406 | 429 | if profile.image |
407 | - profile.image.public_filename( size ) | |
430 | + filename = profile.image.public_filename( size ) | |
431 | + mimetype = profile.image.content_type | |
408 | 432 | else |
409 | - if profile.organization? | |
410 | - if profile.kind_of?(Community) | |
411 | - '/images/icons-app/users_size-'+ size.to_s() +'.png' | |
433 | + icon = | |
434 | + if profile.organization? | |
435 | + if profile.kind_of?(Community) | |
436 | + '/images/icons-app/community-'+ size.to_s() +'.png' | |
437 | + else | |
438 | + '/images/icons-app/enterprise-'+ size.to_s() +'.png' | |
439 | + end | |
412 | 440 | else |
413 | - '/images/icons-app/enterprise-default-pic-'+ size.to_s() +'.png' | |
441 | + '/images/icons-app/person-'+ size.to_s() +'.png' | |
414 | 442 | end |
415 | - else | |
416 | - '/images/icons-app/user_icon_size-'+ size.to_s() +'.png' | |
417 | - end | |
443 | + filename = default_or_themed_icon(icon) | |
418 | 444 | end |
445 | + return_mimetype ? [filename, mimetype] : filename | |
446 | + end | |
419 | 447 | |
448 | + def default_or_themed_icon(icon) | |
449 | + if File.exists?(File.join(Rails.root, 'public', theme_path, icon)) | |
450 | + theme_path + icon | |
451 | + else | |
452 | + icon | |
453 | + end | |
420 | 454 | end |
421 | 455 | |
422 | 456 | def profile_sex_icon( profile ) |
... | ... | @@ -455,71 +489,69 @@ module ApplicationHelper |
455 | 489 | end |
456 | 490 | end |
457 | 491 | |
458 | - # displays a link to add the profile with its image (as generated by | |
459 | - # #profile_image) or only its name below. | |
460 | - def profile_add_link( profile, image=false, size=:portrait, tag='li') | |
461 | - the_class = profile.members.include?(user) ? 'profile_member' : '' | |
462 | - name = h(profile.short_name) | |
463 | - if image | |
464 | - display = content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + | |
465 | - content_tag( 'span', name, :class => 'org' ) + | |
466 | - profile_cat_icons( profile ) | |
467 | - the_class << ' vcard' | |
468 | - else | |
469 | - display = content_tag( 'span', name, :class => 'org' ) | |
492 | + def links_for_balloon(profile) | |
493 | + if environment.enabled?(:show_balloon_with_profile_links_when_clicked) | |
494 | + if profile.kind_of?(Person) | |
495 | + [ | |
496 | + {_('Wall') => {:href => url_for(profile.public_profile_url)}}, | |
497 | + {_('Friends') => {:href => url_for(:controller => :profile, :action => :friends, :profile => profile.identifier)}}, | |
498 | + {_('Communities') => {:href => url_for(:controller => :profile, :action => :communities, :profile => profile.identifier)}}, | |
499 | + {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}}, | |
500 | + {_('Add') => {:href => url_for(profile.add_url), :class => 'add-friend', :style => 'display: none'}} | |
501 | + ] | |
502 | + elsif profile.kind_of?(Community) | |
503 | + [ | |
504 | + {_('Wall') => {:href => url_for(profile.public_profile_url)}}, | |
505 | + {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}}, | |
506 | + {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}}, | |
507 | + {_('Join') => {:href => url_for(profile.join_url), :class => 'join-community', :style => 'display: none'}}, | |
508 | + {_('Leave') => {:href => url_for(profile.leave_url), :class => 'leave-community', :style => 'display: none'}}, | |
509 | + {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}} | |
510 | + ] | |
511 | + elsif profile.kind_of?(Enterprise) | |
512 | + [ | |
513 | + {_('Products') => {:href => catalog_path(profile.identifier)}}, | |
514 | + {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}}, | |
515 | + {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}}, | |
516 | + {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}}, | |
517 | + ] | |
518 | + else | |
519 | + [] | |
520 | + end | |
470 | 521 | end |
471 | - content_tag tag, | |
472 | - link_to_remote( display, | |
473 | - :update => 'search-results-and-pages', | |
474 | - :url => {:controller => 'account', :action => 'profile_details', :profile => profile.identifier}, | |
475 | - :onclick => 'document.location.href = this.href', # work-arround for ie. | |
476 | - :class => 'profile_link url', | |
477 | - :help => _('Click on this icon to add <b>%s</b> to your network') % profile.name, | |
478 | - :title => profile.name ), | |
479 | - :class => the_class | |
480 | 522 | end |
481 | 523 | |
482 | 524 | # displays a link to the profile homepage with its image (as generated by |
483 | 525 | # #profile_image) and its name below it. |
484 | - def profile_image_link( profile, size=:portrait, tag='li' ) | |
485 | - if profile.class == Person | |
486 | - name = profile.short_name | |
487 | - city = content_tag 'span', content_tag( 'span', profile.city, :class => 'locality' ), :class => 'adr' | |
526 | + def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil ) | |
527 | + name = profile.short_name | |
528 | + if profile.person? | |
529 | + url = url_for(profile.check_friendship_url) | |
530 | + trigger_class = 'person-trigger' | |
488 | 531 | else |
489 | - name = profile.short_name | |
490 | 532 | city = '' |
533 | + url = url_for(profile.check_membership_url) | |
534 | + if profile.community? | |
535 | + trigger_class = 'community-trigger' | |
536 | + elsif profile.enterprise? | |
537 | + trigger_class = 'enterprise-trigger' | |
538 | + end | |
491 | 539 | end |
540 | + extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) | |
541 | + links = links_for_balloon(profile) | |
492 | 542 | content_tag tag, |
543 | + (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") + | |
493 | 544 | link_to( |
494 | 545 | content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + |
495 | 546 | content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + |
496 | - city + profile_sex_icon( profile ) + profile_cat_icons( profile ), | |
547 | + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ), | |
497 | 548 | profile.url, |
498 | - :onclick => 'document.location.href = this.href', # work-arround for ie. | |
499 | 549 | :class => 'profile_link url', |
500 | 550 | :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, |
501 | 551 | :title => profile.name ), |
502 | 552 | :class => 'vcard' |
503 | 553 | end |
504 | 554 | |
505 | - # displays a link to the community homepage with its image (as generated by | |
506 | - # #profile_image) and its name and number of members beside it. | |
507 | - def community_image_link( profile, size=:portrait, tag='li' ) | |
508 | - name = h(profile.name) | |
509 | - content_tag tag, | |
510 | - link_to( | |
511 | - content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + | |
512 | - content_tag( 'span', name, :class => 'org' ) + | |
513 | - content_tag( 'span', n_('1 member', '%s members', profile.members.count) % profile.members.count, :class => 'community-member-count' ), | |
514 | - profile.url, | |
515 | - :onclick => 'document.location.href = this.href', # work-arround for ie. | |
516 | - :class => 'profile_link url', | |
517 | - :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, | |
518 | - :title => profile.name ) + | |
519 | - '<br class="may-clear"/>', | |
520 | - :class => 'vcard' | |
521 | - end | |
522 | - | |
523 | 555 | def gravatar_url_for(email, options = {}) |
524 | 556 | # Ta dando erro de roteamento |
525 | 557 | url_for( { :gravatar_id => Digest::MD5.hexdigest(email), |
... | ... | @@ -527,12 +559,12 @@ module ApplicationHelper |
527 | 559 | :protocol => 'http://', |
528 | 560 | :only_path => false, |
529 | 561 | :controller => 'avatar.php', |
530 | - :d => web2_conf['gravatar'] ? web2_conf['gravatar']['default'] : nil | |
562 | + :d => NOOSFERO_CONF['gravatar'] ? NOOSFERO_CONF['gravatar'] : nil | |
531 | 563 | }.merge(options) ) |
532 | 564 | end |
533 | 565 | |
534 | 566 | def str_gravatar_url_for(email, options = {}) |
535 | - default = web2_conf['gravatar'] ? web2_conf['gravatar']['default'] : nil | |
567 | + default = NOOSFERO_CONF['gravatar'] ? NOOSFERO_CONF['gravatar'] : nil | |
536 | 568 | url = 'http://www.gravatar.com/avatar.php?gravatar_id=' + |
537 | 569 | Digest::MD5.hexdigest(email) |
538 | 570 | { |
... | ... | @@ -591,7 +623,16 @@ module ApplicationHelper |
591 | 623 | end |
592 | 624 | |
593 | 625 | def select_folder(label, object, method, collection, html_options = {}, js_options = {}) |
594 | - labelled_form_field(label, select(object, method, collection.map {|f| [ profile.identifier + '/' + f.full_name, f.id ] }, html_options.merge({:include_blank => "#{profile.identifier}"}), js_options)) | |
626 | + root = profile ? profile.identifier : _("root") | |
627 | + labelled_form_field(label, select(object, method, | |
628 | + collection.map {|f| [ root + '/' + f.full_name, f.id ]}, | |
629 | + {:include_blank => root}, html_options.merge(js_options))) | |
630 | + end | |
631 | + | |
632 | + def select_profile_folder(label, object, method, profile, html_options = {}, js_options = {}) | |
633 | + labelled_form_field(label, select(object, method, | |
634 | + profile.folders.map {|f| [ profile.identifier + '/' + f.full_name, f.id ]}, | |
635 | + {:include_blank => profile.identifier}, html_options.merge(js_options))) | |
595 | 636 | end |
596 | 637 | |
597 | 638 | def theme_option(opt = nil) |
... | ... | @@ -851,6 +892,7 @@ module ApplicationHelper |
851 | 892 | |
852 | 893 | def helper_for_article(article) |
853 | 894 | article_helper = ActionView::Base.new |
895 | + article_helper.controller = controller | |
854 | 896 | article_helper.extend ArticleHelper |
855 | 897 | begin |
856 | 898 | class_name = article.class.name + 'Helper' |
... | ... | @@ -877,37 +919,27 @@ module ApplicationHelper |
877 | 919 | end |
878 | 920 | end |
879 | 921 | |
880 | - def ask_to_join? | |
881 | - return if !environment.enabled?(:join_community_popup) | |
882 | - return if params[:action] == 'join' | |
883 | - return unless profile && profile.kind_of?(Community) | |
884 | - if (session[:no_asking] && session[:no_asking].include?(profile.id)) | |
885 | - return false | |
886 | - end | |
887 | - if logged_in? | |
888 | - user.ask_to_join?(profile) | |
889 | - else | |
890 | - true | |
891 | - end | |
892 | - end | |
893 | - | |
894 | 922 | def icon_theme_stylesheet_path |
895 | - theme_path = "/designs/icons/#{environment.icon_theme}/style.css" | |
896 | - if File.exists?(File.join(RAILS_ROOT, 'public', theme_path)) | |
897 | - theme_path | |
898 | - else | |
899 | - '/designs/icons/default/style.css' | |
923 | + icon_themes = [] | |
924 | + theme_icon_themes = theme_option(:icon_theme) || [] | |
925 | + for icon_theme in theme_icon_themes do | |
926 | + theme_path = "/designs/icons/#{icon_theme}/style.css" | |
927 | + if File.exists?(File.join(RAILS_ROOT, 'public', theme_path)) | |
928 | + icon_themes << theme_path | |
929 | + end | |
900 | 930 | end |
931 | + icon_themes | |
901 | 932 | end |
902 | 933 | |
903 | 934 | def page_title |
904 | - (@page ? @page.name + ' - ' : '') + | |
935 | + (@page ? @page.title + ' - ' : '') + | |
905 | 936 | (profile ? profile.short_name + ' - ' : '') + |
906 | 937 | (@topic ? @topic.title + ' - ' : '') + |
907 | 938 | (@section ? @section.title + ' - ' : '') + |
908 | 939 | (@toc ? _('Online Manual') + ' - ' : '') + |
940 | + (@controller.controller_name == 'chat' ? _('Chat') + ' - ' : '') + | |
909 | 941 | environment.name + |
910 | - (@category ? "→ #{@category.full_name}" : '') | |
942 | + (@category ? " - #{@category.full_name}" : '') | |
911 | 943 | end |
912 | 944 | |
913 | 945 | def noosfero_javascript |
... | ... | @@ -946,7 +978,7 @@ module ApplicationHelper |
946 | 978 | end |
947 | 979 | |
948 | 980 | def article_to_html(article, options = {}) |
949 | - options.merge(:page => params[:npage]) | |
981 | + options.merge!(:page => params[:npage]) | |
950 | 982 | content = article.to_html(options) |
951 | 983 | return self.instance_eval(&content) if content.kind_of?(Proc) |
952 | 984 | content |
... | ... | @@ -960,4 +992,221 @@ module ApplicationHelper |
960 | 992 | text_field_tag(name, value, options.merge(:class => 'colorpicker_field')) |
961 | 993 | end |
962 | 994 | |
995 | + def ui_icon(icon_class, extra_class = '') | |
996 | + "<span class='ui-icon #{icon_class} #{extra_class}' style='float:left; margin-right:7px;'></span>" | |
997 | + end | |
998 | + | |
999 | + def ui_button(label, url, html_options = {}) | |
1000 | + link_to(label, url, html_options.merge(:class => 'ui_button fg-button')) | |
1001 | + end | |
1002 | + | |
1003 | + def ui_button_to_remote(label, options, html_options = {}) | |
1004 | + link_to_remote(label, options, html_options.merge(:class => 'ui_button fg-button')) | |
1005 | + end | |
1006 | + | |
1007 | + def jquery_theme | |
1008 | + theme_option(:jquery_theme) || 'smoothness_mod' | |
1009 | + end | |
1010 | + | |
1011 | + def jquery_ui_theme_stylesheet_path | |
1012 | + 'jquery.ui/' + jquery_theme + '/jquery-ui-1.8.2.custom' | |
1013 | + end | |
1014 | + | |
1015 | + def ui_error(message) | |
1016 | + content_tag('div', ui_icon('ui-icon-alert') + message, :class => 'alert fg-state-error ui-state-error') | |
1017 | + end | |
1018 | + | |
1019 | + def ui_highlight(message) | |
1020 | + content_tag('div', ui_icon('ui-icon-info') + message, :class => 'alert fg-state-highlight ui-state-highlight') | |
1021 | + end | |
1022 | + | |
1023 | + def float_to_currency(value) | |
1024 | + number_to_currency(value, :unit => environment.currency_unit, :separator => environment.currency_separator, :delimiter => environment.currency_delimiter, :format => "%u %n") | |
1025 | + end | |
1026 | + | |
1027 | + def collapsed_item_icon | |
1028 | + "<span class='ui-icon ui-icon-circlesmall-plus' style='float:left;'></span>" | |
1029 | + end | |
1030 | + def expanded_item_icon | |
1031 | + "<span class='ui-icon ui-icon-circlesmall-minus' style='float:left;'></span>" | |
1032 | + end | |
1033 | + def leaf_item_icon | |
1034 | + "<span class='ui-icon ui-icon-arrow-1-e' style='float:left;'></span>" | |
1035 | + end | |
1036 | + | |
1037 | + def display_category_menu(block, categories, root = true) | |
1038 | + categories = categories.sort{|x,y| x.name <=> y.name} | |
1039 | + return "" if categories.blank? | |
1040 | + content_tag(:ul, | |
1041 | + categories.map do |category| | |
1042 | + category_path = category.kind_of?(ProductCategory) ? {:controller => 'search', :action => 'assets', :asset => 'products', :product_category => category.id} : { :controller => 'search', :action => 'category_index', :category_path => category.explode_path } | |
1043 | + category.display_in_menu? ? | |
1044 | + content_tag(:li, | |
1045 | + ( !category.is_leaf_displayable_in_menu? ? content_tag(:a, collapsed_item_icon, :href => "#", :id => "block_#{block.id}_category_#{category.id}", :class => 'category-link-expand ' + (root ? 'category-root' : 'category-no-root'), :onclick => "expandCategory(#{block.id}, #{category.id}); return false", :style => 'display: none') : leaf_item_icon) + | |
1046 | + link_to(content_tag(:span, category.name, :class => 'category-name'), category_path, :class => ("category-leaf" if category.is_leaf_displayable_in_menu?)) + | |
1047 | + content_tag(:div, display_category_menu(block, category.children, false), :id => "block_#{block.id}_category_content_#{category.id}", :class => 'child-category') | |
1048 | + ) : '' | |
1049 | + end | |
1050 | + ) + | |
1051 | + content_tag(:p) + | |
1052 | + (root ? javascript_tag(" | |
1053 | + jQuery('.child-category').hide(); | |
1054 | + jQuery('.category-link-expand').show(); | |
1055 | + var expanded_icon = \"#{ expanded_item_icon }\"; | |
1056 | + var collapsed_icon = \"#{ collapsed_item_icon }\"; | |
1057 | + var category_expanded = { 'block' : 0, 'category' : 0 }; | |
1058 | + ") : '') | |
1059 | + end | |
1060 | + | |
1061 | + def browse_people_menu | |
1062 | + links = [ | |
1063 | + {s_('people|More Recent') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_recent'})}}, | |
1064 | + {s_('people|More Active') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_active'})}}, | |
1065 | + {s_('people|More Popular') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_popular'})}} | |
1066 | + ] | |
1067 | + if logged_in? | |
1068 | + links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})}) | |
1069 | + links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})}) | |
1070 | + end | |
1071 | + | |
1072 | + link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "browse", :action => 'people'}, :id => 'submenu-people') + | |
1073 | + link_to(content_tag(:span, _('People Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-people-trigger') | |
1074 | + end | |
1075 | + | |
1076 | + def browse_communities_menu | |
1077 | + links = [ | |
1078 | + {s_('communities|More Recent') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_recent'})}}, | |
1079 | + {s_('communities|More Active') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_active'})}}, | |
1080 | + {s_('communities|More Popular') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_popular'})}} | |
1081 | + ] | |
1082 | + if logged_in? | |
1083 | + links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})}) | |
1084 | + links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})}) | |
1085 | + end | |
1086 | + | |
1087 | + link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "browse", :action => 'communities'}, :id => 'submenu-communities') + | |
1088 | + link_to(content_tag(:span, _('Communities Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-communities-trigger') | |
1089 | + end | |
1090 | + | |
1091 | + def pagination_links(collection, options={}) | |
1092 | + options = {:prev_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options) | |
1093 | + will_paginate(collection, options) | |
1094 | + end | |
1095 | + | |
1096 | + def render_environment_features(folder) | |
1097 | + result = '' | |
1098 | + environment.enabled_features.keys.each do |feature| | |
1099 | + file = File.join(@controller.view_paths, 'shared', folder.to_s, "#{feature}.rhtml") | |
1100 | + if File.exists?(file) | |
1101 | + result << render(:file => file, :use_full_path => false) | |
1102 | + end | |
1103 | + end | |
1104 | + result | |
1105 | + end | |
1106 | + | |
1107 | + def usermenu_logged_in | |
1108 | + pending_tasks_count = '' | |
1109 | + if user && user.all_pending_tasks.count > 0 | |
1110 | + pending_tasks_count = link_to(user.all_pending_tasks.count.to_s, '/myprofile/{login}/tasks', :id => 'pending-tasks-count', :title => _("Manage your pending tasks")) | |
1111 | + end | |
1112 | + | |
1113 | + (_('Welcome, %s') % link_to('<i></i><strong>{login}</strong>', '/{login}', :id => "homepage-link", :title => _('Go to your homepage'))) + | |
1114 | + render_environment_features(:usermenu) + | |
1115 | + link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', { :controller => 'admin_panel', :action => 'index' }, :id => "controlpanel", :title => _("Configure the environment"), :class => 'admin-link', :style => 'display: none') + | |
1116 | + link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', '/myprofile/{login}', :id => "controlpanel", :title => _("Configure your personal account and content")) + | |
1117 | + pending_tasks_count + | |
1118 | + link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) | |
1119 | + end | |
1120 | + | |
1121 | + def limited_text_area(object_name, method, limit, text_area_id, options = {}) | |
1122 | + content_tag(:div, [ | |
1123 | + text_area(object_name, method, { :id => text_area_id, :onkeyup => "limited_text_area('#{text_area_id}', #{limit})" }.merge(options)), | |
1124 | + content_tag(:p, content_tag(:span, limit) + ' ' + _(' characters left'), :id => text_area_id + '_left'), | |
1125 | + content_tag(:p, _('Limit of characters reached'), :id => text_area_id + '_limit', :style => 'display: none') | |
1126 | + ], :class => 'limited-text-area') | |
1127 | + end | |
1128 | + | |
1129 | + def pluralize_without_count(count, singular, plural = nil) | |
1130 | + count == 1 ? singular : (plural || singular.pluralize) | |
1131 | + end | |
1132 | + | |
1133 | + def unique_with_count(list, connector = 'for') | |
1134 | + list.sort.inject(Hash.new(0)){|h,i| h[i] += 1; h }.collect{ |x, n| [n, connector, x].join(" ") }.sort | |
1135 | + end | |
1136 | + | |
1137 | + #FIXME Use time_ago_in_words instead of this method if you're using Rails 2.2+ | |
1138 | + def time_ago_as_sentence(from_time, include_seconds = false) | |
1139 | + to_time = Time.now | |
1140 | + from_time = from_time.to_time if from_time.respond_to?(:to_time) | |
1141 | + to_time = to_time.to_time if to_time.respond_to?(:to_time) | |
1142 | + distance_in_minutes = (((to_time - from_time).abs)/60).round | |
1143 | + distance_in_seconds = ((to_time - from_time).abs).round | |
1144 | + case distance_in_minutes | |
1145 | + when 0..1 | |
1146 | + return (distance_in_minutes == 0) ? _('less than a minute') : _('1 minute') unless include_seconds | |
1147 | + case distance_in_seconds | |
1148 | + when 0..4 then _('less than 5 seconds') | |
1149 | + when 5..9 then _('less than 10 seconds') | |
1150 | + when 10..19 then _('less than 20 seconds') | |
1151 | + when 20..39 then _('half a minute') | |
1152 | + when 40..59 then _('less than a minute') | |
1153 | + else _('1 minute') | |
1154 | + end | |
1155 | + | |
1156 | + when 2..44 then _('%{distance} minutes') % { :distance => distance_in_minutes } | |
1157 | + when 45..89 then _('about 1 hour') | |
1158 | + when 90..1439 then _('about %{distance} hours') % { :distance => (distance_in_minutes.to_f / 60.0).round } | |
1159 | + when 1440..2879 then _('1 day') | |
1160 | + when 2880..43199 then _('%{distance} days') % { :distance => (distance_in_minutes / 1440).round } | |
1161 | + when 43200..86399 then _('about 1 month') | |
1162 | + when 86400..525599 then _('%{distance} months') % { :distance => (distance_in_minutes / 43200).round } | |
1163 | + when 525600..1051199 then _('about 1 year') | |
1164 | + else _('over %{distance} years') % { :distance => (distance_in_minutes / 525600).round } | |
1165 | + end | |
1166 | + end | |
1167 | + | |
1168 | + def comment_balloon(options = {}, &block) | |
1169 | + wrapper = content_tag(:div, capture(&block), :class => 'comment-balloon-content') | |
1170 | + (1..8).to_a.reverse.each { |i| wrapper = content_tag(:div, wrapper, :class => "comment-wrapper-#{i}") } | |
1171 | + classes = options.delete(:class) || options.delete("class") || '' | |
1172 | + concat(content_tag('div', wrapper + tag('br', :style => 'clear: both;'), { :class => 'comment-balloon ' + classes.to_s }.merge(options)), block.binding) | |
1173 | + end | |
1174 | + | |
1175 | + def display_source_info(page) | |
1176 | + if !page.source.blank? | |
1177 | + source_url = link_to(page.source_name.blank? ? page.source : page.source_name, page.source) | |
1178 | + elsif page.reference_article | |
1179 | + source_url = link_to(page.reference_article.profile.name, page.reference_article.url) | |
1180 | + end | |
1181 | + content_tag(:div, _('Source: %s') % source_url, :id => 'article-source') unless source_url.nil? | |
1182 | + end | |
1183 | + | |
1184 | + def task_information(task) | |
1185 | + values = {} | |
1186 | + values.merge!({:requestor => link_to(task.requestor.name, task.requestor.public_profile_url)}) if task.requestor | |
1187 | + values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject | |
1188 | + values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject | |
1189 | + values.merge!(task.information[:variables]) if task.information[:variables] | |
1190 | + task.information[:message] % values | |
1191 | + end | |
1192 | + | |
1193 | + def add_zoom_to_images | |
1194 | + if environment.enabled?(:show_zoom_button_on_article_images) | |
1195 | + stylesheet_link_tag('fancybox') + | |
1196 | + javascript_include_tag('jquery.fancybox-1.3.4.pack') + | |
1197 | + javascript_tag("jQuery(function($) { | |
1198 | + $('#article .article-body img').each( function(index) { | |
1199 | + var original = original_image_dimensions($(this).attr('src')); | |
1200 | + if ($(this).width() < original['width'] || $(this).height() < original['height']) { | |
1201 | + $(this).wrap('<div class=\"zoomable-image\" />'); | |
1202 | + $(this).parent('.zoomable-image').attr('style', $(this).attr('style')); | |
1203 | + $(this).attr('style', ''); | |
1204 | + $(this).after(\'<a href=\"' + $(this).attr('src') + '\" class=\"zoomify-image\"><span class=\"zoomify-text\">%s</span></a>'); | |
1205 | + } | |
1206 | + }); | |
1207 | + $('.zoomify-image').fancybox(); | |
1208 | + });" % _('Zoom in')) | |
1209 | + end | |
1210 | + end | |
1211 | + | |
963 | 1212 | end | ... | ... |
app/helpers/article_helper.rb
... | ... | @@ -8,12 +8,13 @@ module ArticleHelper |
8 | 8 | 'div', |
9 | 9 | check_box(:article, :published) + |
10 | 10 | content_tag('label', _('This article must be published (visible to other people)'), :for => 'article_published') |
11 | - ) + | |
11 | + ) + (article.parent && article.parent.forum? && controller.action_name == 'new' ? | |
12 | + hidden_field_tag('article[accept_comments]', 1) : | |
12 | 13 | content_tag( |
13 | 14 | 'div', |
14 | 15 | check_box(:article, :accept_comments) + |
15 | - content_tag('label', _('I want to receive comments about this article'), :for => 'article_accept_comments') | |
16 | - ) + | |
16 | + content_tag('label', (article.parent && article.parent.forum? ? _('This topic is opened for replies') : _('I want to receive comments about this article')), :for => 'article_accept_comments') | |
17 | + )) + | |
17 | 18 | content_tag( |
18 | 19 | 'div', |
19 | 20 | check_box(:article, :notify_comments) + | ... | ... |
app/helpers/assets_helper.rb
... | ... | @@ -9,7 +9,7 @@ module AssetsHelper |
9 | 9 | [ options.merge(:asset => 'products'), "icon-menu-product", _('Products') ], |
10 | 10 | [ options.merge(:asset => 'enterprises'), "icon-menu-enterprise", __('Enterprises') ], |
11 | 11 | [ options.merge(:asset => 'communities'), "icon-menu-community", __('Communities') ], |
12 | - [ options.merge(:asset => 'events'), "icon-menu-events", __('Events') ], | |
12 | + [ options.merge(:asset => 'events'), "icon-event", __('Events') ], | |
13 | 13 | |
14 | 14 | ].select do |target, css_class, name| |
15 | 15 | !environment.enabled?('disable_asset_' + target[:asset]) | ... | ... |
app/helpers/block_helper.rb
app/helpers/blog_helper.rb
... | ... | @@ -11,7 +11,7 @@ module BlogHelper |
11 | 11 | end |
12 | 12 | |
13 | 13 | def cms_label_for_edit |
14 | - _('Edit blog') | |
14 | + _('Configure blog') | |
15 | 15 | end |
16 | 16 | |
17 | 17 | def list_posts(articles, format = 'full') |
... | ... | @@ -48,7 +48,7 @@ module BlogHelper |
48 | 48 | |
49 | 49 | def display_short_format(article) |
50 | 50 | html = content_tag('div', |
51 | - article.first_paragraph + | |
51 | + article.lead + | |
52 | 52 | content_tag('div', |
53 | 53 | link_to_comments(article) + |
54 | 54 | link_to( _('Read more'), article.url), | ... | ... |
app/helpers/boxes_helper.rb
... | ... | @@ -65,7 +65,7 @@ module BoxesHelper |
65 | 65 | end |
66 | 66 | |
67 | 67 | def display_box_content(box, main_content) |
68 | - context = { :article => @page } | |
68 | + context = { :article => @page, :request_path => request.path, :locale => locale } | |
69 | 69 | box_decorator.select_blocks(box.blocks, context).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box) |
70 | 70 | end |
71 | 71 | ... | ... |
app/helpers/catalog_helper.rb
1 | 1 | module CatalogHelper |
2 | 2 | |
3 | 3 | include DisplayHelper |
4 | +include ManageProductsHelper | |
4 | 5 | |
5 | 6 | def display_products_list(profile, products) |
6 | 7 | data = '' |
... | ... | @@ -19,7 +20,7 @@ include DisplayHelper |
19 | 20 | content_tag('h1', _('Products/Services')) + content_tag('ul', data, :id => 'product_list') |
20 | 21 | end |
21 | 22 | |
22 | -private | |
23 | + private | |
23 | 24 | |
24 | 25 | def product_category_name(profile, product_category) |
25 | 26 | if profile.enabled? | ... | ... |
app/helpers/categories_helper.rb
... | ... | @@ -34,8 +34,7 @@ module CategoriesHelper |
34 | 34 | |
35 | 35 | def select_category_type(field) |
36 | 36 | value = params[field] |
37 | - types = TYPES.select { |title,typename| environment.category_types.include?(typename) } | |
38 | - labelled_form_field(_('Type of category'), select_tag('type', options_for_select(types, value))) | |
37 | + labelled_form_field(_('Type of category'), select_tag('type', options_for_select(TYPES, value))) | |
39 | 38 | end |
40 | 39 | |
41 | 40 | end | ... | ... |
... | ... | @@ -0,0 +1,24 @@ |
1 | +module ChatHelper | |
2 | + | |
3 | + def user_status_menu(icon_class, status) | |
4 | + links = [ | |
5 | + ['icon-menu-online', _('Online'), 'chat-connect'], | |
6 | + ['icon-menu-busy', _('Busy'), 'chat-busy'], | |
7 | + ['icon-menu-offline', _('Sign out of chat'), 'chat-disconnect'], | |
8 | + ] | |
9 | + content_tag('span', | |
10 | + link_to(content_tag('span', status) + ui_icon('ui-icon-triangle-1-s'), | |
11 | + '#', | |
12 | + :onclick => 'toggleMenu(this); return false', | |
13 | + :class => icon_class + ' simplemenu-trigger' | |
14 | + ) + | |
15 | + content_tag('ul', | |
16 | + links.map{|link| content_tag('li', link_to(link[1], '#', :class => link[0], :id => link[2], 'data-jid' => user.jid), :class => 'simplemenu-item') }.join("\n"), | |
17 | + :style => 'display: none; z-index: 100', | |
18 | + :class => 'simplemenu-submenu' | |
19 | + ), | |
20 | + :class => 'user-status' | |
21 | + ) | |
22 | + end | |
23 | + | |
24 | +end | ... | ... |
app/helpers/cms_helper.rb
... | ... | @@ -27,4 +27,34 @@ module CmsHelper |
27 | 27 | article_helper.custom_options_for_article(article) |
28 | 28 | end |
29 | 29 | |
30 | + def link_to_article(article) | |
31 | + article_name = short_filename(article.title, 30) | |
32 | + if article.folder? | |
33 | + link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article) | |
34 | + else | |
35 | + if article.image? | |
36 | + image_tag(icon_for_article(article)) + link_to(article_name, article.url) | |
37 | + else | |
38 | + link_to article_name, article.url, :class => icon_for_article(article) | |
39 | + end | |
40 | + end | |
41 | + end | |
42 | + | |
43 | + def display_spread_button(profile, article) | |
44 | + if profile.person? | |
45 | + button_without_text :spread, _('Spread this'), :action => 'publish', :id => article.id | |
46 | + elsif profile.community? && environment.portal_community | |
47 | + button_without_text :spread, _('Spread this'), :action => 'publish_on_portal_community', :id => article.id | |
48 | + end | |
49 | + end | |
50 | + | |
51 | + def display_delete_button(article) | |
52 | + confirm_message = if article.folder? | |
53 | + _('Are you sure that you want to remove this folder? Note that all the items inside it will also be removed!') | |
54 | + else | |
55 | + _('Are you sure that you want to remove this item?') | |
56 | + end | |
57 | + | |
58 | + button_without_text :delete, _('Delete'), { :action => 'destroy', :id => article.id }, :method => :post, :confirm => confirm_message | |
59 | + end | |
30 | 60 | end | ... | ... |
app/helpers/content_viewer_helper.rb
1 | 1 | module ContentViewerHelper |
2 | 2 | |
3 | 3 | include BlogHelper |
4 | + include ForumHelper | |
4 | 5 | |
5 | 6 | def number_of_comments(article) |
6 | 7 | n = article.comments.size |
... | ... | @@ -20,7 +21,7 @@ module ContentViewerHelper |
20 | 21 | title = content_tag('h1', link_to(article.name, article.url), :class => 'title') |
21 | 22 | end |
22 | 23 | comments = args[:no_comments] ? '' : (("- %s") % link_to_comments(article)) |
23 | - title << content_tag('span', _("%s, by %s %s") % [show_date(article.published_at), link_to(article.author.name, article.author.url), comments], :class => 'created-at') | |
24 | + title << content_tag('span', _("%s, by %s %s") % [show_date(article.published_at), link_to(article.author_name, article.author.url), comments], :class => 'created-at') | |
24 | 25 | end |
25 | 26 | title |
26 | 27 | end |
... | ... | @@ -30,8 +31,20 @@ module ContentViewerHelper |
30 | 31 | end |
31 | 32 | |
32 | 33 | def image_label(image) |
33 | - text = image.title || image.abstract | |
34 | + text = image.abstract || image.title | |
34 | 35 | text && (text.first(40) + (text.size > 40 ? '…' : '')) |
35 | 36 | end |
36 | 37 | |
38 | + def article_translations(article) | |
39 | + unless article.native_translation.translations.empty? | |
40 | + links = (article.native_translation.translations + [article.native_translation]).map do |translation| | |
41 | + { Noosfero.locales[translation.language] => { :href => url_for(translation.url) } } | |
42 | + end | |
43 | + content_tag(:div, link_to(_('Translations'), '#', | |
44 | + :onclick => "toggleSubmenu(this, '#{_('Translations')}', #{links.to_json}); return false", | |
45 | + :class => 'article-translations-menu simplemenu-trigger up'), | |
46 | + :class => 'article-translations') | |
47 | + end | |
48 | + end | |
49 | + | |
37 | 50 | end | ... | ... |
app/helpers/dates_helper.rb
... | ... | @@ -36,7 +36,7 @@ module DatesHelper |
36 | 36 | # formats a datetime for displaying. |
37 | 37 | def show_time(time) |
38 | 38 | if time |
39 | - _('%{day} %{month} %{year}, %{hour}:%{minutes}') % { :year => time.year, :month => month_name(time.month), :day => time.day, :hour => time.hour, :minutes => time.min } | |
39 | + _('%{day} %{month} %{year}, %{hour}:%{minutes}') % { :year => time.year, :month => month_name(time.month), :day => time.day, :hour => time.hour, :minutes => time.strftime("%M") } | |
40 | 40 | else |
41 | 41 | '' |
42 | 42 | end |
... | ... | @@ -46,7 +46,7 @@ module DatesHelper |
46 | 46 | if (date1 == date2) || (date2.nil?) |
47 | 47 | show_date(date1) |
48 | 48 | else |
49 | - _('from %s to %s') % [show_date(date1), show_date(date2)] | |
49 | + _('from %{date1} to %{date2}') % {:date1 => show_date(date1), :date2 => show_date(date2)} | |
50 | 50 | end |
51 | 51 | end |
52 | 52 | ... | ... |
app/helpers/display_helper.rb
... | ... | @@ -2,12 +2,16 @@ module DisplayHelper |
2 | 2 | |
3 | 3 | def link_to_product(product, opts={}) |
4 | 4 | return _('No product') unless product |
5 | - target = product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'catalog', :action => 'show', :id => product) : product.enterprise.url | |
5 | + target = product_path(product) | |
6 | 6 | link_to content_tag( 'span', product.name ), |
7 | 7 | target, |
8 | 8 | opts |
9 | 9 | end |
10 | 10 | |
11 | + def product_path(product) | |
12 | + product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url | |
13 | + end | |
14 | + | |
11 | 15 | def link_to_category(category, full = true) |
12 | 16 | return _('Uncategorized product') unless category |
13 | 17 | name = full ? category.full_name(' → ') : category.name |
... | ... | @@ -27,9 +31,10 @@ module DisplayHelper |
27 | 31 | gsub( /\n\s*\n/, ' <p/> ' ). |
28 | 32 | gsub( /\n/, ' <br/> ' ). |
29 | 33 | gsub( /(^|\s)(www\.[^\s])/, '\1http://\2' ). |
30 | - gsub( /(https?:\/\/([^\s]+))/, | |
31 | - '<a href="\1" target="_blank" rel="nofolow" onclick="return confirm(\'' + | |
32 | - escape_javascript( _('Are you sure you want to visit this web site?') ) + | |
33 | - '\n\n\'+this.href)">\2</a>' ) | |
34 | + gsub( /(https?:\/\/([^\s]+))/ ) do | |
35 | + href, content = $1, $2.scan(/.{4}/).join('​') | |
36 | + content_tag(:a, content, :href => href, :target => '_blank', :rel => 'nofolow', | |
37 | + :onclick => "return confirm('%s')" % escape_javascript(_('Are you sure you want to visit this web site?'))) | |
38 | + end | |
34 | 39 | end |
35 | 40 | end | ... | ... |
app/helpers/events_helper.rb
... | ... | @@ -6,7 +6,7 @@ module EventsHelper |
6 | 6 | content_tag('h2', title) + |
7 | 7 | content_tag('div', |
8 | 8 | (events.any? ? |
9 | - content_tag('table', events.select { |item| item.public? }.map {|item| display_event_in_listing(item)}.join('')) : | |
9 | + content_tag('table', events.select { |item| item.display_to?(user) }.map {|item| display_event_in_listing(item)}.join('')) : | |
10 | 10 | content_tag('em', _('No events for this date'), :class => 'no-events') |
11 | 11 | ), :id => 'agenda-items' |
12 | 12 | ) |
... | ... | @@ -15,7 +15,7 @@ module EventsHelper |
15 | 15 | def display_event_in_listing(article) |
16 | 16 | content_tag( |
17 | 17 | 'tr', |
18 | - content_tag('td', link_to(image_tag(icon_for_article(article)) + article.name, article.url)), | |
18 | + content_tag('td', link_to(article.name, article.url, :class => icon_for_article(article))), | |
19 | 19 | :class => 'agenda-item' |
20 | 20 | ) |
21 | 21 | end |
... | ... | @@ -26,7 +26,7 @@ module EventsHelper |
26 | 26 | # the day itself |
27 | 27 | date, |
28 | 28 | # is there any events in this date? |
29 | - events.any? do |event| | |
29 | + events.select {|event| event.display_to?(user)}.any? do |event| | |
30 | 30 | event.date_range.include?(date) |
31 | 31 | end, |
32 | 32 | # is this date in the current month? | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +module FloatHelper | |
2 | + | |
3 | + def decimal_to_float( num ) | |
4 | + if num.count('.') == 1 && num.count(',') == 0 | |
5 | + # number like "12.34" | |
6 | + return num.to_f | |
7 | + end | |
8 | + | |
9 | + if num.count('.') == 0 && num.count(',') == 1 | |
10 | + # number like "12,34" | |
11 | + return num.tr(',','.').to_f | |
12 | + end | |
13 | + | |
14 | + if num.count('.') > 0 && num.count(',') > 0 | |
15 | + # number like "12.345.678,90" or "12,345,678.90" | |
16 | + dec_sep = num.tr('0-9','')[-1].chr | |
17 | + return num.tr('^0-9'+dec_sep,'').tr(dec_sep,'.').to_f | |
18 | + end | |
19 | + | |
20 | + # if you are here is because there is only one | |
21 | + # separator and this appears 2 times or more. | |
22 | + # number like "12.345.678" or "12,345,678" | |
23 | + | |
24 | + return num.tr(',.','').to_f | |
25 | + end | |
26 | + | |
27 | +end | ... | ... |
app/helpers/folder_helper.rb
1 | 1 | module FolderHelper |
2 | 2 | |
3 | + include ShortFilename | |
4 | + | |
3 | 5 | def list_articles(articles, recursive = false) |
4 | 6 | if !articles.blank? |
5 | - content_tag( | |
6 | - 'table', | |
7 | - content_tag('tr', content_tag('th', _('Title')) + content_tag('th', _('Last update'))) + | |
8 | - articles.map {|item| display_article_in_listing(item, recursive, 0)}.join('') | |
7 | + articles = articles.paginate( | |
8 | + :order => "updated_at DESC", | |
9 | + :per_page => 10, | |
10 | + :page => params[:npage] | |
9 | 11 | ) |
12 | + | |
13 | + render :file => 'shared/articles_list', :locals => {:articles => articles, :recursive => recursive} | |
10 | 14 | else |
11 | 15 | content_tag('em', _('(empty folder)')) |
12 | 16 | end |
... | ... | @@ -17,9 +21,14 @@ module FolderHelper |
17 | 21 | end |
18 | 22 | |
19 | 23 | def display_article_in_listing(article, recursive = false, level = 0) |
24 | + article_link = if article.image? | |
25 | + link_to(' ' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true)) | |
26 | + else | |
27 | + link_to(' ' * (level * 4) + short_filename(article.name), article.url.merge(:view => true), :class => icon_for_article(article)) | |
28 | + end | |
20 | 29 | result = content_tag( |
21 | 30 | 'tr', |
22 | - content_tag('td', link_to((' ' * (level * 4) ) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true)))+ | |
31 | + content_tag('td', article_link )+ | |
23 | 32 | content_tag('td', show_date(article.updated_at), :class => 'last-update'), |
24 | 33 | :class => 'sitemap-item' |
25 | 34 | ) |
... | ... | @@ -31,18 +40,22 @@ module FolderHelper |
31 | 40 | end |
32 | 41 | |
33 | 42 | def icon_for_article(article) |
34 | - icon = article.icon_name | |
43 | + icon = article.class.icon_name(article) | |
35 | 44 | if (icon =~ /\//) |
36 | 45 | icon |
37 | 46 | else |
38 | - if File.exists?(File.join(RAILS_ROOT, 'public', 'images', 'icons-mime', "#{icon}.png")) | |
39 | - "icons-mime/#{icon}.png" | |
40 | - else | |
41 | - "icons-mime/unknown.png" | |
47 | + klasses = 'icon icon-' + icon | |
48 | + if article.kind_of?(UploadedFile) | |
49 | + klasses += ' icon-upload-file' | |
42 | 50 | end |
51 | + klasses | |
43 | 52 | end |
44 | 53 | end |
45 | 54 | |
55 | + def icon_for_new_article(type) | |
56 | + "icon-new icon-new%s" % type.constantize.icon_name | |
57 | + end | |
58 | + | |
46 | 59 | def custom_options_for_article(article) |
47 | 60 | @article = article |
48 | 61 | content_tag('h4', _('Options')) + |
... | ... | @@ -69,11 +82,4 @@ module FolderHelper |
69 | 82 | _('Edit folder') |
70 | 83 | end |
71 | 84 | |
72 | - def short_filename(filename, limit_chars = 43) | |
73 | - return filename if filename.size <= limit_chars | |
74 | - extname = File.extname(filename) | |
75 | - basename = File.basename(filename,extname) | |
76 | - str_complement = '(...)' | |
77 | - return basename[0..(limit_chars - extname.size - str_complement.size - 1)] + str_complement + extname | |
78 | - end | |
79 | 85 | end | ... | ... |
app/helpers/forms_helper.rb
... | ... | @@ -41,8 +41,7 @@ module FormsHelper |
41 | 41 | the_class << ' ' << html_options[:class] |
42 | 42 | end |
43 | 43 | |
44 | - # FIXME: should be in stylesheet | |
45 | - bt_submit = submit_tag(label, html_options.merge(:style => 'height:28px; cursor:pointer', :class => the_class)) | |
44 | + bt_submit = submit_tag(label, html_options.merge(:class => the_class)) | |
46 | 45 | |
47 | 46 | bt_submit + bt_cancel |
48 | 47 | end |
... | ... | @@ -60,6 +59,10 @@ module FormsHelper |
60 | 59 | |
61 | 60 | state_id = 'state-' + FormsHelper.next_id_number |
62 | 61 | city_id = 'city-' + FormsHelper.next_id_number |
62 | + | |
63 | + if states.length < 1 | |
64 | + return | |
65 | + end | |
63 | 66 | |
64 | 67 | if simple |
65 | 68 | states = [State.new(:name => _('Select the State'))] + states |
... | ... | @@ -108,6 +111,18 @@ module FormsHelper |
108 | 111 | )) |
109 | 112 | end |
110 | 113 | |
114 | + def options_for_select_with_title(container, selected = nil) | |
115 | + container = container.to_a if Hash === container | |
116 | + | |
117 | + options_for_select = container.inject([]) do |options, element| | |
118 | + text, value = option_text_and_value(element) | |
119 | + selected_attribute = ' selected="selected"' if option_value_selected?(value, selected) | |
120 | + options << %(<option title="#{html_escape(text.to_s)}" value="#{html_escape(value.to_s)}"#{selected_attribute}>#{html_escape(text.to_s)}</option>) | |
121 | + end | |
122 | + | |
123 | + options_for_select.join("\n") | |
124 | + end | |
125 | + | |
111 | 126 | protected |
112 | 127 | def self.next_id_number |
113 | 128 | if defined? @@id_num | ... | ... |
... | ... | @@ -0,0 +1,51 @@ |
1 | +module ForumHelper | |
2 | + | |
3 | + def cms_label_for_new_children | |
4 | + _('New discussion topic') | |
5 | + end | |
6 | + | |
7 | + def cms_label_for_edit | |
8 | + _('Configure forum') | |
9 | + end | |
10 | + | |
11 | + def list_forum_posts(articles) | |
12 | + pagination = will_paginate(articles, { | |
13 | + :param_name => 'npage', | |
14 | + :prev_label => _('« Newer posts'), | |
15 | + :next_label => _('Older posts »') | |
16 | + }) | |
17 | + content = [content_tag('tr', | |
18 | + content_tag('th', _('Discussion topic')) + | |
19 | + content_tag('th', _('Posts')) + | |
20 | + content_tag('th', _('Last post')) | |
21 | + ) | |
22 | + ] | |
23 | + artic_len = articles.length | |
24 | + articles.each_with_index{ |art,i| | |
25 | + css_add = [ 'position-'+(i+1).to_s() ] | |
26 | + position = (i%2 == 0) ? 'odd-post' : 'even-post' | |
27 | + css_add << 'first' if i == 0 | |
28 | + css_add << 'last' if i == (artic_len-1) | |
29 | + css_add << 'not-published' if !art.published? | |
30 | + css_add << position | |
31 | + content << content_tag('tr', | |
32 | + content_tag('td', link_to(art.title, art.url)) + | |
33 | + content_tag('td', link_to(art.comments.count, art.url.merge(:anchor => 'comments_list'))) + | |
34 | + content_tag('td', last_topic_update(art)), | |
35 | + :class => 'forum-post ' + css_add.join(' '), | |
36 | + :id => "post-#{art.id}" | |
37 | + ) | |
38 | + } | |
39 | + content_tag('table', content) + (pagination or '') | |
40 | + end | |
41 | + | |
42 | + def last_topic_update(article) | |
43 | + info = article.info_from_last_update | |
44 | + if info[:author_url] | |
45 | + time_ago_as_sentence(info[:date]) + ' ' + _('ago') + ' ' + _('by') + ' ' + link_to(info[:author_name], info[:author_url]) | |
46 | + else | |
47 | + time_ago_as_sentence(info[:date]) + ' ' + _('ago') + ' ' + _('by') + ' ' + info[:author_name] | |
48 | + end | |
49 | + end | |
50 | + | |
51 | +end | ... | ... |
app/helpers/friends_helper.rb
1 | 1 | module FriendsHelper |
2 | 2 | |
3 | - def link_to_import(text, options = {}) | |
4 | - options.merge!({:action => 'invite', :import => 1, :wizard => true}) | |
5 | - link_to text, options | |
6 | - end | |
7 | - | |
8 | - def pagination_links(collection, options={}) | |
9 | - options = {:prev_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options) | |
10 | - will_paginate(collection, options) | |
11 | - end | |
12 | - | |
13 | 3 | end | ... | ... |
app/helpers/manage_products_helper.rb
1 | 1 | module ManageProductsHelper |
2 | + | |
3 | + def remote_function_to_update_categories_selection(container_id, options = {}) | |
4 | + remote_function({ | |
5 | + :update => container_id, | |
6 | + :url => { :action => "categories_for_selection" }, | |
7 | + :loading => "loading('hierarchy_navigation', '#{ _('loading…') }'); loading('#{container_id}', ' ')", | |
8 | + :complete => "loading_done('hierarchy_navigation'); loading_done('#{container_id}')" | |
9 | + }.merge(options)) | |
10 | + end | |
11 | + | |
12 | + def hierarchy_category_item(category, make_links, title = nil) | |
13 | + title ||= category.name | |
14 | + if make_links | |
15 | + link_to(title, '#', | |
16 | + :title => title, | |
17 | + :onclick => remote_function_to_update_categories_selection("categories_container_level#{ category.level + 1 }", | |
18 | + :with => "'category_id=#{ category.id }'" | |
19 | + ) | |
20 | + ) | |
21 | + else | |
22 | + title | |
23 | + end | |
24 | + end | |
25 | + | |
26 | + def hierarchy_category_navigation(current_category, options = {}) | |
27 | + hierarchy = [] | |
28 | + if current_category | |
29 | + count_chars = 0 | |
30 | + unless options[:hide_current_category] | |
31 | + hierarchy << current_category.name | |
32 | + count_chars += current_category.name.length | |
33 | + end | |
34 | + ancestors = current_category.ancestors | |
35 | + toplevel = ancestors.pop | |
36 | + if toplevel | |
37 | + count_chars += toplevel.name.length | |
38 | + end | |
39 | + ancestors.each do |category| | |
40 | + if count_chars > 55 | |
41 | + hierarchy << hierarchy_category_item(category, options[:make_links], '( … )') | |
42 | + break | |
43 | + else | |
44 | + hierarchy << hierarchy_category_item(category, options[:make_links]) | |
45 | + end | |
46 | + count_chars += category.name.length | |
47 | + end | |
48 | + if toplevel | |
49 | + hierarchy << hierarchy_category_item(toplevel, options[:make_links]) | |
50 | + end | |
51 | + end | |
52 | + hierarchy.reverse.join(options[:separator] || ' → ') | |
53 | + end | |
54 | + | |
55 | + def options_for_select_categories(categories, selected = nil) | |
56 | + categories.sort_by{|cat| cat.name.transliterate}.map do |category| | |
57 | + selected_attribute = selected.nil? ? '' : (category == selected ? "selected='selected'" : '') | |
58 | + "<option value='#{category.id}' title='#{category.name}' #{selected_attribute}>#{truncate(category.name, 33) + (category.leaf? ? '': ' »')}</option>" | |
59 | + end.join("\n") | |
60 | + end | |
61 | + | |
62 | + def build_selects_for_ancestors(ancestors, current_category) | |
63 | + current_ancestor = ancestors.shift | |
64 | + if current_ancestor.nil? | |
65 | + select_for_new_category(current_category.children, current_category.level + 1) | |
66 | + else | |
67 | + content_tag('div', | |
68 | + select_tag('category_id', | |
69 | + options_for_select_categories(current_ancestor.siblings + [current_ancestor], current_ancestor), | |
70 | + :size => 10, | |
71 | + :onchange => remote_function_to_update_categories_selection("categories_container_level#{ current_ancestor.level + 1 }", :with => "'category_id=' + this.value") | |
72 | + ) + | |
73 | + build_selects_for_ancestors(ancestors, current_category), | |
74 | + :class => 'categories_container', | |
75 | + :id => "categories_container_level#{ current_ancestor.level }" | |
76 | + ) | |
77 | + end | |
78 | + end | |
79 | + | |
80 | + def selects_for_all_ancestors(current_category) | |
81 | + build_selects_for_ancestors(current_category.ancestors.reverse + [current_category], current_category) | |
82 | + end | |
83 | + | |
84 | + def select_for_new_category(categories, level) | |
85 | + content_tag('div', | |
86 | + render(:partial => 'categories_for_selection', :locals => { :categories => categories, :level => level }), | |
87 | + :class => 'categories_container', | |
88 | + :id => "categories_container_level#{ level }" | |
89 | + ) | |
90 | + end | |
91 | + | |
92 | + def categories_container(categories_selection_html, hierarchy_html = '') | |
93 | + hidden_field_tag('selected_category_id') + | |
94 | + content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') + | |
95 | + content_tag('div', categories_selection_html, :id => 'categories_container_wrapper') | |
96 | + end | |
97 | + | |
98 | + def select_for_categories(categories, level = 0) | |
99 | + if categories.empty? | |
100 | + content_tag('div', '', :id => 'no_subcategories') | |
101 | + else | |
102 | + select_tag('category_id', | |
103 | + options_for_select_categories(categories), | |
104 | + :size => 10, | |
105 | + :onchange => remote_function_to_update_categories_selection("categories_container_level#{ level + 1 }", :with => "'category_id=' + this.value") | |
106 | + ) + | |
107 | + content_tag('div', '', :class => 'categories_container', :id => "categories_container_level#{ level + 1 }") | |
108 | + end | |
109 | + end | |
110 | + | |
111 | + def edit_link(label, url, html_options = {}) | |
112 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
113 | + link_to(label, url, html_options) | |
114 | + end | |
115 | + | |
116 | + def edit_product_link_to_remote(product, field, label, html_options = {}) | |
117 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
118 | + options = html_options.merge(:id => 'link-edit-product-' + field) | |
119 | + options[:class] = options[:class] ? options[:class] + ' link-to-remote' : 'link-to-remote' | |
120 | + | |
121 | + link_to_remote(label, | |
122 | + {:update => "product-#{field}", | |
123 | + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field }, | |
124 | + :method => :get, | |
125 | + :loading => "loading_for_button('#link-edit-product-#{field}')"}, | |
126 | + options) | |
127 | + end | |
128 | + | |
129 | + def edit_button(type, label, url, html_options = {}) | |
130 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
131 | + button(type, label, url, html_options) | |
132 | + end | |
133 | + | |
134 | + def edit_product_button_to_remote(product, field, label, html_options = {}) | |
135 | + the_class = 'button with-text icon-edit' | |
136 | + if html_options.has_key?(:class) | |
137 | + the_class << ' ' << html_options[:class] | |
138 | + end | |
139 | + edit_product_link_to_remote(product, field, label, html_options.merge(:class => the_class)) | |
140 | + end | |
141 | + | |
142 | + def edit_ui_button(label, url, html_options = {}) | |
143 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
144 | + ui_button(label, url, html_options) | |
145 | + end | |
146 | + | |
147 | + def edit_product_ui_button_to_remote(product, field, label, html_options = {}) | |
148 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
149 | + id = 'edit-product-remote-button-ui-' + field | |
150 | + options = html_options.merge(:id => id) | |
151 | + | |
152 | + ui_button_to_remote(label, | |
153 | + {:update => "product-#{field}", | |
154 | + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field }, | |
155 | + :complete => "$('edit-product-button-ui-#{field}').hide()", | |
156 | + :method => :get, | |
157 | + :loading => "loading_for_button('##{id}')"}, | |
158 | + options) | |
159 | + end | |
160 | + | |
161 | + def cancel_edit_product_link(product, field, html_options = {}) | |
162 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
163 | + button_to_function(:cancel, _('Cancel'), nil, html_options) do |page| | |
164 | + page.replace_html "product-#{field}", :partial => "display_#{field}", :locals => {:product => product} | |
165 | + end | |
166 | + end | |
167 | + | |
168 | + def edit_product_category_link(product, html_options = {}) | |
169 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
170 | + options = html_options.merge(:id => 'link-edit-product-category') | |
171 | + link_to(_('Change category'), { :action => 'edit_category', :id => product.id}, options) | |
172 | + end | |
173 | + | |
174 | + def display_value(product) | |
175 | + price = product.price | |
176 | + return '' if price.blank? || price.zero? | |
177 | + discount = product.discount | |
178 | + if discount.blank? || discount.zero? | |
179 | + result = display_price(_('Price: '), price) | |
180 | + else | |
181 | + result = display_price_with_discount(price, product.price_with_discount) | |
182 | + end | |
183 | + content_tag('span', content_tag('span', result, :class => 'product-price'), :class => "#{product.available? ? '' : 'un'}available-product") | |
184 | + end | |
185 | + | |
186 | + def display_availability(product) | |
187 | + if !product.available? | |
188 | + ui_highlight(_('Product not available!')) | |
189 | + end | |
190 | + end | |
191 | + | |
192 | + def display_price(label, price) | |
193 | + content_tag('span', label, :class => 'field-name') + | |
194 | + content_tag('span', float_to_currency(price), :class => 'field-value') | |
195 | + end | |
196 | + | |
197 | + def display_price_with_discount(price, price_with_discount) | |
198 | + original_value = content_tag('span', display_price(_('List price: '), price), :class => 'list-price') | |
199 | + discount_value = content_tag('span', display_price(_('On sale: '), price_with_discount), :class => 'on-sale-price') | |
200 | + original_value + tag('br') + discount_value | |
201 | + end | |
202 | + | |
203 | + def display_qualifiers(product) | |
204 | + data = '' | |
205 | + product.product_qualifiers.each do |pq| | |
206 | + certified_by = '' | |
207 | + certifier = pq.certifier | |
208 | + if certifier | |
209 | + certifier_name = certifier.link.blank? ? certifier.name : link_to(certifier.name, certifier.link) | |
210 | + certified_by = _('certified by %s') % certifier_name | |
211 | + else | |
212 | + certified_by = _('(Self declared)') | |
213 | + end | |
214 | + data << content_tag('li', "✔ #{pq.qualifier.name} #{certified_by}", :class => 'product-qualifiers-item') | |
215 | + end | |
216 | + content_tag('ul', data, :id => 'product-qualifiers') | |
217 | + end | |
218 | + | |
219 | + def qualifiers_for_select | |
220 | + [[_('Select...'), nil]] + environment.qualifiers.map{ |c| [c.name, c.id] } | |
221 | + end | |
222 | + def certifiers_for_select(qualifier) | |
223 | + [[_('Self declared'), nil]] + qualifier.certifiers.map{ |c| [c.name, c.id] } | |
224 | + end | |
225 | + def select_qualifiers(product, selected = nil) | |
226 | + select_tag('selected_qualifier', options_for_select(qualifiers_for_select, selected), | |
227 | + :onchange => remote_function( | |
228 | + :url => {:action => 'certifiers_for_selection'}, | |
229 | + :with => "'id=' + value + '&certifier_area=' + jQuery(this).parent().next().attr('id')", | |
230 | + :before => "small_loading(jQuery(this).parent().next().attr('id'), ' ')" | |
231 | + ), | |
232 | + :id => nil | |
233 | + ) | |
234 | + end | |
235 | + def select_certifiers(qualifier, product = nil) | |
236 | + if qualifier | |
237 | + selected = product ? product.product_qualifiers.find_by_qualifier_id(qualifier.id).certifier_id : nil | |
238 | + select_tag("product[qualifiers_list][#{qualifier.id}]", options_for_select(certifiers_for_select(qualifier), selected)) | |
239 | + else | |
240 | + select_tag("product[qualifiers_list][nil]") | |
241 | + end | |
242 | + end | |
243 | + | |
244 | + def select_unit(object) | |
245 | + selected = object.unit.nil? ? '' : object.unit | |
246 | + select(object.class.name.downcase, 'unit', Product::UNITS.map{|unit| [_(unit[0]), unit[0]]}, {:selected => selected, :include_blank => _('Select the unit')}) | |
247 | + end | |
248 | + | |
249 | + def input_icon(input) | |
250 | + if input.is_from_solidarity_economy? | |
251 | + hint = _('Product from solidarity economy') | |
252 | + image_tag("/images/solidarity-economy.png", :class => 'solidatiry-economy-icon', :alt => hint, :title => hint) | |
253 | + end | |
254 | + end | |
255 | + | |
256 | + def display_price_by(unit) | |
257 | + selected_unit = content_tag('span', unit, :class => 'selected-unit') | |
258 | + content_tag('span', _('by') + ' ' + selected_unit, :class => 'price-by-unit') | |
259 | + end | |
260 | + | |
261 | + def label_amount_used(input) | |
262 | + product_unit = input.product.unit | |
263 | + if product_unit.blank? | |
264 | + _('Amount used in this product or service') | |
265 | + else | |
266 | + _('Amount used by %s of this product or service') % _(product_unit) | |
267 | + end | |
268 | + end | |
269 | + | |
270 | + def display_unit(input) | |
271 | + input_amount_used = content_tag('span', input.formatted_amount, :class => 'input-amount-used') | |
272 | + return input_amount_used if input.unit.blank? | |
273 | + units = Product::UNITS.find {|unit| unit[0] == input.unit} | |
274 | + n_('1 %{singular_unit}', '%{num} %{plural_unit}', input.amount_used.to_f) % { :num => input_amount_used, :singular_unit => content_tag('span', units[0], :class => 'input-unit'), :plural_unit => content_tag('span', units[1], :class => 'input-unit') } | |
275 | + end | |
2 | 276 | end | ... | ... |
app/helpers/profile_helper.rb
... | ... | @@ -15,9 +15,12 @@ module ProfileHelper |
15 | 15 | end |
16 | 16 | end |
17 | 17 | |
18 | - def pagination_links(collection, options={}) | |
19 | - options = {:prev_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options) | |
20 | - will_paginate(collection, options) | |
21 | - end | |
18 | + def render_tabs(tabs) | |
19 | + titles = tabs.inject(''){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') } | |
20 | + contents = tabs.inject(''){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) } | |
22 | 21 | |
22 | + content_tag :div, :class => 'ui-tabs' do | |
23 | + content_tag(:ul, titles) + contents | |
24 | + end | |
25 | + end | |
23 | 26 | end | ... | ... |
app/helpers/search_helper.rb
... | ... | @@ -98,11 +98,6 @@ module SearchHelper |
98 | 98 | ), :class => 'profile-info') |
99 | 99 | end |
100 | 100 | |
101 | - def pagination_links(collection, options={}) | |
102 | - options = {:prev_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options) | |
103 | - will_paginate(collection, options) | |
104 | - end | |
105 | - | |
106 | 101 | def product_categories_menu(asset, product_category, object_ids = nil) |
107 | 102 | cats = ProductCategory.menu_categories(@product_category, environment) |
108 | 103 | cats += cats.select { |c| c.children_count > 0 }.map(&:children).flatten | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +class ActionTrackerNotification < ActiveRecord::Base | |
2 | + | |
3 | + belongs_to :profile | |
4 | + belongs_to :action_tracker, :class_name => 'ActionTracker::Record', :foreign_key => 'action_tracker_id' | |
5 | + | |
6 | + validates_presence_of :profile_id, :action_tracker_id | |
7 | + validates_uniqueness_of :action_tracker_id, :scope => :profile_id | |
8 | + | |
9 | +end | |
10 | + | |
11 | +ActionTracker::Record.has_many :action_tracker_notifications, :class_name => 'ActionTrackerNotification', :foreign_key => 'action_tracker_id', :dependent => :destroy | ... | ... |
app/models/add_friend.rb
1 | 1 | class AddFriend < Task |
2 | 2 | |
3 | - acts_as_having_settings :group_for_person, :group_for_friend, :field => :data | |
3 | + settings_items :group_for_person, :group_for_friend | |
4 | 4 | |
5 | 5 | validates_presence_of :requestor_id, :target_id |
6 | 6 | |
7 | 7 | validates_uniqueness_of :target_id, :scope => [ :requestor_id ] |
8 | 8 | |
9 | - validates_length_of :group_for_person, :group_for_friend, :maximum => 150, :allow_nil => true | |
9 | + validates_length_of :group_for_person, :group_for_friend, :maximum => 150, :allow_nil => true | |
10 | 10 | |
11 | 11 | alias :person :requestor |
12 | 12 | alias :person= :requestor= |
... | ... | @@ -15,21 +15,37 @@ class AddFriend < Task |
15 | 15 | alias :friend= :target= |
16 | 16 | |
17 | 17 | def perform |
18 | - requestor.add_friend(target, group_for_person) | |
19 | 18 | target.add_friend(requestor, group_for_friend) |
20 | - end | |
21 | - | |
22 | - def description | |
23 | - _('%s wants to be your friend.') % requestor.name | |
19 | + requestor.add_friend(target, group_for_person) | |
24 | 20 | end |
25 | 21 | |
26 | 22 | def permission |
27 | 23 | :manage_friends |
28 | 24 | end |
29 | 25 | |
26 | + def target_notification_description | |
27 | + _('%{requestor} wants to be your friend.') % {:requestor => requestor.name} | |
28 | + end | |
29 | + | |
30 | 30 | def target_notification_message |
31 | - description + "\n\n" + | |
31 | + target_notification_description + "\n\n" + | |
32 | 32 | _('You need to login to %{system} in order to accept %{requestor} as your friend.') % { :system => target.environment.name, :requestor => requestor.name } |
33 | 33 | end |
34 | 34 | |
35 | + def title | |
36 | + _("New friend") | |
37 | + end | |
38 | + | |
39 | + def information | |
40 | + {:message => _('%{requestor} wants to be your friend.') } | |
41 | + end | |
42 | + | |
43 | + def accept_details | |
44 | + true | |
45 | + end | |
46 | + | |
47 | + def icon | |
48 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
49 | + end | |
50 | + | |
35 | 51 | end | ... | ... |
app/models/add_member.rb
... | ... | @@ -8,23 +8,39 @@ class AddMember < Task |
8 | 8 | alias :organization :target |
9 | 9 | alias :organization= :target= |
10 | 10 | |
11 | - acts_as_having_settings :roles, :field => :data | |
11 | + settings_items :roles | |
12 | 12 | |
13 | 13 | def perform |
14 | 14 | self.roles ||= [Profile::Roles.member(organization.environment.id).id] |
15 | 15 | target.affiliate(requestor, self.roles.select{|r| !r.to_i.zero? }.map{|i| Role.find(i)}) |
16 | 16 | end |
17 | 17 | |
18 | - def description | |
19 | - _('%s wants to be a member of "%s".') % [requestor.name, organization.name] | |
18 | + def title | |
19 | + _("New member") | |
20 | + end | |
21 | + | |
22 | + def information | |
23 | + {:message => _('%{requestor} wants to be a member of this community.')} | |
24 | + end | |
25 | + | |
26 | + def accept_details | |
27 | + true | |
28 | + end | |
29 | + | |
30 | + def icon | |
31 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
20 | 32 | end |
21 | 33 | |
22 | 34 | def permission |
23 | 35 | :manage_memberships |
24 | 36 | end |
25 | 37 | |
38 | + def target_notification_description | |
39 | + _('%{requestor} wants to be a member of this community.') % {:requestor => requestor.name} | |
40 | + end | |
41 | + | |
26 | 42 | def target_notification_message |
27 | - description + "\n\n" + | |
43 | + target_notification_description + "\n\n" + | |
28 | 44 | _('You will need login to %{system} in order to accept or reject %{requestor} as a member of %{organization}.') % { :system => target.environment.name, :requestor => requestor.name, :organization => organization.name } |
29 | 45 | end |
30 | 46 | ... | ... |
app/models/approve_article.rb
1 | 1 | class ApproveArticle < Task |
2 | - serialize :data, Hash | |
3 | - | |
4 | 2 | validates_presence_of :requestor_id, :target_id |
5 | 3 | |
6 | - def description | |
7 | - _('%{author} wants to publish "%{article}" on %{community}') % { :author => requestor.name, :article => article_title, :community => target.name } | |
8 | - end | |
9 | - | |
10 | 4 | def article_title |
11 | 5 | article ? article.title : _('(The original text was removed)') |
12 | 6 | end |
13 | 7 | |
14 | - def data | |
15 | - self[:data] ||= {} | |
16 | - end | |
17 | - | |
18 | 8 | def article |
19 | 9 | Article.find_by_id data[:article_id] |
20 | 10 | end |
... | ... | @@ -24,53 +14,97 @@ class ApproveArticle < Task |
24 | 14 | end |
25 | 15 | |
26 | 16 | def name |
27 | - data[:name] | |
17 | + data[:name].blank? ? (article ? article.name : _("Article removed.")) : data[:name] | |
28 | 18 | end |
29 | 19 | |
30 | 20 | def name= value |
31 | 21 | data[:name] = value |
32 | 22 | end |
33 | 23 | |
34 | - def closing_statment | |
35 | - data[:closing_statment] | |
24 | + settings_items :closing_statment, :article_parent_id, :highlighted | |
25 | + | |
26 | + def article_parent | |
27 | + Article.find_by_id article_parent_id.to_i | |
36 | 28 | end |
37 | - | |
38 | - def closing_statment= value | |
39 | - data[:closing_statment] = value | |
29 | + | |
30 | + def article_parent= value | |
31 | + self.article_parent_id = value.id | |
40 | 32 | end |
41 | 33 | |
42 | - def article_parent_id= value | |
43 | - data[:parent_id] = value | |
34 | + def abstract= value | |
35 | + data[:abstract] = value | |
44 | 36 | end |
45 | 37 | |
46 | - def article_parent_id | |
47 | - data[:parent_id] | |
38 | + def abstract | |
39 | + data[:abstract].blank? ? (article ? article.abstract : '') : data[:abstract] | |
48 | 40 | end |
49 | 41 | |
50 | - def article_parent | |
51 | - Article.find_by_id article_parent_id.to_i | |
42 | + def body= value | |
43 | + data[:body] = value | |
52 | 44 | end |
53 | 45 | |
54 | - def article_parent= value | |
55 | - self.article_parent_id = value.id | |
46 | + def body | |
47 | + data[:body].blank? ? (article ? article.body : "") : data[:body] | |
56 | 48 | end |
57 | 49 | |
58 | - def highlighted= value | |
59 | - data[:highlighted] = value | |
50 | + def perform | |
51 | + article.copy!(:name => name, :abstract => abstract, :body => body, :profile => target, :reference_article => article, :parent => article_parent, :highlighted => highlighted, :source => article.source) | |
60 | 52 | end |
61 | 53 | |
62 | - def highlighted | |
63 | - data[:highlighted] | |
54 | + def title | |
55 | + _("New article") | |
64 | 56 | end |
65 | 57 | |
66 | - def perform | |
67 | - PublishedArticle.create!(:name => name, :profile => target, :reference_article => article, :parent => article_parent, :highlighted => highlighted) | |
58 | + def icon | |
59 | + result = {:type => :defined_image, :src => '/images/icons-app/article-minor.png', :name => name} | |
60 | + result.merge({:url => article.url}) if article | |
61 | + return result | |
62 | + end | |
63 | + | |
64 | + def linked_subject | |
65 | + {:text => name, :url => article.url} if article | |
66 | + end | |
67 | + | |
68 | + def information | |
69 | + if article | |
70 | + {:message => _('%{requestor} wants to publish the article: %{linked_subject}.')} | |
71 | + else | |
72 | + {:message => _("The article was removed.")} | |
73 | + end | |
74 | + end | |
75 | + | |
76 | + def accept_details | |
77 | + true | |
78 | + end | |
79 | + | |
80 | + def default_decision | |
81 | + if article | |
82 | + 'skip' | |
83 | + else | |
84 | + 'reject' | |
85 | + end | |
86 | + end | |
87 | + | |
88 | + def accept_disabled? | |
89 | + article.blank? | |
90 | + end | |
91 | + | |
92 | + def target_notification_description | |
93 | + _('%{requestor} wants to publish the article: %{article}.') % {:requestor => requestor.name, :article => article.name} | |
68 | 94 | end |
69 | 95 | |
70 | 96 | def target_notification_message |
71 | 97 | return nil if target.organization? && !target.moderated_articles? |
72 | - description + "\n\n" + | |
98 | + target_notification_description + "\n\n" + | |
73 | 99 | _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name } |
74 | 100 | end |
75 | 101 | |
102 | + def task_finished_message | |
103 | + if !closing_statment.blank? | |
104 | + _("Your request for publishing the article \"%{article}\" was approved. Here is the comment left by the admin who approved your article:\n\n%{comment} ") % {:article => name, :comment => closing_statment} | |
105 | + else | |
106 | + _('Your request for publishing the article "%{article}" was approved.') % {:article => name} | |
107 | + end | |
108 | + end | |
109 | + | |
76 | 110 | end | ... | ... |
app/models/article.rb
1 | +require 'hpricot' | |
2 | + | |
1 | 3 | class Article < ActiveRecord::Base |
2 | 4 | |
5 | + track_actions :create_article, :after_create, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && !a.image? }, :custom_target => :action_tracker_target | |
6 | + track_actions :update_article, :before_update, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && (a.body_changed? || a.name_changed?) }, :custom_target => :action_tracker_target | |
7 | + track_actions :remove_article, :before_destroy, :keep_params => [:name], :if => Proc.new { |a| a.is_trackable? }, :custom_target => :action_tracker_target | |
8 | + | |
3 | 9 | # xss_terminate plugin can't sanitize array fields |
4 | 10 | before_save :sanitize_tag_list |
5 | 11 | |
... | ... | @@ -19,14 +25,25 @@ class Article < ActiveRecord::Base |
19 | 25 | acts_as_having_settings :field => :setting |
20 | 26 | |
21 | 27 | settings_items :display_hits, :type => :boolean, :default => true |
28 | + settings_items :author_name, :type => :string, :default => "" | |
22 | 29 | |
23 | 30 | belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id' |
24 | 31 | |
32 | + has_many :translations, :class_name => 'Article', :foreign_key => :translation_of_id | |
33 | + belongs_to :translation_of, :class_name => 'Article', :foreign_key => :translation_of_id | |
34 | + before_destroy :rotate_translations | |
35 | + | |
25 | 36 | before_create do |article| |
26 | 37 | article.published_at = article.created_at if article.published_at.nil? |
38 | + if article.reference_article && !article.parent | |
39 | + parent = article.reference_article.parent | |
40 | + if parent && parent.blog? && article.profile.has_blog? | |
41 | + article.parent = article.profile.blog | |
42 | + end | |
43 | + end | |
27 | 44 | end |
28 | 45 | |
29 | - xss_terminate :only => [ :name ], :on => 'validation' | |
46 | + xss_terminate :only => [ :name ], :on => 'validation', :with => 'white_list' | |
30 | 47 | |
31 | 48 | named_scope :in_category, lambda { |category| |
32 | 49 | {:include => 'categories', :conditions => { 'categories.id' => category.id }} |
... | ... | @@ -38,20 +55,17 @@ class Article < ActiveRecord::Base |
38 | 55 | ] |
39 | 56 | }} |
40 | 57 | |
41 | - def self.first_day_of_month(date) | |
42 | - date ||= Date.today | |
43 | - Date.new(date.year, date.month, 1) | |
44 | - end | |
45 | - | |
46 | - def self.last_day_of_month(date) | |
47 | - date ||= Date.today | |
48 | - date >>= 1 | |
49 | - Date.new(date.year, date.month, 1) - 1.day | |
50 | - end | |
51 | - | |
52 | 58 | URL_FORMAT = /\A(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\Z/ix |
53 | 59 | |
54 | 60 | validates_format_of :external_link, :with => URL_FORMAT, :if => lambda { |article| !article.external_link.blank? } |
61 | + validate :known_language | |
62 | + validate :used_translation | |
63 | + validate :native_translation_must_have_language | |
64 | + validate :translation_must_have_language | |
65 | + | |
66 | + def is_trackable? | |
67 | + self.published? && self.notifiable? && self.advertise? | |
68 | + end | |
55 | 69 | |
56 | 70 | def external_link=(link) |
57 | 71 | if !link.blank? && link !~ /^[a-z]+:\/\//i |
... | ... | @@ -60,6 +74,9 @@ class Article < ActiveRecord::Base |
60 | 74 | self[:external_link] = link |
61 | 75 | end |
62 | 76 | |
77 | + def action_tracker_target | |
78 | + self.profile | |
79 | + end | |
63 | 80 | |
64 | 81 | def self.human_attribute_name(attrib) |
65 | 82 | case attrib.to_sym |
... | ... | @@ -120,9 +137,9 @@ class Article < ActiveRecord::Base |
120 | 137 | |
121 | 138 | # retrieves all articles belonging to the given +profile+ that are not |
122 | 139 | # sub-articles of any other article. |
123 | - def self.top_level_for(profile) | |
124 | - self.find(:all, :conditions => [ 'parent_id is null and profile_id = ?', profile.id ]) | |
125 | - end | |
140 | + named_scope :top_level_for, lambda { |profile| | |
141 | + {:conditions => [ 'parent_id is null and profile_id = ?', profile.id ]} | |
142 | + } | |
126 | 143 | |
127 | 144 | # retrieves the latest +limit+ articles, sorted from the most recent to the |
128 | 145 | # oldest. |
... | ... | @@ -182,7 +199,7 @@ class Article < ActiveRecord::Base |
182 | 199 | # to return their specific icons. |
183 | 200 | # |
184 | 201 | # FIXME use mime_type and generate this name dinamically |
185 | - def icon_name | |
202 | + def self.icon_name(article = nil) | |
186 | 203 | 'text-html' |
187 | 204 | end |
188 | 205 | |
... | ... | @@ -206,10 +223,24 @@ class Article < ActiveRecord::Base |
206 | 223 | name |
207 | 224 | end |
208 | 225 | |
226 | + include ActionView::Helpers::TextHelper | |
227 | + def short_title | |
228 | + truncate self.title, 15, '...' | |
229 | + end | |
230 | + | |
209 | 231 | def belongs_to_blog? |
210 | 232 | self.parent and self.parent.blog? |
211 | 233 | end |
212 | 234 | |
235 | + def info_from_last_update | |
236 | + last_comment = comments.last | |
237 | + if last_comment | |
238 | + {:date => last_comment.created_at, :author_name => last_comment.author_name, :author_url => last_comment.author_url} | |
239 | + else | |
240 | + {:date => updated_at, :author_name => author.name, :author_url => author.url} | |
241 | + end | |
242 | + end | |
243 | + | |
213 | 244 | def url |
214 | 245 | @url ||= self.profile.url.merge(:page => path.split('/')) |
215 | 246 | end |
... | ... | @@ -230,6 +261,73 @@ class Article < ActiveRecord::Base |
230 | 261 | false |
231 | 262 | end |
232 | 263 | |
264 | + def forum? | |
265 | + false | |
266 | + end | |
267 | + | |
268 | + def has_posts? | |
269 | + false | |
270 | + end | |
271 | + | |
272 | + named_scope :native_translations, :conditions => { :translation_of_id => nil } | |
273 | + | |
274 | + def translatable? | |
275 | + false | |
276 | + end | |
277 | + | |
278 | + def native_translation | |
279 | + self.translation_of.nil? ? self : self.translation_of | |
280 | + end | |
281 | + | |
282 | + def possible_translations | |
283 | + possibilities = Noosfero.locales.keys - self.native_translation.translations(:select => :language).map(&:language) - [self.native_translation.language] | |
284 | + possibilities << self.language unless self.language_changed? | |
285 | + possibilities | |
286 | + end | |
287 | + | |
288 | + def known_language | |
289 | + unless self.language.blank? | |
290 | + errors.add(:language, N_('Language not supported by Noosfero')) unless Noosfero.locales.key?(self.language) | |
291 | + end | |
292 | + end | |
293 | + | |
294 | + def used_translation | |
295 | + unless self.language.blank? or self.translation_of.nil? | |
296 | + errors.add(:language, N_('Language is already used')) unless self.possible_translations.include?(self.language) | |
297 | + end | |
298 | + end | |
299 | + | |
300 | + def translation_must_have_language | |
301 | + unless self.translation_of.nil? | |
302 | + errors.add(:language, N_('Language must be choosen')) if self.language.blank? | |
303 | + end | |
304 | + end | |
305 | + | |
306 | + def native_translation_must_have_language | |
307 | + unless self.translation_of.nil? | |
308 | + errors.add_to_base(N_('A language must be choosen for the native article')) if self.translation_of.language.blank? | |
309 | + end | |
310 | + end | |
311 | + | |
312 | + def rotate_translations | |
313 | + unless self.translations.empty? | |
314 | + rotate = self.translations | |
315 | + root = rotate.shift | |
316 | + root.update_attribute(:translation_of_id, nil) | |
317 | + root.translations = rotate | |
318 | + end | |
319 | + end | |
320 | + | |
321 | + def get_translation_to(locale) | |
322 | + if self.language.nil? || self.language == locale | |
323 | + self | |
324 | + elsif self.native_translation.language == locale | |
325 | + self.native_translation | |
326 | + else | |
327 | + self.native_translation.translations.first(:conditions => { :language => locale }) || self | |
328 | + end | |
329 | + end | |
330 | + | |
233 | 331 | def published? |
234 | 332 | if self.published |
235 | 333 | if self.parent && !self.parent.published? |
... | ... | @@ -241,22 +339,33 @@ class Article < ActiveRecord::Base |
241 | 339 | end |
242 | 340 | end |
243 | 341 | |
244 | - named_scope :published, :conditions => { :published => true } | |
245 | - named_scope :folders, :conditions => { :type => ['Folder', 'Blog'] } | |
342 | + named_scope :published, :conditions => { :published => true } | |
343 | + named_scope :folders, :conditions => { :type => ['Folder', 'Blog', 'Forum', 'Gallery'] } | |
344 | + named_scope :galleries, :conditions => { :type => 'Gallery' } | |
345 | + named_scope :images, :conditions => { :is_image => true } | |
346 | + | |
347 | + def self.display_filter(user, profile) | |
348 | + return {:conditions => ['published = ?', true]} if !user | |
349 | + {:conditions => [" articles.published = ? OR | |
350 | + articles.last_changed_by_id = ? OR | |
351 | + articles.profile_id = ? OR | |
352 | + ?", | |
353 | + true, user.id, user.id, user.has_permission?(:view_private_content, profile)] } | |
354 | + end | |
246 | 355 | |
247 | 356 | def display_unpublished_article_to?(user) |
248 | - self.author == user || allow_view_private_content?(user) || user == self.profile || | |
249 | - user.is_admin?(self.profile.environment) || user.is_admin?(self.profile) | |
357 | + user == author || allow_view_private_content?(user) || user == profile || | |
358 | + user.is_admin?(profile.environment) || user.is_admin?(profile) | |
250 | 359 | end |
251 | 360 | |
252 | - def display_to?(user) | |
253 | - if self.published? | |
254 | - self.profile.display_info_to?(user) | |
361 | + def display_to?(user = nil) | |
362 | + if published? | |
363 | + profile.display_info_to?(user) | |
255 | 364 | else |
256 | - if user.nil? | |
365 | + if !user | |
257 | 366 | false |
258 | 367 | else |
259 | - self.display_unpublished_article_to?(user) | |
368 | + display_unpublished_article_to?(user) | |
260 | 369 | end |
261 | 370 | end |
262 | 371 | end |
... | ... | @@ -286,12 +395,18 @@ class Article < ActiveRecord::Base |
286 | 395 | end |
287 | 396 | |
288 | 397 | |
289 | - def copy(options) | |
398 | + def copy(options = {}) | |
290 | 399 | attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) } |
291 | 400 | attrs.merge!(options) |
292 | 401 | self.class.create(attrs) |
293 | 402 | end |
294 | 403 | |
404 | + def copy!(options = {}) | |
405 | + attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) } | |
406 | + attrs.merge!(options) | |
407 | + self.class.create!(attrs) | |
408 | + end | |
409 | + | |
295 | 410 | ATTRIBUTES_NOT_COPIED = [ |
296 | 411 | :id, |
297 | 412 | :profile_id, |
... | ... | @@ -329,13 +444,28 @@ class Article < ActiveRecord::Base |
329 | 444 | false |
330 | 445 | end |
331 | 446 | |
332 | - def display_as_gallery? | |
447 | + def event? | |
448 | + false | |
449 | + end | |
450 | + | |
451 | + def gallery? | |
452 | + false | |
453 | + end | |
454 | + | |
455 | + def tiny_mce? | |
333 | 456 | false |
334 | 457 | end |
335 | 458 | |
336 | 459 | def author |
337 | - last_changed_by || | |
338 | - profile | |
460 | + if reference_article | |
461 | + reference_article.author | |
462 | + else | |
463 | + last_changed_by || profile | |
464 | + end | |
465 | + end | |
466 | + | |
467 | + def author_name | |
468 | + setting[:author_name].blank? ? author.name : setting[:author_name] | |
339 | 469 | end |
340 | 470 | |
341 | 471 | alias :active_record_cache_key :cache_key |
... | ... | @@ -348,8 +478,16 @@ class Article < ActiveRecord::Base |
348 | 478 | end |
349 | 479 | |
350 | 480 | def first_paragraph |
351 | - to_html =~ /(.*<\/p>)/ | |
352 | - $1 || '' | |
481 | + paragraphs = Hpricot(to_html).search('p') | |
482 | + paragraphs.empty? ? '' : paragraphs.first.to_html | |
483 | + end | |
484 | + | |
485 | + def lead | |
486 | + abstract.blank? ? first_paragraph : abstract | |
487 | + end | |
488 | + | |
489 | + def short_lead | |
490 | + truncate sanitize_html(self.lead), 170, '...' | |
353 | 491 | end |
354 | 492 | |
355 | 493 | def creator |
... | ... | @@ -357,6 +495,10 @@ class Article < ActiveRecord::Base |
357 | 495 | creator_id && Profile.find(creator_id) |
358 | 496 | end |
359 | 497 | |
498 | + def notifiable? | |
499 | + false | |
500 | + end | |
501 | + | |
360 | 502 | private |
361 | 503 | |
362 | 504 | def sanitize_tag_list |
... | ... | @@ -368,4 +510,9 @@ class Article < ActiveRecord::Base |
368 | 510 | tag_name.gsub(/[<>]/, '') |
369 | 511 | end |
370 | 512 | |
513 | + def sanitize_html(text) | |
514 | + sanitizer = HTML::FullSanitizer.new | |
515 | + sanitizer.sanitize(text) | |
516 | + end | |
517 | + | |
371 | 518 | end | ... | ... |
app/models/block.rb
... | ... | @@ -19,12 +19,22 @@ class Block < ActiveRecord::Base |
19 | 19 | # may contain the following keys: |
20 | 20 | # |
21 | 21 | # * <tt>:article</tt>: the article being viewed currently |
22 | + # * <tt>:language</tt>: in which language the block will be displayed | |
22 | 23 | def visible?(context = nil) |
23 | 24 | if display == 'never' |
24 | 25 | return false |
25 | 26 | end |
26 | - if context && context[:article] && display == 'home_page_only' | |
27 | - return context[:article] == owner.home_page | |
27 | + if context | |
28 | + if language != 'all' && language != context[:locale] | |
29 | + return false | |
30 | + end | |
31 | + if display == 'home_page_only' | |
32 | + if context[:article] | |
33 | + return context[:article] == owner.home_page | |
34 | + else | |
35 | + return context[:request_path] == '/' | |
36 | + end | |
37 | + end | |
28 | 38 | end |
29 | 39 | true |
30 | 40 | end |
... | ... | @@ -37,6 +47,11 @@ class Block < ActiveRecord::Base |
37 | 47 | # homepage of its owner. |
38 | 48 | settings_items :display, :type => :string, :default => 'always' |
39 | 49 | |
50 | + # The block can be configured to be displayed in all languages or in just one language. It can assume any locale of the environment: | |
51 | + # | |
52 | + # * <tt>'all'</tt>: the block is always displayed | |
53 | + settings_items :language, :type => :string, :default => 'all' | |
54 | + | |
40 | 55 | # returns the description of the block, used when the user sees a list of |
41 | 56 | # blocks to choose one to include in the design. |
42 | 57 | # | ... | ... |
app/models/blog.rb
1 | 1 | class Blog < Folder |
2 | 2 | |
3 | - has_many :posts, :class_name => 'Article', :foreign_key => 'parent_id', :source => :children, :conditions => [ 'type != ?', 'RssFeed' ], :order => 'published_at DESC, id DESC' | |
4 | - | |
5 | - attr_accessor :feed_attrs | |
6 | - | |
7 | - after_create do |blog| | |
8 | - blog.children << RssFeed.new(:name => 'feed', :profile => blog.profile) | |
9 | - blog.feed = blog.feed_attrs | |
10 | - end | |
11 | - | |
12 | - settings_items :posts_per_page, :type => :integer, :default => 5 | |
3 | + acts_as_having_posts | |
13 | 4 | |
14 | 5 | def self.short_description |
15 | 6 | _('Blog') |
... | ... | @@ -35,21 +26,6 @@ class Blog < Folder |
35 | 26 | true |
36 | 27 | end |
37 | 28 | |
38 | - def feed | |
39 | - self.children.find(:first, :conditions => {:type => 'RssFeed'}) | |
40 | - end | |
41 | - | |
42 | - def feed=(attrs) | |
43 | - if attrs | |
44 | - if self.feed | |
45 | - self.feed.update_attributes(attrs) | |
46 | - else | |
47 | - self.feed_attrs = attrs | |
48 | - end | |
49 | - end | |
50 | - self.feed | |
51 | - end | |
52 | - | |
53 | 29 | has_one :external_feed, :foreign_key => 'blog_id', :dependent => :destroy |
54 | 30 | |
55 | 31 | attr_accessor :external_feed_data |
... | ... | @@ -78,17 +54,15 @@ class Blog < Folder |
78 | 54 | end |
79 | 55 | end |
80 | 56 | |
81 | - def name=(value) | |
82 | - self.set_name(value) | |
83 | - if self.slug.blank? | |
84 | - self.slug = self.name.to_slug | |
85 | - else | |
86 | - self.slug = self.slug.to_slug | |
87 | - end | |
57 | + def self.icon_name(article = nil) | |
58 | + 'blog' | |
88 | 59 | end |
89 | 60 | |
90 | 61 | settings_items :visualization_format, :type => :string, :default => 'full' |
91 | 62 | validates_inclusion_of :visualization_format, :in => [ 'full', 'short' ], :if => :visualization_format |
92 | 63 | |
64 | + settings_items :display_posts_in_current_language, :type => :boolean, :default => true | |
65 | + | |
66 | + alias :display_posts_in_current_language? :display_posts_in_current_language | |
93 | 67 | |
94 | 68 | end | ... | ... |
app/models/blog_archives_block.rb
... | ... | @@ -24,7 +24,7 @@ class BlogArchivesBlock < Block |
24 | 24 | owner_blog = self.blog |
25 | 25 | return nil unless owner_blog |
26 | 26 | results = '' |
27 | - owner_blog.posts.group_by {|i| i.published_at.year }.sort_by { |year,count| -year }.each do |year, results_by_year| | |
27 | + owner_blog.posts.native_translations.group_by {|i| i.published_at.year }.sort_by { |year,count| -year }.each do |year, results_by_year| | |
28 | 28 | results << content_tag('li', content_tag('strong', "#{year} (#{results_by_year.size})")) |
29 | 29 | results << "<ul class='#{year}-archive'>" |
30 | 30 | results_by_year.group_by{|i| [ ('%02d' % i.published_at.month()), gettext(MONTHS[i.published_at.month() - 1])]}.sort.reverse.each do |month, results_by_month| | ... | ... |
... | ... | @@ -0,0 +1,38 @@ |
1 | +class CategoriesBlock < Block | |
2 | + | |
3 | + CATEGORY_TYPES = { | |
4 | + _('Generic category') => nil, | |
5 | + _('Region') => 'Region', | |
6 | + _('Product') => 'ProductCategory' | |
7 | + } | |
8 | + | |
9 | + settings_items :category_types, :type => Array, :default => [] | |
10 | + | |
11 | + def self.description | |
12 | + _("Categories Menu") | |
13 | + end | |
14 | + | |
15 | + def default_title | |
16 | + _("Categories Menu") | |
17 | + end | |
18 | + | |
19 | + def help | |
20 | + _('This block presents the categories like a web site menu.') | |
21 | + end | |
22 | + | |
23 | + def available_category_types | |
24 | + CATEGORY_TYPES | |
25 | + end | |
26 | + | |
27 | + def selected_categories | |
28 | + Category.top_level_for(self.owner).from_types(self.category_types) | |
29 | + end | |
30 | + | |
31 | + def content | |
32 | + block = self | |
33 | + lambda do | |
34 | + render :file => 'blocks/categories', :locals => { :block => block } | |
35 | + end | |
36 | + end | |
37 | + | |
38 | +end | ... | ... |
app/models/category.rb
... | ... | @@ -9,9 +9,9 @@ class Category < ActiveRecord::Base |
9 | 9 | validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.') |
10 | 10 | |
11 | 11 | # Finds all top level categories for a given environment. |
12 | - def self.top_level_for(environment) | |
13 | - self.find(:all, :conditions => ['parent_id is null and environment_id = ?', environment.id ]) | |
14 | - end | |
12 | + named_scope :top_level_for, lambda { |environment| | |
13 | + {:conditions => ['parent_id is null and environment_id = ?', environment.id ]} | |
14 | + } | |
15 | 15 | |
16 | 16 | acts_as_filesystem |
17 | 17 | |
... | ... | @@ -30,6 +30,12 @@ class Category < ActiveRecord::Base |
30 | 30 | |
31 | 31 | acts_as_having_image |
32 | 32 | |
33 | + named_scope :from_types, lambda { |types| | |
34 | + types.select{ |t| t.blank? }.empty? ? | |
35 | + { :conditions => { :type => types } } : | |
36 | + { :conditions => [ "type IN (?) OR type IS NULL", types.reject{ |t| t.blank? } ] } | |
37 | + } | |
38 | + | |
33 | 39 | def recent_articles(limit = 10) |
34 | 40 | self.articles.recent(limit) |
35 | 41 | end |
... | ... | @@ -58,4 +64,9 @@ class Category < ActiveRecord::Base |
58 | 64 | results |
59 | 65 | end |
60 | 66 | |
67 | + def is_leaf_displayable_in_menu? | |
68 | + return false if self.display_in_menu == false | |
69 | + self.children.find(:all, :conditions => {:display_in_menu => true}).empty? | |
70 | + end | |
71 | + | |
61 | 72 | end | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +class Certifier < ActiveRecord::Base | |
2 | + | |
3 | + belongs_to :environment | |
4 | + | |
5 | + has_many :qualifier_certifiers | |
6 | + has_many :qualifiers, :through => :qualifier_certifiers | |
7 | + | |
8 | + validates_presence_of :environment_id | |
9 | + validates_presence_of :name | |
10 | + | |
11 | + def link | |
12 | + self[:link] || '' | |
13 | + end | |
14 | +end | ... | ... |
app/models/change_password.rb
1 | 1 | class ChangePassword < Task |
2 | 2 | |
3 | - serialize :data, Hash | |
4 | - def data | |
5 | - self[:data] ||= {} | |
6 | - end | |
7 | - | |
8 | 3 | attr_accessor :login, :email, :password, :password_confirmation, :environment_id |
9 | 4 | |
10 | 5 | def self.human_attribute_name(attrib) |
... | ... | @@ -45,7 +40,7 @@ class ChangePassword < Task |
45 | 40 | end |
46 | 41 | |
47 | 42 | before_validation_on_create do |change_password| |
48 | - change_password.requestor = Person.find_by_identifier(change_password.login) | |
43 | + change_password.requestor = Person.find_by_identifier_and_environment_id(change_password.login, change_password.environment_id) | |
49 | 44 | end |
50 | 45 | |
51 | 46 | ################################################### |
... | ... | @@ -56,9 +51,16 @@ class ChangePassword < Task |
56 | 51 | validates_presence_of :password_confirmation, :on => :update, :if => lambda { |change| change.status != Task::Status::CANCELLED } |
57 | 52 | validates_confirmation_of :password, :if => lambda { |change| change.status != Task::Status::CANCELLED } |
58 | 53 | |
59 | - def initialize(*args) | |
60 | - super(*args) | |
61 | - self[:data] = {} | |
54 | + def title | |
55 | + _("Change password") | |
56 | + end | |
57 | + | |
58 | + def information | |
59 | + {:message => _('%{requestor} wants to change its password.')} | |
60 | + end | |
61 | + | |
62 | + def icon | |
63 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
62 | 64 | end |
63 | 65 | |
64 | 66 | def perform |
... | ... | @@ -87,8 +89,8 @@ class ChangePassword < Task |
87 | 89 | end |
88 | 90 | end |
89 | 91 | |
90 | - def description | |
91 | - _('Password change request') | |
92 | + def environment | |
93 | + self.requestor.environment | |
92 | 94 | end |
93 | 95 | |
94 | 96 | end | ... | ... |
app/models/comment.rb
1 | 1 | class Comment < ActiveRecord::Base |
2 | - | |
2 | + | |
3 | + track_actions :leave_comment, :after_create, :keep_params => ["article.title", "article.url", "title", "url", "body"], :custom_target => :action_tracker_target | |
4 | + | |
3 | 5 | validates_presence_of :title, :body |
4 | 6 | belongs_to :article, :counter_cache => true |
5 | 7 | belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id' |
8 | + has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy | |
9 | + belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' | |
6 | 10 | |
7 | 11 | # unauthenticated authors: |
8 | 12 | validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) |
... | ... | @@ -19,6 +23,10 @@ class Comment < ActiveRecord::Base |
19 | 23 | |
20 | 24 | xss_terminate :only => [ :body, :title, :name ], :on => 'validation' |
21 | 25 | |
26 | + def action_tracker_target | |
27 | + self.article.profile | |
28 | + end | |
29 | + | |
22 | 30 | def author_name |
23 | 31 | if author |
24 | 32 | author.short_name |
... | ... | @@ -35,16 +43,20 @@ class Comment < ActiveRecord::Base |
35 | 43 | author ? author.url : email |
36 | 44 | end |
37 | 45 | |
46 | + def author_url | |
47 | + author ? author.url : nil | |
48 | + end | |
49 | + | |
38 | 50 | def url |
39 | 51 | article.view_url.merge(:anchor => anchor) |
40 | 52 | end |
41 | 53 | |
42 | 54 | def message |
43 | - author_id ? _('(removed user)') : ('<br />' + _('(unauthenticated user)')) | |
55 | + author_id ? _('(removed user)') : _('(unauthenticated user)') | |
44 | 56 | end |
45 | 57 | |
46 | 58 | def removed_user_image |
47 | - '/images/icons-app/user_icon_size-minor.png' | |
59 | + '/images/icons-app/person-minor.png' | |
48 | 60 | end |
49 | 61 | |
50 | 62 | def anchor |
... | ... | @@ -62,18 +74,34 @@ class Comment < ActiveRecord::Base |
62 | 74 | end |
63 | 75 | |
64 | 76 | after_create do |comment| |
65 | - if comment.article.notify_comments? | |
77 | + if comment.article.notify_comments? && !comment.article.profile.notification_emails.empty? | |
66 | 78 | Comment::Notifier.deliver_mail(comment) |
67 | 79 | end |
68 | 80 | end |
69 | 81 | |
82 | + def replies | |
83 | + @replies || children | |
84 | + end | |
85 | + | |
86 | + def replies=(comments_list) | |
87 | + @replies = comments_list | |
88 | + end | |
89 | + | |
90 | + def self.as_thread | |
91 | + result = {} | |
92 | + root = [] | |
93 | + all.each do |c| | |
94 | + c.replies = [] | |
95 | + result[c.id] ||= c | |
96 | + c.reply_of_id.nil? ? root << c : result[c.reply_of_id].replies << c | |
97 | + end | |
98 | + root | |
99 | + end | |
100 | + | |
70 | 101 | class Notifier < ActionMailer::Base |
71 | 102 | def mail(comment) |
72 | 103 | profile = comment.article.profile |
73 | - email = profile.notification_emails | |
74 | - return unless email | |
75 | - recipients email | |
76 | - | |
104 | + recipients profile.notification_emails | |
77 | 105 | from "#{profile.environment.name} <#{profile.environment.contact_email}>" |
78 | 106 | subject _("[%s] you got a new comment!") % [profile.environment.name] |
79 | 107 | body :recipient => profile.nickname || profile.name, |
... | ... | @@ -81,6 +109,8 @@ class Comment < ActiveRecord::Base |
81 | 109 | :sender_link => comment.author_link, |
82 | 110 | :article_title => comment.article.name, |
83 | 111 | :comment_url => comment.url, |
112 | + :comment_title => comment.title, | |
113 | + :comment_body => comment.body, | |
84 | 114 | :environment => profile.environment.name, |
85 | 115 | :url => profile.environment.top_url |
86 | 116 | end | ... | ... |
app/models/communities_block.rb
... | ... | @@ -8,10 +8,6 @@ class CommunitiesBlock < ProfileListBlock |
8 | 8 | n__('{#} community', '{#} communities', profile_count) |
9 | 9 | end |
10 | 10 | |
11 | - def profile_image_link_method | |
12 | - :community_image_link | |
13 | - end | |
14 | - | |
15 | 11 | def help |
16 | 12 | __('This block displays the communities in which the user is a member.') |
17 | 13 | end |
... | ... | @@ -25,35 +21,15 @@ class CommunitiesBlock < ProfileListBlock |
25 | 21 | end |
26 | 22 | when Environment |
27 | 23 | lambda do |
28 | - link_to s_('communities|View all'), :controller => 'search', :action => 'assets', :asset => 'communities' | |
24 | + link_to s_('communities|View all'), :controller => 'browse', :action => 'communities' | |
29 | 25 | end |
30 | 26 | else |
31 | 27 | '' |
32 | 28 | end |
33 | 29 | end |
34 | 30 | |
35 | - def profile_count | |
36 | - if owner.kind_of?(Environment) | |
37 | - owner.communities.count(:conditions => { :visible => true }) | |
38 | - else | |
39 | - owner.communities(:visible => true).count | |
40 | - end | |
41 | - end | |
42 | - | |
43 | - def profile_finder | |
44 | - @profile_finder ||= CommunitiesBlock::Finder.new(self) | |
45 | - end | |
46 | - | |
47 | - class Finder < ProfileListBlock::Finder | |
48 | - def ids | |
49 | - # FIXME when owner is an environment (i.e. listing communities globally | |
50 | - # this can become SLOW) | |
51 | - if block.owner.kind_of?(Environment) | |
52 | - block.owner.communities.all(:conditions => {:visible => true}, :limit => block.limit, :order => 'random()').map(&:id) | |
53 | - else | |
54 | - block.owner.communities(:visible => true).map(&:id) | |
55 | - end | |
56 | - end | |
31 | + def profiles | |
32 | + owner.communities | |
57 | 33 | end |
58 | 34 | |
59 | 35 | end | ... | ... |
app/models/community.rb
... | ... | @@ -67,4 +67,12 @@ class Community < Organization |
67 | 67 | def blocks_to_expire_cache |
68 | 68 | [MembersBlock] |
69 | 69 | end |
70 | + | |
71 | + def each_member(offset=0) | |
72 | + while member = self.members.first(:order => :id, :offset => offset) | |
73 | + yield member | |
74 | + offset = offset + 1 | |
75 | + end | |
76 | + end | |
77 | + | |
70 | 78 | end | ... | ... |
app/models/consumption.rb
... | ... | @@ -0,0 +1,27 @@ |
1 | +class ContactList < ActiveRecord::Base | |
2 | + | |
3 | + serialize :list, Array | |
4 | + | |
5 | + def list | |
6 | + self[:list] || [] | |
7 | + end | |
8 | + | |
9 | + def data | |
10 | + if self.fetched | |
11 | + { "fetched" => true, "contact_list" => self.id, "error" => self.error_fetching } | |
12 | + else | |
13 | + {} | |
14 | + end | |
15 | + end | |
16 | + | |
17 | + def register_auth_error | |
18 | + msg = _('There was an error while authenticating. Did you enter correct login and password?') | |
19 | + self.update_attributes(:fetched => true, :error_fetching => msg) | |
20 | + end | |
21 | + | |
22 | + def register_error | |
23 | + msg = _('There was an error while looking for your contact list. Please, try again') | |
24 | + self.update_attributes(:fetched => true, :error_fetching => msg) | |
25 | + end | |
26 | + | |
27 | +end | ... | ... |
app/models/create_community.rb
... | ... | @@ -6,25 +6,11 @@ class CreateCommunity < Task |
6 | 6 | alias :environment :target |
7 | 7 | alias :environment= :target= |
8 | 8 | |
9 | - serialize :data, Hash | |
10 | - attr_protected :data | |
11 | - def data | |
12 | - self[:data] ||= Hash.new | |
13 | - end | |
14 | - | |
15 | 9 | acts_as_having_image |
16 | 10 | |
17 | 11 | DATA_FIELDS = Community.fields + ['name', 'closed'] |
18 | - | |
19 | 12 | DATA_FIELDS.each do |field| |
20 | - # getter | |
21 | - define_method(field) do | |
22 | - self.data[field.to_sym] | |
23 | - end | |
24 | - # setter | |
25 | - define_method("#{field}=") do |value| | |
26 | - self.data[field.to_sym] = value | |
27 | - end | |
13 | + settings_items field.to_sym | |
28 | 14 | end |
29 | 15 | |
30 | 16 | def validate |
... | ... | @@ -48,16 +34,30 @@ class CreateCommunity < Task |
48 | 34 | community.add_admin(self.requestor) |
49 | 35 | end |
50 | 36 | |
51 | - def description | |
52 | - _('%s wants to create community %s.') % [requestor.name, self.name] | |
37 | + def title | |
38 | + _("New community") | |
39 | + end | |
40 | + | |
41 | + def icon | |
42 | + src = image ? image.public_filename(:minor) : '/images/icons-app/community-minor.png' | |
43 | + {:type => :defined_image, :src => src, :name => name} | |
44 | + end | |
45 | + | |
46 | + def subject | |
47 | + name | |
53 | 48 | end |
54 | 49 | |
55 | - def closing_statement | |
56 | - data[:closing_statement] | |
50 | + def information | |
51 | + if description.blank? | |
52 | + { :message => _('%{requestor} wants to create community %{subject} with no description.') } | |
53 | + else | |
54 | + { :message => _('%{requestor} wants to create community %{subject} with this description:<p><em>%{description}</em></p>'), | |
55 | + :variables => {:description => description} } | |
56 | + end | |
57 | 57 | end |
58 | 58 | |
59 | - def closing_statement= value | |
60 | - data[:closing_statement] = value | |
59 | + def reject_details | |
60 | + true | |
61 | 61 | end |
62 | 62 | |
63 | 63 | # tells if this request was rejected |
... | ... | @@ -70,8 +70,11 @@ class CreateCommunity < Task |
70 | 70 | self.status == Task::Status::FINISHED |
71 | 71 | end |
72 | 72 | |
73 | + def target_notification_description | |
74 | + _('%{requestor} wants to create community %{subject}') % {:requestor => requestor.name, :subject => subject} | |
75 | + end | |
76 | + | |
73 | 77 | def target_notification_message |
74 | - description + "\n\n" + | |
75 | 78 | _("User \"%{user}\" just requested to create community %{community}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :user => self.requestor.name, :community => self.name } |
76 | 79 | end |
77 | 80 | |
... | ... | @@ -82,7 +85,7 @@ class CreateCommunity < Task |
82 | 85 | end |
83 | 86 | |
84 | 87 | def task_cancelled_message |
85 | - _("Your request for registering community %{community} at %{environment} was not approved by the environment administrator. The following explanation was given: \n\n%{explanation}") % { :community => self.name, :environment => self.environment, :explanation => self.closing_statement } | |
88 | + _("Your request for registering community %{community} at %{environment} was not approved by the environment administrator. The following explanation was given: \n\n%{explanation}") % { :community => self.name, :environment => self.environment, :explanation => self.reject_explanation } | |
86 | 89 | end |
87 | 90 | |
88 | 91 | def task_finished_message | ... | ... |
app/models/create_enterprise.rb
... | ... | @@ -11,23 +11,9 @@ class CreateEnterprise < Task |
11 | 11 | N_('Economic activity') |
12 | 12 | N_('Management information') |
13 | 13 | |
14 | - DATA_FIELDS = Enterprise.fields + %w[name identifier region_id reject_explanation] | |
15 | - | |
16 | - serialize :data, Hash | |
17 | - attr_protected :data | |
18 | - def data | |
19 | - self[:data] ||= Hash.new | |
20 | - end | |
21 | - | |
14 | + DATA_FIELDS = Enterprise.fields + %w[name identifier region_id] | |
22 | 15 | DATA_FIELDS.each do |field| |
23 | - # getter | |
24 | - define_method(field) do | |
25 | - self.data[field.to_sym] | |
26 | - end | |
27 | - # setter | |
28 | - define_method("#{field}=") do |value| | |
29 | - self.data[field.to_sym] = value | |
30 | - end | |
16 | + settings_items field.to_sym | |
31 | 17 | end |
32 | 18 | |
33 | 19 | # checks for virtual attributes |
... | ... | @@ -48,7 +34,6 @@ class CreateEnterprise < Task |
48 | 34 | |
49 | 35 | # check for explanation when rejecting |
50 | 36 | validates_presence_of :reject_explanation, :if => (lambda { |record| record.status == Task::Status::CANCELLED } ) |
51 | - | |
52 | 37 | xss_terminate :only => [ :acronym, :address, :contact_person, :contact_phone, :economic_activity, :legal_form, :management_information, :name ], :on => 'validation' |
53 | 38 | |
54 | 39 | def validate |
... | ... | @@ -59,7 +44,7 @@ class CreateEnterprise < Task |
59 | 44 | end |
60 | 45 | end |
61 | 46 | |
62 | - if self.identifier && Profile.exists?(:identifier => self.identifier) | |
47 | + if self.status != Task::Status::CANCELLED && self.identifier && Profile.exists?(:identifier => self.identifier) | |
63 | 48 | self.errors.add(:identifier, '%{fn} is already being as identifier by another enterprise, organization or person.') |
64 | 49 | end |
65 | 50 | end |
... | ... | @@ -91,7 +76,7 @@ class CreateEnterprise < Task |
91 | 76 | end |
92 | 77 | |
93 | 78 | def environment |
94 | - region ? region.environment : self.requestor ? self.requestor.environment : Environment.default | |
79 | + requestor.environment | |
95 | 80 | end |
96 | 81 | |
97 | 82 | def available_regions |
... | ... | @@ -153,8 +138,24 @@ class CreateEnterprise < Task |
153 | 138 | enterprise.add_admin(enterprise.user.person) |
154 | 139 | end |
155 | 140 | |
156 | - def description | |
157 | - _('Enterprise registration: "%s"') % self.name | |
141 | + def title | |
142 | + _("Enterprise registration") | |
143 | + end | |
144 | + | |
145 | + def icon | |
146 | + {:type => :defined_image, :src => '/images/icons-app/enterprise-minor.png', :name => name} | |
147 | + end | |
148 | + | |
149 | + def subject | |
150 | + name | |
151 | + end | |
152 | + | |
153 | + def information | |
154 | + {:message => _('%{requestor} wants to create enterprise %{subject}.')} | |
155 | + end | |
156 | + | |
157 | + def reject_details | |
158 | + true | |
158 | 159 | end |
159 | 160 | |
160 | 161 | def task_created_message |
... | ... | @@ -164,18 +165,18 @@ class CreateEnterprise < Task |
164 | 165 | end |
165 | 166 | |
166 | 167 | def task_finished_message |
167 | - _('Your request for registering the enterprise "%{enterprise}" was approved. You can access %{environment} now and provide start providing all relevant information your new enterprise.') % { :enterprise => self.name, :environment => self.environment } | |
168 | + __('Your request for registering the enterprise "%{enterprise}" was approved. You can access %{environment} now and provide start providing all relevant information your new enterprise.') % { :enterprise => self.name, :environment => self.environment } | |
168 | 169 | end |
169 | 170 | |
170 | 171 | def task_cancelled_message |
171 | - _("Your request for registering the enterprise %{enterprise} at %{environment} was NOT approved by the validator organization. The following explanation was given: \n\n%{explanation}") % { :enterprise => self.name, :environment => self.environment, :explanation => self.reject_explanation } | |
172 | + __("Your request for registering the enterprise %{enterprise} at %{environment} was NOT approved by the validator organization. The following explanation was given: \n\n%{explanation}") % { :enterprise => self.name, :environment => self.environment, :explanation => self.reject_explanation } | |
172 | 173 | end |
173 | 174 | |
174 | 175 | def target_notification_message |
175 | 176 | msg = "" |
176 | - msg << _("Enterprise \"%{enterprise}\" just requested to enter %{environment}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :enterprise => self.name, :environment => self.environment } | |
177 | + msg << __("Enterprise \"%{enterprise}\" just requested to enter %{environment}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :enterprise => self.name, :environment => self.environment } | |
177 | 178 | msg << "\n" |
178 | - msg << _("The data provided by the enterprise was the following:\n") << "\n" | |
179 | + msg << __("The data provided by the enterprise was the following:\n") << "\n" | |
179 | 180 | |
180 | 181 | |
181 | 182 | msg << (_("Name: %s") % self.name) << "\n" |
... | ... | @@ -185,16 +186,20 @@ class CreateEnterprise < Task |
185 | 186 | msg << (_("Foundation Year: %d") % self.foundation_year) << "\n" unless self.foundation_year.blank? |
186 | 187 | msg << (_("Economic activity: %s") % self.economic_activity) << "\n" |
187 | 188 | |
188 | - msg << _("Information about enterprise's management:\n") << self.management_information.to_s << "\n" | |
189 | + msg << __("Information about enterprise's management:\n") << self.management_information.to_s << "\n" | |
189 | 190 | |
190 | 191 | msg << (_("Contact phone: %s") % self.contact_phone) << "\n" |
191 | 192 | msg << (_("Contact person: %s") % self.contact_person) << "\n" |
192 | 193 | |
193 | - msg << _('CreateEnterprise|Identifier') | |
194 | + msg << __('CreateEnterprise|Identifier') | |
194 | 195 | |
195 | 196 | msg |
196 | 197 | end |
197 | 198 | |
199 | + def target_notification_description | |
200 | + _('%{requestor} wants to create enterprise %{subject}.') % {:requestor => requestor.name, :subject => subject} | |
201 | + end | |
202 | + | |
198 | 203 | def permission |
199 | 204 | :validate_enterprise |
200 | 205 | end | ... | ... |
app/models/disabled_enterprise_message_block.rb
1 | 1 | class DisabledEnterpriseMessageBlock < Block |
2 | 2 | |
3 | 3 | def self.description |
4 | - _('"Disabled enterprise" message') | |
4 | + __('"Disabled enterprise" message') | |
5 | 5 | end |
6 | 6 | |
7 | 7 | def help |
8 | - _('Shows a message for disabled enterprises.') | |
8 | + __('Shows a message for disabled enterprises.') | |
9 | 9 | end |
10 | 10 | |
11 | 11 | def default_title | ... | ... |
app/models/email_activation.rb
... | ... | @@ -11,8 +11,20 @@ class EmailActivation < Task |
11 | 11 | end |
12 | 12 | end |
13 | 13 | |
14 | - def description | |
15 | - _("'%{user} wants to activate email '%{email}'") % { :user => person.name, :email => person.email_addresses.join(', ') } | |
14 | + def title | |
15 | + _("Email activation") | |
16 | + end | |
17 | + | |
18 | + def subject | |
19 | + person.email_addresses.join(', ') | |
20 | + end | |
21 | + | |
22 | + def information | |
23 | + {:message => _("%{requestor} wants to activate the following email: %{subject}.")} | |
24 | + end | |
25 | + | |
26 | + def icon | |
27 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
16 | 28 | end |
17 | 29 | |
18 | 30 | def perform | ... | ... |
app/models/enterprise.rb
... | ... | @@ -4,7 +4,8 @@ class Enterprise < Organization |
4 | 4 | |
5 | 5 | N_('Enterprise') |
6 | 6 | |
7 | - has_many :products, :dependent => :destroy | |
7 | + has_many :products, :dependent => :destroy, :order => 'name ASC' | |
8 | + has_many :inputs, :through => :products | |
8 | 9 | |
9 | 10 | extra_data_for_index :product_categories |
10 | 11 | |
... | ... | @@ -51,6 +52,10 @@ class Enterprise < Organization |
51 | 52 | environment ? environment.active_enterprise_fields : [] |
52 | 53 | end |
53 | 54 | |
55 | + def highlighted_products_with_image(options = {}) | |
56 | + Product.find(:all, {:conditions => {:highlighted => true}, :joins => :image}.merge(options)) | |
57 | + end | |
58 | + | |
54 | 59 | def required_fields |
55 | 60 | environment ? environment.required_enterprise_fields : [] |
56 | 61 | end |
... | ... | @@ -117,17 +122,28 @@ class Enterprise < Organization |
117 | 122 | end |
118 | 123 | |
119 | 124 | def default_set_of_blocks |
125 | + links = [ | |
126 | + {:name => _("Enterprises's profile"), :address => '/profile/{profile}', :icon => 'ok'}, | |
127 | + {:name => _('Blog'), :address => '/{profile}/blog', :icon => 'edit'}, | |
128 | + {:name => _('Products'), :address => '/catalog/{profile}', :icon => 'new'}, | |
129 | + ] | |
120 | 130 | blocks = [ |
121 | - [MainBlock], | |
122 | - [ProfileInfoBlock, MembersBlock], | |
123 | - [RecentDocumentsBlock] | |
131 | + [MainBlock.new], | |
132 | + [ProfileImageBlock.new, LinkListBlock.new(:links => links)], | |
133 | + [] | |
124 | 134 | ] |
125 | 135 | if !environment.enabled?('disable_products_for_enterprises') |
126 | - blocks[2].unshift ProductsBlock | |
136 | + blocks[2].unshift ProductsBlock.new | |
127 | 137 | end |
128 | 138 | blocks |
129 | 139 | end |
130 | 140 | |
141 | + def default_set_of_articles | |
142 | + [ | |
143 | + Blog.new(:name => _('Blog')), | |
144 | + ] | |
145 | + end | |
146 | + | |
131 | 147 | before_create do |enterprise| |
132 | 148 | if enterprise.environment.enabled?('enterprises_are_disabled_when_created') |
133 | 149 | enterprise.enabled = false |
... | ... | @@ -149,10 +165,4 @@ class Enterprise < Organization |
149 | 165 | enable_contact_us |
150 | 166 | end |
151 | 167 | |
152 | - protected | |
153 | - | |
154 | - def default_homepage(attrs) | |
155 | - EnterpriseHomepage.new(attrs) | |
156 | - end | |
157 | - | |
158 | 168 | end | ... | ... |
app/models/enterprise_activation.rb
... | ... | @@ -2,7 +2,6 @@ class EnterpriseActivation < Task |
2 | 2 | |
3 | 3 | class RequestorRequired < Exception; end |
4 | 4 | |
5 | - acts_as_having_settings :field => :data | |
6 | 5 | settings_items :enterprise_id, :integer |
7 | 6 | |
8 | 7 | validates_presence_of :enterprise_id |
... | ... | @@ -20,4 +19,20 @@ class EnterpriseActivation < Task |
20 | 19 | self.enterprise.enable(requestor) |
21 | 20 | end |
22 | 21 | |
22 | + def title | |
23 | + _("Enterprise activation") | |
24 | + end | |
25 | + | |
26 | + def linked_subject | |
27 | + {:text => target.name, :url => target.public_profile_url} | |
28 | + end | |
29 | + | |
30 | + def information | |
31 | + {:message => _('%{requestor} wants to activate enterprise %{linked_subject}.')} | |
32 | + end | |
33 | + | |
34 | + def icon | |
35 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
36 | + end | |
37 | + | |
23 | 38 | end | ... | ... |
app/models/enterprise_homepage.rb
app/models/enterprises_block.rb
... | ... | @@ -28,29 +28,8 @@ class EnterprisesBlock < ProfileListBlock |
28 | 28 | end |
29 | 29 | end |
30 | 30 | |
31 | - def profile_count | |
32 | - if owner.kind_of?(Environment) | |
33 | - owner.enterprises.count(:conditions => { :visible => true }) | |
34 | - else | |
35 | - owner.enterprises(:visible => true).count | |
36 | - end | |
37 | - | |
38 | - end | |
39 | - | |
40 | - def profile_finder | |
41 | - @profile_finder ||= EnterprisesBlock::Finder.new(self) | |
42 | - end | |
43 | - | |
44 | - class Finder < ProfileListBlock::Finder | |
45 | - def ids | |
46 | - # FIXME when owner is an environment (i.e. listing enterprises globally | |
47 | - # this can become SLOW) | |
48 | - if block.owner.kind_of?(Environment) | |
49 | - block.owner.enterprises.all(:conditions => {:visible => true}, :limit => block.limit, :order => 'random()').map(&:id) | |
50 | - else | |
51 | - block.owner.enterprises.select(&:visible).map(&:id) | |
52 | - end | |
53 | - end | |
31 | + def profiles | |
32 | + owner.enterprises | |
54 | 33 | end |
55 | 34 | |
56 | 35 | end | ... | ... |
app/models/environment.rb
... | ... | @@ -3,6 +3,8 @@ |
3 | 3 | # domains. |
4 | 4 | class Environment < ActiveRecord::Base |
5 | 5 | |
6 | + has_many :users | |
7 | + | |
6 | 8 | self.partial_updates = false |
7 | 9 | |
8 | 10 | has_many :tasks, :dependent => :destroy, :as => 'target' |
... | ... | @@ -14,6 +16,7 @@ class Environment < ActiveRecord::Base |
14 | 16 | 'manage_environment_categories' => N_('Manage environment categories'), |
15 | 17 | 'manage_environment_roles' => N_('Manage environment roles'), |
16 | 18 | 'manage_environment_validators' => N_('Manage environment validators'), |
19 | + 'manage_environment_users' => N_('Manage environment users'), | |
17 | 20 | } |
18 | 21 | |
19 | 22 | module Roles |
... | ... | @@ -42,9 +45,7 @@ class Environment < ActiveRecord::Base |
42 | 45 | :name => N_('Member'), |
43 | 46 | :environment => self, |
44 | 47 | :permissions => [ |
45 | - 'edit_profile', | |
46 | - 'post_content', | |
47 | - 'manage_products' | |
48 | + 'invite_members', | |
48 | 49 | ] |
49 | 50 | ) |
50 | 51 | # moderators for enterprises, communities etc |
... | ... | @@ -67,7 +68,7 @@ class Environment < ActiveRecord::Base |
67 | 68 | end |
68 | 69 | |
69 | 70 | def admins |
70 | - self.members_by_role(Environment::Roles.admin(self.id)) | |
71 | + Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', Environment::Roles.admin(self).id]) | |
71 | 72 | end |
72 | 73 | |
73 | 74 | # returns the available features for a Environment, in the form of a |
... | ... | @@ -80,7 +81,7 @@ class Environment < ActiveRecord::Base |
80 | 81 | 'disable_asset_communities' => __('Disable search for communities'), |
81 | 82 | 'disable_asset_products' => _('Disable search for products'), |
82 | 83 | 'disable_asset_events' => _('Disable search for events'), |
83 | - 'disable_products_for_enterprises' => _('Disable products for enterprises'), | |
84 | + 'disable_products_for_enterprises' => __('Disable products for enterprises'), | |
84 | 85 | 'disable_categories' => _('Disable categories'), |
85 | 86 | 'disable_cms' => _('Disable CMS'), |
86 | 87 | 'disable_header_and_footer' => _('Disable header/footer editing by users'), |
... | ... | @@ -89,14 +90,12 @@ class Environment < ActiveRecord::Base |
89 | 90 | 'disable_select_city_for_contact' => _('Disable state/city select for contact form'), |
90 | 91 | 'disable_contact_person' => _('Disable contact for people'), |
91 | 92 | 'disable_contact_community' => _('Disable contact for groups/communities'), |
92 | - 'enterprise_registration' => _('Enterprise registration'), | |
93 | - 'join_community_popup' => _('Ask users to join a group/community with a popup'), | |
93 | + 'enterprise_registration' => __('Enterprise registration'), | |
94 | 94 | |
95 | - 'enterprise_activation' => _('Enable activation of enterprises'), | |
95 | + 'enterprise_activation' => __('Enable activation of enterprises'), | |
96 | 96 | 'wysiwyg_editor_for_environment_home' => _('Use WYSIWYG editor to edit environment home page'), |
97 | 97 | 'media_panel' => _('Media panel in WYSIWYG editor'), |
98 | 98 | 'select_preferred_domain' => _('Select preferred domains per profile'), |
99 | - 'display_wizard_signup' => _('Display wizard signup'), | |
100 | 99 | 'use_portal_community' => _('Use the portal as news source for front page'), |
101 | 100 | 'user_themes' => _('Allow users to create their own themes'), |
102 | 101 | 'search_in_home' => _("Display search form in home page"), |
... | ... | @@ -107,7 +106,10 @@ class Environment < ActiveRecord::Base |
107 | 106 | 'organizations_are_moderated_by_default' => _("Organizations have moderated publication by default"), |
108 | 107 | 'enable_organization_url_change' => _("Allow organizations to change their URL"), |
109 | 108 | 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"), |
110 | - 'enterprises_are_disabled_when_created' => _('Enterprises are disabled when created'), | |
109 | + 'enterprises_are_disabled_when_created' => __('Enterprises are disabled when created'), | |
110 | + 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'), | |
111 | + 'xmpp_chat' => _('XMPP/Jabber based chat'), | |
112 | + 'show_zoom_button_on_article_images' => _('Show a zoom link on all article images') | |
111 | 113 | } |
112 | 114 | end |
113 | 115 | |
... | ... | @@ -131,7 +133,8 @@ class Environment < ActiveRecord::Base |
131 | 133 | env.boxes[1].blocks << RecentDocumentsBlock.new |
132 | 134 | |
133 | 135 | # "right" area |
134 | - env.boxes[2].blocks << ProfileListBlock.new | |
136 | + env.boxes[2].blocks << CommunitiesBlock.new(:limit => 6) | |
137 | + env.boxes[2].blocks << PeopleBlock.new(:limit => 6) | |
135 | 138 | end |
136 | 139 | |
137 | 140 | # One Environment can be reached by many domains |
... | ... | @@ -147,10 +150,16 @@ class Environment < ActiveRecord::Base |
147 | 150 | has_many :categories |
148 | 151 | has_many :display_categories, :class_name => 'Category', :conditions => 'display_color is not null and parent_id is null', :order => 'display_color' |
149 | 152 | |
153 | + has_many :product_categories, :conditions => { :type => 'ProductCategory'} | |
150 | 154 | has_many :regions |
151 | 155 | |
152 | 156 | has_many :roles |
153 | 157 | |
158 | + has_many :qualifiers | |
159 | + has_many :certifiers | |
160 | + | |
161 | + has_many :mailings, :class_name => 'EnvironmentMailing', :foreign_key => :source_id, :as => 'source' | |
162 | + | |
154 | 163 | acts_as_accessible |
155 | 164 | |
156 | 165 | def superior_intances |
... | ... | @@ -198,16 +207,21 @@ class Environment < ActiveRecord::Base |
198 | 207 | settings_items :location, :type => String |
199 | 208 | settings_items :layout_template, :type => String, :default => 'default' |
200 | 209 | settings_items :homepage, :type => String |
201 | - settings_items :description, :type => String | |
202 | - settings_items :category_types, :type => Array, :default => ['Category'] | |
210 | + settings_items :description, :type => String, :default => '<div style="text-align: center"><a href="http://noosfero.org/"><img src="/images/noosfero-network.png" alt="Noosfero"/></a></div>' | |
203 | 211 | settings_items :enable_ssl |
204 | - settings_items :theme, :type => String, :default => 'default' | |
205 | - settings_items :icon_theme, :type => String, :default => 'default' | |
206 | 212 | settings_items :local_docs, :type => Array, :default => [] |
207 | 213 | settings_items :news_amount_by_folder, :type => Integer, :default => 4 |
208 | 214 | settings_items :help_message_to_add_enterprise, :type => String, :default => '' |
209 | 215 | settings_items :tip_message_enterprise_activation_question, :type => String, :default => '' |
210 | 216 | |
217 | + settings_items :currency_unit, :type => String, :default => '$' | |
218 | + settings_items :currency_separator, :type => String, :default => '.' | |
219 | + settings_items :currency_delimiter, :type => String, :default => ',' | |
220 | + | |
221 | + settings_items :trusted_sites_for_iframe, :type => Array, :default => ['itheora.org', 'tv.softwarelivre.org', 'stream.softwarelivre.org'] | |
222 | + | |
223 | + settings_items :enabled_plugins, :type => Array, :default => [] | |
224 | + | |
211 | 225 | def news_amount_by_folder=(amount) |
212 | 226 | settings[:news_amount_by_folder] = amount.to_i |
213 | 227 | end |
... | ... | @@ -241,6 +255,29 @@ class Environment < ActiveRecord::Base |
241 | 255 | end |
242 | 256 | end |
243 | 257 | |
258 | + def enabled_features | |
259 | + features = self.class.available_features | |
260 | + features.delete_if{ |k, v| !self.enabled?(k) } | |
261 | + end | |
262 | + | |
263 | + before_create :enable_default_features | |
264 | + def enable_default_features | |
265 | + %w( | |
266 | + disable_asset_products | |
267 | + disable_gender_icon | |
268 | + disable_products_for_enterprises | |
269 | + disable_select_city_for_contact | |
270 | + enterprise_registration | |
271 | + media_panel | |
272 | + organizations_are_moderated_by_default | |
273 | + show_balloon_with_profile_links_when_clicked | |
274 | + use_portal_community | |
275 | + wysiwyg_editor_for_environment_home | |
276 | + ).each do |feature| | |
277 | + enable(feature) | |
278 | + end | |
279 | + end | |
280 | + | |
244 | 281 | # returns <tt>true</tt> if this Environment has terms of use to be |
245 | 282 | # accepted by users before registration. |
246 | 283 | def has_terms_of_use? |
... | ... | @@ -457,6 +494,10 @@ class Environment < ActiveRecord::Base |
457 | 494 | |
458 | 495 | xss_terminate :only => [ :message_for_disabled_enterprise ], :with => 'white_list', :on => 'validation' |
459 | 496 | |
497 | + validates_presence_of :theme | |
498 | + | |
499 | + include WhiteListFilter | |
500 | + filter_iframes :message_for_disabled_enterprise, :whitelist => lambda { trusted_sites_for_iframe } | |
460 | 501 | |
461 | 502 | # ################################################# |
462 | 503 | # Business logic in general |
... | ... | @@ -525,7 +566,29 @@ class Environment < ActiveRecord::Base |
525 | 566 | end |
526 | 567 | |
527 | 568 | def themes=(values) |
528 | - settings[:themes] = values.map(&:id) | |
569 | + settings[:themes] = values | |
570 | + end | |
571 | + | |
572 | + def add_themes(values) | |
573 | + if settings[:themes].nil? | |
574 | + self.themes = values | |
575 | + else | |
576 | + settings[:themes] += values | |
577 | + end | |
578 | + end | |
579 | + | |
580 | + before_create do |env| | |
581 | + env.settings[:themes] ||= %w[ | |
582 | + aluminium | |
583 | + butter | |
584 | + chameleon | |
585 | + chocolate | |
586 | + noosfero | |
587 | + orange | |
588 | + plum | |
589 | + scarletred | |
590 | + skyblue | |
591 | + ] | |
529 | 592 | end |
530 | 593 | |
531 | 594 | def community_template |
... | ... | @@ -589,7 +652,7 @@ class Environment < ActiveRecord::Base |
589 | 652 | end |
590 | 653 | |
591 | 654 | def portal_folders |
592 | - (settings[:portal_folders] || []).map{|fid| portal_community.articles.find(fid) } | |
655 | + (settings[:portal_folders] || []).map{|fid| portal_community.articles.find(:first, :conditions => { :id => fid }) }.compact | |
593 | 656 | end |
594 | 657 | |
595 | 658 | def portal_folders=(folders) |
... | ... | @@ -628,4 +691,17 @@ class Environment < ActiveRecord::Base |
628 | 691 | end |
629 | 692 | end |
630 | 693 | |
694 | + def highlighted_products_with_image(options = {}) | |
695 | + Product.find(:all, {:conditions => {:highlighted => true, :enterprise_id => self.enterprises.find(:all, :select => :id) }, :joins => :image}.merge(options)) | |
696 | + end | |
697 | + | |
698 | + settings_items :home_cache_in_minutes, :type => :integer, :default => 5 | |
699 | + settings_items :general_cache_in_minutes, :type => :integer, :default => 15 | |
700 | + settings_items :profile_cache_in_minutes, :type => :integer, :default => 15 | |
701 | + | |
702 | + def image_galleries | |
703 | + portal_community ? portal_community.image_galleries : [] | |
704 | + end | |
705 | + | |
631 | 706 | end |
707 | + | ... | ... |
... | ... | @@ -0,0 +1,25 @@ |
1 | +class EnvironmentMailing < Mailing | |
2 | + | |
3 | + def recipients(offset=0, limit=100) | |
4 | + source.people.all(:order => :id, :offset => offset, :limit => limit, :joins => "LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)", :conditions => { "m.person_id" => nil }) | |
5 | + end | |
6 | + | |
7 | + def each_recipient | |
8 | + offset = 0 | |
9 | + limit = 100 | |
10 | + while !(people = recipients(offset, limit)).empty? | |
11 | + people.each do |person| | |
12 | + yield person | |
13 | + end | |
14 | + offset = offset + limit | |
15 | + end | |
16 | + end | |
17 | + | |
18 | + def signature_message | |
19 | + _('Sent by %s.') % source.name | |
20 | + end | |
21 | + | |
22 | + def url | |
23 | + source.top_url | |
24 | + end | |
25 | +end | ... | ... |
app/models/environment_statistics_block.rb
... | ... | @@ -17,11 +17,12 @@ class EnvironmentStatisticsBlock < Block |
17 | 17 | enterprises = owner.enterprises.visible.count |
18 | 18 | communities = owner.communities.visible.count |
19 | 19 | |
20 | - info = [ | |
21 | - n_('One user', '%{num} users', users) % { :num => users }, | |
22 | - n__('One enterprise', '%{num} enterprises', enterprises) % { :num => enterprises }, | |
23 | - n__('One community', '%{num} communities', communities) % { :num => communities }, | |
24 | - ] | |
20 | + info = [] | |
21 | + info << (n_('One user', '%{num} users', users) % { :num => users }) | |
22 | + unless owner.enabled?('disable_asset_enterprises') | |
23 | + info << (n__('One enterprise', '%{num} enterprises', enterprises) % { :num => enterprises }) | |
24 | + end | |
25 | + info << (n__('One community', '%{num} communities', communities) % { :num => communities }) | |
25 | 26 | |
26 | 27 | block_title(title) + content_tag('ul', info.map {|item| content_tag('li', item) }.join("\n")) |
27 | 28 | end | ... | ... |
app/models/event.rb
1 | 1 | class Event < Article |
2 | 2 | |
3 | - acts_as_having_settings :field => :body | |
4 | - | |
5 | - settings_items :description, :type => :string | |
6 | - settings_items :link, :type => :string | |
7 | 3 | settings_items :address, :type => :string |
8 | 4 | |
5 | + def link=(value) | |
6 | + self.setting[:link] = maybe_add_http(value) | |
7 | + end | |
8 | + | |
9 | + def link | |
10 | + maybe_add_http(self.setting[:link]) | |
11 | + end | |
12 | + | |
9 | 13 | xss_terminate :only => [ :link ], :on => 'validation' |
10 | - xss_terminate :only => [ :description, :link, :address ], :with => 'white_list', :on => 'validation' | |
14 | + xss_terminate :only => [ :body, :link, :address ], :with => 'white_list', :on => 'validation' | |
15 | + | |
16 | + def initialize(*args) | |
17 | + super(*args) | |
18 | + self.start_date ||= Date.today | |
19 | + end | |
11 | 20 | |
12 | 21 | validates_presence_of :title, :start_date |
13 | 22 | |
... | ... | @@ -21,6 +30,9 @@ class Event < Article |
21 | 30 | {:conditions => ['start_date = :date AND end_date IS NULL OR (start_date <= :date AND end_date >= :date)', {:date => date}]} |
22 | 31 | } |
23 | 32 | |
33 | + include WhiteListFilter | |
34 | + filter_iframes :body, :link, :address, :whitelist => lambda { profile && profile.environment && profile.environment.trusted_sites_for_iframe } | |
35 | + | |
24 | 36 | def self.description |
25 | 37 | _('A calendar event') |
26 | 38 | end |
... | ... | @@ -29,7 +41,7 @@ class Event < Article |
29 | 41 | _('Event') |
30 | 42 | end |
31 | 43 | |
32 | - def icon_name | |
44 | + def self.icon_name(article = nil) | |
33 | 45 | 'event' |
34 | 46 | end |
35 | 47 | |
... | ... | @@ -88,26 +100,27 @@ class Event < Article |
88 | 100 | } |
89 | 101 | } |
90 | 102 | |
91 | - if self.description | |
103 | + if self.body | |
92 | 104 | html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description') |
93 | 105 | end |
94 | 106 | } |
95 | 107 | |
96 | - if self.description | |
97 | - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.description) | |
108 | + if self.body | |
109 | + result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.body) | |
98 | 110 | end |
99 | 111 | |
100 | 112 | result |
101 | 113 | end |
102 | 114 | |
103 | - def link=(value) | |
104 | - self.body[:link] = maybe_add_http(value) | |
115 | + def event? | |
116 | + true | |
105 | 117 | end |
106 | 118 | |
107 | - def link | |
108 | - maybe_add_http(self.body[:link]) | |
119 | + def tiny_mce? | |
120 | + true | |
109 | 121 | end |
110 | 122 | |
123 | + include Noosfero::TranslatableContent | |
111 | 124 | include MaybeAddHttp |
112 | 125 | |
113 | 126 | end | ... | ... |
app/models/favorite_enterprises_block.rb
... | ... | @@ -9,7 +9,7 @@ class FavoriteEnterprisesBlock < ProfileListBlock |
9 | 9 | end |
10 | 10 | |
11 | 11 | def self.description |
12 | - __('Favorite enterprises') | |
12 | + __('Favorite Enterprises') | |
13 | 13 | end |
14 | 14 | |
15 | 15 | def footer |
... | ... | @@ -20,18 +20,8 @@ class FavoriteEnterprisesBlock < ProfileListBlock |
20 | 20 | end |
21 | 21 | end |
22 | 22 | |
23 | - def profile_count | |
24 | - owner.favorite_enterprises.count | |
25 | - end | |
26 | - | |
27 | - def profile_finder | |
28 | - @profile_finder ||= FavoriteEnterprisesBlock::Finder.new(self) | |
29 | - end | |
30 | - | |
31 | - class Finder < ProfileListBlock::Finder | |
32 | - def ids | |
33 | - block.owner.favorite_enterprises.map(&:id) | |
34 | - end | |
23 | + def profiles | |
24 | + owner.favorite_enterprises | |
35 | 25 | end |
36 | 26 | |
37 | 27 | end | ... | ... |
... | ... | @@ -0,0 +1,35 @@ |
1 | +class FeaturedProductsBlock < Block | |
2 | + | |
3 | + settings_items :product_ids, :type => Array, :default => [] | |
4 | + settings_items :groups_of, :type => :integer, :default => 3 | |
5 | + settings_items :speed, :type => :integer, :default => 1000 | |
6 | + settings_items :reflect, :type => :boolean, :default => true | |
7 | + | |
8 | + before_save do |block| | |
9 | + if block.owner.kind_of?(Environment) && block.product_ids.blank? | |
10 | + seed = block.owner.products.count | |
11 | + block.product_ids = block.owner.highlighted_products_with_image(:offset => (rand(seed) % (seed - block.groups_of * 3)), :limit => block.groups_of * 3).map(&:id) | |
12 | + end | |
13 | + block.groups_of = block.groups_of.to_i | |
14 | + end | |
15 | + | |
16 | + def self.description | |
17 | + _('Featured Products') | |
18 | + end | |
19 | + | |
20 | + def products | |
21 | + Product.find(self.product_ids) || [] | |
22 | + end | |
23 | + | |
24 | + def products_for_selection | |
25 | + self.owner.highlighted_products_with_image | |
26 | + end | |
27 | + | |
28 | + def content | |
29 | + block = self | |
30 | + lambda do | |
31 | + render :file => 'blocks/featured_products', :locals => { :block => block } | |
32 | + end | |
33 | + end | |
34 | + | |
35 | +end | ... | ... |
app/models/feed_reader_block.rb
... | ... | @@ -12,7 +12,7 @@ class FeedReaderBlock < Block |
12 | 12 | def address=(new_address) |
13 | 13 | old_address = address |
14 | 14 | orig_set_address(new_address) |
15 | - self.enabled = (old_address.blank? && !new_address.blank?) || (new_address && new_address != old_address) || false | |
15 | + self.enabled = (new_address && new_address != old_address) || (new_address && self.enabled) || false | |
16 | 16 | end |
17 | 17 | |
18 | 18 | settings_items :limit, :type => :integer | ... | ... |
app/models/folder.rb
... | ... | @@ -2,19 +2,10 @@ class Folder < Article |
2 | 2 | |
3 | 3 | acts_as_having_settings :field => :setting |
4 | 4 | |
5 | - settings_items :view_as, :type => :string, :default => 'folder' | |
6 | - | |
7 | 5 | xss_terminate :only => [ :body ], :with => 'white_list', :on => 'validation' |
8 | 6 | |
9 | - def self.select_views | |
10 | - [[_('Folder'), 'folder'], [_('Image gallery'), 'image_gallery']] | |
11 | - end | |
12 | - | |
13 | - def self.views | |
14 | - select_views.map(&:last) | |
15 | - end | |
16 | - | |
17 | - validates_inclusion_of :view_as, :in => self.views | |
7 | + include WhiteListFilter | |
8 | + filter_iframes :body, :whitelist => lambda { profile && profile.environment && profile.environment.trusted_sites_for_iframe } | |
18 | 9 | |
19 | 10 | def self.short_description |
20 | 11 | _('Folder') |
... | ... | @@ -24,37 +15,22 @@ class Folder < Article |
24 | 15 | _('A folder, inside which you can put other articles.') |
25 | 16 | end |
26 | 17 | |
27 | - def icon_name | |
18 | + def self.icon_name(article = nil) | |
28 | 19 | 'folder' |
29 | 20 | end |
30 | 21 | |
31 | - | |
22 | + include ActionView::Helpers::TagHelper | |
32 | 23 | def to_html(options = {}) |
33 | - send(view_as) | |
34 | - end | |
35 | - | |
36 | - def folder | |
37 | 24 | folder = self |
38 | 25 | lambda do |
39 | 26 | render :file => 'content_viewer/folder', :locals => { :folder => folder } |
40 | 27 | end |
41 | 28 | end |
42 | 29 | |
43 | - def image_gallery | |
44 | - article = self | |
45 | - lambda do | |
46 | - render :file => 'content_viewer/image_gallery', :locals => {:article => article} | |
47 | - end | |
48 | - end | |
49 | - | |
50 | 30 | def folder? |
51 | 31 | true |
52 | 32 | end |
53 | 33 | |
54 | - def display_as_gallery? | |
55 | - view_as == 'image_gallery' | |
56 | - end | |
57 | - | |
58 | 34 | def can_display_hits? |
59 | 35 | false |
60 | 36 | end |
... | ... | @@ -70,7 +46,5 @@ class Folder < Article |
70 | 46 | has_many :images, :class_name => 'Article', |
71 | 47 | :foreign_key => 'parent_id', |
72 | 48 | :order => 'articles.type, articles.name', |
73 | - :include => :reference_article, | |
74 | - :conditions => ["articles.type = 'UploadedFile' and articles.content_type in (?) or articles.type = 'Folder' or (articles.type = 'PublishedArticle' and reference_articles_articles.type = 'UploadedFile' and reference_articles_articles.content_type in (?))", UploadedFile.content_types, UploadedFile.content_types] | |
75 | - | |
49 | + :conditions => ["articles.type = 'UploadedFile' and articles.content_type in (?) or articles.type in ('Folder','Gallery')", UploadedFile.content_types] | |
76 | 50 | end | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +class Forum < Folder | |
2 | + | |
3 | + acts_as_having_posts :order => 'updated_at DESC' | |
4 | + | |
5 | + def self.short_description | |
6 | + _('Forum') | |
7 | + end | |
8 | + | |
9 | + def self.description | |
10 | + _('An internet forum, also called message board, where discussions can be held.') | |
11 | + end | |
12 | + | |
13 | + include ActionView::Helpers::TagHelper | |
14 | + def to_html(options = {}) | |
15 | + lambda do | |
16 | + render :file => 'content_viewer/forum_page' | |
17 | + end | |
18 | + end | |
19 | + | |
20 | + def forum? | |
21 | + true | |
22 | + end | |
23 | + | |
24 | + def self.icon_name(article = nil) | |
25 | + 'forum' | |
26 | + end | |
27 | +end | ... | ... |
app/models/friends_block.rb
... | ... | @@ -19,18 +19,8 @@ class FriendsBlock < ProfileListBlock |
19 | 19 | end |
20 | 20 | end |
21 | 21 | |
22 | - class FriendsBlock::Finder < ProfileListBlock::Finder | |
23 | - def ids | |
24 | - self.block.owner.friend_ids | |
25 | - end | |
26 | - end | |
27 | - | |
28 | - def profile_finder | |
29 | - @profile_finder ||= FriendsBlock::Finder.new(self) | |
30 | - end | |
31 | - | |
32 | - def profile_count | |
33 | - owner.friends.visible.count | |
22 | + def profiles | |
23 | + owner.friends | |
34 | 24 | end |
35 | 25 | |
36 | 26 | end | ... | ... |