Commit 94eaff0817c168e1f83a802d84ef9a9dfc3abb0c

Authored by Larissa Reis
2 parents 9372126a a7ef8536

Merge branch 'master' into stoa

Conflicts:
	Gemfile.lock
	script/development
Showing 761 changed files with 11720 additions and 56317 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 761 files displayed.

1 --ignore-dir=log 1 --ignore-dir=log
2 --ignore-dir=tmp 2 --ignore-dir=tmp
3 --ignore-dir=pkg 3 --ignore-dir=pkg
  4 +--ignore-dir=public/javascripts/cache
  5 +--ignore-dir=public/stylesheets/cache
@@ -198,6 +198,7 @@ Martín Olivera <molivera@solar.org.ar> @@ -198,6 +198,7 @@ Martín Olivera <molivera@solar.org.ar>
198 Moises Machado <moises@colivre.coop.br> 198 Moises Machado <moises@colivre.coop.br>
199 Naíla Alves <naila@colivre.coop.br> 199 Naíla Alves <naila@colivre.coop.br>
200 Nanda Lopes <nanda.listas+psl@gmail.com> 200 Nanda Lopes <nanda.listas+psl@gmail.com>
  201 +Parley Martins <parleypachecomartins@gmail.com>
201 Paulo Meirelles + Alessandro Palmeira + João M. M. da Silva <paulo@softwarelivre.org> 202 Paulo Meirelles + Alessandro Palmeira + João M. M. da Silva <paulo@softwarelivre.org>
202 Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org> 203 Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org>
203 Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org> 204 Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org>
1 source "https://rubygems.org" 1 source "https://rubygems.org"
2 -gem 'rails'  
3 -gem 'fast_gettext'  
4 -gem 'acts-as-taggable-on'  
5 -gem 'prototype-rails'  
6 -gem 'prototype_legacy_helper', '0.0.0', :path => 'vendor/prototype_legacy_helper'  
7 -gem 'rails_autolink'  
8 -gem 'pg'  
9 -gem 'rmagick'  
10 -gem 'RedCloth'  
11 -gem 'will_paginate'  
12 -gem 'ruby-feedparser'  
13 -gem 'daemons'  
14 -gem 'thin'  
15 -gem 'hpricot'  
16 -gem 'nokogiri' 2 +gem 'rails', '~> 3.2.19'
  3 +gem 'fast_gettext', '~> 0.6.8'
  4 +gem 'acts-as-taggable-on', '~> 3.0.2'
  5 +gem 'prototype-rails', '~> 3.2.1'
  6 +gem 'prototype_legacy_helper', '0.0.0', :path => 'vendor/prototype_legacy_helper'
  7 +gem 'rails_autolink', '~> 1.1.5'
  8 +gem 'pg', '~> 0.13.2'
  9 +gem 'rmagick', '~> 2.13.1'
  10 +gem 'RedCloth', '~> 4.2.9'
  11 +gem 'will_paginate', '~> 3.0.3'
  12 +gem 'ruby-feedparser', '~> 0.7'
  13 +gem 'daemons', '~> 1.1.5'
  14 +gem 'thin', '~> 1.3.1'
  15 +gem 'hpricot', '~> 0.8.6'
  16 +gem 'nokogiri', '~> 1.5.5'
17 gem 'rake', :require => false 17 gem 'rake', :require => false
18 -gem 'rest-client'  
19 -gem 'exception_notification'  
20 -gem 'gettext', :require => false, :group => :development 18 +gem 'rest-client', '~> 1.6.7'
  19 +gem 'exception_notification', '~> 4.0.1'
  20 +gem 'gettext', '~> 2.2.1', :require => false, :group => :development
  21 +gem 'locale', '~> 2.0.5'
21 22
22 gem 'whenever', :require => false 23 gem 'whenever', :require => false
23 24
@@ -25,25 +26,26 @@ gem &#39;whenever&#39;, :require =&gt; false @@ -25,25 +26,26 @@ gem &#39;whenever&#39;, :require =&gt; false
25 # with their GEM names (not the Debian package names) 26 # with their GEM names (not the Debian package names)
26 27
27 group :production do 28 group :production do
28 - gem 'dalli' 29 + gem 'dalli', '~> 2.7.0'
29 end 30 end
30 31
31 group :test do 32 group :test do
32 - gem 'rspec'  
33 - gem 'rspec-rails'  
34 - gem 'mocha', :require => false 33 + gem 'rspec', '~> 2.10.0'
  34 + gem 'rspec-rails', '~> 2.10.1'
  35 + gem 'mocha', '~> 1.1.0', :require => false
35 end 36 end
36 37
37 group :cucumber do 38 group :cucumber do
38 - gem 'cucumber-rails', :require => false  
39 - gem 'capybara'  
40 - gem 'cucumber'  
41 - gem 'database_cleaner'  
42 - gem 'selenium-webdriver' 39 + gem 'cucumber-rails', '~> 1.0.6', :require => false
  40 + gem 'capybara', '~> 2.1.0'
  41 + gem 'cucumber', '~> 1.0.6'
  42 + gem 'database_cleaner', '~> 1.2.0'
  43 + gem 'selenium-webdriver', '~> 2.39.0'
43 end 44 end
44 45
45 -# include plugin gemfiles  
46 -Dir.glob(File.join('config', 'plugins', '*')).each do |plugin|  
47 - plugin_gemfile = File.join(plugin, 'Gemfile')  
48 - eval File.read(plugin_gemfile) if File.exists?(plugin_gemfile) 46 +# include gemfiles from enabled plugins
  47 +# plugins in baseplugins/ are not included on purpose. They should not have any
  48 +# dependencies.
  49 +Dir.glob('config/plugins/*/Gemfile').each do |gemfile|
  50 + eval File.read(gemfile)
49 end 51 end
Gemfile.lock
@@ -1,200 +0,0 @@ @@ -1,200 +0,0 @@
1 -PATH  
2 - remote: vendor/prototype_legacy_helper  
3 - specs:  
4 - prototype_legacy_helper (0.0.0)  
5 -  
6 -GEM  
7 - remote: https://rubygems.org/  
8 - specs:  
9 - RedCloth (4.2.9)  
10 - actionmailer (3.2.6)  
11 - actionpack (= 3.2.6)  
12 - mail (~> 2.4.4)  
13 - actionpack (3.2.6)  
14 - activemodel (= 3.2.6)  
15 - activesupport (= 3.2.6)  
16 - builder (~> 3.0.0)  
17 - erubis (~> 2.7.0)  
18 - journey (~> 1.0.1)  
19 - rack (~> 1.4.0)  
20 - rack-cache (~> 1.2)  
21 - rack-test (~> 0.6.1)  
22 - sprockets (~> 2.1.3)  
23 - activemodel (3.2.6)  
24 - activesupport (= 3.2.6)  
25 - builder (~> 3.0.0)  
26 - activerecord (3.2.6)  
27 - activemodel (= 3.2.6)  
28 - activesupport (= 3.2.6)  
29 - arel (~> 3.0.2)  
30 - tzinfo (~> 0.3.29)  
31 - activeresource (3.2.6)  
32 - activemodel (= 3.2.6)  
33 - activesupport (= 3.2.6)  
34 - activesupport (3.2.6)  
35 - i18n (~> 0.6)  
36 - multi_json (~> 1.0)  
37 - acts-as-taggable-on (3.0.2)  
38 - rails (>= 3, < 5)  
39 - arel (3.0.2)  
40 - builder (3.0.0)  
41 - capybara (2.1.0)  
42 - mime-types (>= 1.16)  
43 - nokogiri (>= 1.3.3)  
44 - rack (>= 1.0.0)  
45 - rack-test (>= 0.5.4)  
46 - xpath (~> 2.0)  
47 - childprocess (0.3.3)  
48 - ffi (~> 1.0.6)  
49 - chronic (0.10.2)  
50 - cucumber (1.0.6)  
51 - builder (>= 2.1.2)  
52 - diff-lcs (>= 1.1.2)  
53 - gherkin (~> 2.4.18)  
54 - json (>= 1.4.6)  
55 - term-ansicolor (>= 1.0.6)  
56 - cucumber-rails (1.0.6)  
57 - capybara (>= 1.1.1)  
58 - cucumber (>= 1.0.6)  
59 - nokogiri (>= 1.5.0)  
60 - daemons (1.1.5)  
61 - dalli (2.7.0)  
62 - database_cleaner (1.2.0)  
63 - diff-lcs (1.1.3)  
64 - erubis (2.7.0)  
65 - eventmachine (0.12.10)  
66 - exception_notification (4.0.1)  
67 - actionmailer (>= 3.0.4)  
68 - activesupport (>= 3.0.4)  
69 - fast_gettext (0.6.8)  
70 - ffi (1.0.11)  
71 - gettext (2.2.1)  
72 - locale  
73 - gherkin (2.4.21)  
74 - json (>= 1.4.6)  
75 - hike (1.2.1)  
76 - hpricot (0.8.6)  
77 - i18n (0.6.0)  
78 - journey (1.0.3)  
79 - json (1.7.3)  
80 - locale (2.0.5)  
81 - mail (2.4.4)  
82 - i18n (>= 0.4.0)  
83 - mime-types (~> 1.16)  
84 - treetop (~> 1.4.8)  
85 - metaclass (0.0.1)  
86 - mime-types (1.19)  
87 - mocha (0.11.3)  
88 - metaclass (~> 0.0.1)  
89 - multi_json (1.3.6)  
90 - nokogiri (1.5.5)  
91 - pg (0.13.2)  
92 - polyglot (0.3.3)  
93 - prototype-rails (3.2.1)  
94 - rails (~> 3.2)  
95 - rack (1.4.1)  
96 - rack-cache (1.2)  
97 - rack (>= 0.4)  
98 - rack-ssl (1.3.2)  
99 - rack  
100 - rack-test (0.6.1)  
101 - rack (>= 1.0)  
102 - rails (3.2.6)  
103 - actionmailer (= 3.2.6)  
104 - actionpack (= 3.2.6)  
105 - activerecord (= 3.2.6)  
106 - activeresource (= 3.2.6)  
107 - activesupport (= 3.2.6)  
108 - bundler (~> 1.0)  
109 - railties (= 3.2.6)  
110 - rails_autolink (1.1.5)  
111 - rails (> 3.1)  
112 - railties (3.2.6)  
113 - actionpack (= 3.2.6)  
114 - activesupport (= 3.2.6)  
115 - rack-ssl (~> 1.3.2)  
116 - rake (>= 0.8.7)  
117 - rdoc (~> 3.4)  
118 - thor (>= 0.14.6, < 2.0)  
119 - rake (0.9.2.2)  
120 - rdoc (3.9.4)  
121 - rest-client (1.6.7)  
122 - mime-types (>= 1.16)  
123 - rmagick (2.13.1)  
124 - rspec (2.10.0)  
125 - rspec-core (~> 2.10.0)  
126 - rspec-expectations (~> 2.10.0)  
127 - rspec-mocks (~> 2.10.0)  
128 - rspec-core (2.10.1)  
129 - rspec-expectations (2.10.0)  
130 - diff-lcs (~> 1.1.3)  
131 - rspec-mocks (2.10.1)  
132 - rspec-rails (2.10.1)  
133 - actionpack (>= 3.0)  
134 - activesupport (>= 3.0)  
135 - railties (>= 3.0)  
136 - rspec (~> 2.10.0)  
137 - ruby-feedparser (0.7)  
138 - rubyzip (1.1.2)  
139 - selenium-webdriver (2.39.0)  
140 - childprocess (>= 0.2.5)  
141 - multi_json (~> 1.0)  
142 - rubyzip (~> 1.0)  
143 - websocket (~> 1.0.4)  
144 - sprockets (2.1.3)  
145 - hike (~> 1.2)  
146 - multi_json (~> 1.0)  
147 - rack (~> 1.0)  
148 - tilt (~> 1.1, != 1.3.0)  
149 - term-ansicolor (1.0.7)  
150 - thin (1.3.1)  
151 - daemons (>= 1.0.9)  
152 - eventmachine (>= 0.12.6)  
153 - rack (>= 1.0.0)  
154 - thor (0.15.3)  
155 - tilt (1.3.3)  
156 - treetop (1.4.10)  
157 - polyglot  
158 - polyglot (>= 0.3.1)  
159 - tzinfo (0.3.33)  
160 - websocket (1.0.7)  
161 - whenever (0.9.2)  
162 - activesupport (>= 2.3.4)  
163 - chronic (>= 0.6.3)  
164 - will_paginate (3.0.3)  
165 - xpath (2.0.0)  
166 - nokogiri (~> 1.3)  
167 -  
168 -PLATFORMS  
169 - ruby  
170 -  
171 -DEPENDENCIES  
172 - RedCloth  
173 - acts-as-taggable-on  
174 - capybara  
175 - cucumber  
176 - cucumber-rails  
177 - daemons  
178 - dalli  
179 - database_cleaner  
180 - exception_notification  
181 - fast_gettext  
182 - gettext  
183 - hpricot  
184 - mocha  
185 - nokogiri  
186 - pg  
187 - prototype-rails  
188 - prototype_legacy_helper (= 0.0.0)!  
189 - rails  
190 - rails_autolink  
191 - rake  
192 - rest-client  
193 - rmagick  
194 - rspec  
195 - rspec-rails  
196 - ruby-feedparser  
197 - selenium-webdriver  
198 - thin  
199 - whenever  
200 - will_paginate  
@@ -186,8 +186,8 @@ Apache instalation @@ -186,8 +186,8 @@ Apache instalation
186 186
187 # apt-get install apache2 187 # apt-get install apache2
188 188
189 -Apache configuration  
190 --------------------- 189 +Configuration - noosfero at /
  190 +-----------------------------
191 191
192 First you have to enable the following some apache modules: 192 First you have to enable the following some apache modules:
193 193
@@ -257,6 +257,62 @@ Now restart your apache server (as root): @@ -257,6 +257,62 @@ Now restart your apache server (as root):
257 257
258 # invoke-rc.d apache2 restart 258 # invoke-rc.d apache2 restart
259 259
  260 +Configuration - noosfero at a /subdirectory
  261 +-------------------------------------------
  262 +
  263 +This section describes how to configure noosfero at a subdirectory, what is
  264 +specially useful when you want Noosfero to share a domain name with other
  265 +applications. For example you can host noosfero at yourdomain.com/social, a
  266 +webmail application at yourdomain.com/webmail, and have a static HTML website
  267 +at yourdomain.com/.
  268 +
  269 +**NOTE:** Some plugins might not work well with this setting. Before deploying
  270 +this setting, make sure you test that everything you need works properly with
  271 +it.
  272 +
  273 +The configuration is similar to the main configuration instructions, except for
  274 +the following points. In the description below, replace '/subdirectory' with
  275 +the actual subdirectory you want.
  276 +
  277 +1) add a `prefix: /subdirectory` line to your thin configuration file (thin.yml).
  278 +
  279 +1.1) remember to restart the noosfero application server whenever you make
  280 +changes to that configuration file.
  281 +
  282 + # service noosfero restart
  283 +
  284 +2) add a line saying `export RAILS_RELATIVE_URL_ROOT=/subdirectory` to
  285 +/etc/default/noosfero (you can create it with just this line if it does not
  286 +exist already).
  287 +
  288 +3) You should add the following apache configuration to an existing virtual
  289 +host (plus the `<Proxy balancer://noosfero>` section as displayed above):
  290 +
  291 +```
  292 +Alias /subdirectory /path/to/noosfero/public
  293 +<Directory "/path/to/noosfero/public">
  294 + Options FollowSymLinks
  295 + AllowOverride None
  296 + Order Allow,Deny
  297 + Allow from all
  298 +
  299 + Include /path/to/noosfero/etc/noosfero/apache/cache.conf
  300 +
  301 + RewriteEngine On
  302 + RewriteBase /subdirectory
  303 + # Rewrite index to check for static index.html
  304 + RewriteRule ^$ index.html [QSA]
  305 + # Rewrite to check for Rails cached page
  306 + RewriteRule ^([^.]+)$ $1.html [QSA]
  307 + RewriteCond %{REQUEST_FILENAME} !-f
  308 + RewriteRule ^(.*)$ http://localhost:3000%{REQUEST_URI} [P,QSA,L]
  309 +</Directory>
  310 +```
  311 +
  312 +3.1) remember to reload the apache server whenever any apache configuration
  313 +file changes.
  314 +
  315 + # sudo service apache2 reload
260 316
261 Enabling exception notifications 317 Enabling exception notifications
262 ================================ 318 ================================
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 3
4 VAGRANTFILE_API_VERSION = "2" 4 VAGRANTFILE_API_VERSION = "2"
5 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 5 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
6 - config.vm.box = "debian-wheezy" 6 + config.vm.box = ENV.fetch('VAGRANT_BOX', "debian-wheezy")
7 config.vm.network :forwarded_port, host: 3000, guest: 3000 7 config.vm.network :forwarded_port, host: 3000, guest: 3000
8 config.vm.provision :shell do |shell| 8 config.vm.provision :shell do |shell|
9 shell.inline = 'su vagrant -c /vagrant/script/vagrant' 9 shell.inline = 'su vagrant -c /vagrant/script/vagrant'
app/controllers/public/account_controller.rb
@@ -99,6 +99,7 @@ class AccountController &lt; ApplicationController @@ -99,6 +99,7 @@ class AccountController &lt; ApplicationController
99 @user.return_to = session[:return_to] 99 @user.return_to = session[:return_to]
100 @person = Person.new(params[:profile_data]) 100 @person = Person.new(params[:profile_data])
101 @person.environment = @user.environment 101 @person.environment = @user.environment
  102 +
102 if request.post? 103 if request.post?
103 if may_be_a_bot 104 if may_be_a_bot
104 set_signup_start_time_for_now 105 set_signup_start_time_for_now
@@ -117,6 +118,14 @@ class AccountController &lt; ApplicationController @@ -117,6 +118,14 @@ class AccountController &lt; ApplicationController
117 invitation.update_attributes!({:friend => @user.person}) 118 invitation.update_attributes!({:friend => @user.person})
118 invitation.finish 119 invitation.finish
119 end 120 end
  121 +
  122 + unless params[:file].nil?
  123 + image = Image::new :uploaded_data=> params[:file][:image]
  124 +
  125 + @user.person.image = image
  126 + @user.person.save
  127 + end
  128 +
120 if @user.activated? 129 if @user.activated?
121 self.current_user = @user 130 self.current_user = @user
122 check_join_in_community(@user) 131 check_join_in_community(@user)
@@ -186,7 +195,7 @@ class AccountController &lt; ApplicationController @@ -186,7 +195,7 @@ class AccountController &lt; ApplicationController
186 else 195 else
187 @change_password.errors[:base] << _('Could not find any user with %s equal to "%s".') % [fields_label, params[:value]] 196 @change_password.errors[:base] << _('Could not find any user with %s equal to "%s".') % [fields_label, params[:value]]
188 end 197 end
189 - rescue ActiveRecord::RecordInvald 198 + rescue ActiveRecord::RecordInvalid
190 @change_password.errors[:base] << _('Could not perform password recovery for the user.') 199 @change_password.errors[:base] << _('Could not perform password recovery for the user.')
191 end 200 end
192 end 201 end
app/controllers/public/content_viewer_controller.rb
@@ -126,7 +126,7 @@ class ContentViewerController &lt; ApplicationController @@ -126,7 +126,7 @@ class ContentViewerController &lt; ApplicationController
126 elsif !@page.display_to?(user) 126 elsif !@page.display_to?(user)
127 if !profile.public? 127 if !profile.public?
128 private_profile_partial_parameters 128 private_profile_partial_parameters
129 - render :template => 'profile/_private_profile', :status => 403 129 + render :template => 'profile/_private_profile', :status => 403, :formats => [:html]
130 allowed = false 130 allowed = false
131 else #if !profile.visible? 131 else #if !profile.visible?
132 render_access_denied 132 render_access_denied
app/controllers/public/profile_controller.rb
@@ -17,7 +17,11 @@ class ProfileController &lt; PublicController @@ -17,7 +17,11 @@ class ProfileController &lt; PublicController
17 end 17 end
18 @tags = profile.article_tags 18 @tags = profile.article_tags
19 unless profile.display_info_to?(user) 19 unless profile.display_info_to?(user)
20 - profile.visible? ? private_profile : invisible_profile 20 + if profile.visible?
  21 + private_profile
  22 + else
  23 + invisible_profile
  24 + end
21 end 25 end
22 end 26 end
23 27
@@ -315,7 +319,7 @@ class ProfileController &lt; PublicController @@ -315,7 +319,7 @@ class ProfileController &lt; PublicController
315 abuse_report = AbuseReport.new(params[:abuse_report]) 319 abuse_report = AbuseReport.new(params[:abuse_report])
316 if !params[:content_type].blank? 320 if !params[:content_type].blank?
317 article = params[:content_type].constantize.find(params[:content_id]) 321 article = params[:content_type].constantize.find(params[:content_id])
318 - abuse_report.content = instance_eval(&article.reported_version) 322 + abuse_report.content = article_reported_version(article)
319 end 323 end
320 324
321 user.register_report(abuse_report, profile) 325 user.register_report(abuse_report, profile)
@@ -394,6 +398,7 @@ class ProfileController &lt; PublicController @@ -394,6 +398,7 @@ class ProfileController &lt; PublicController
394 398
395 def private_profile 399 def private_profile
396 private_profile_partial_parameters 400 private_profile_partial_parameters
  401 + render :action => 'index', :status => 403
397 end 402 end
398 403
399 def invisible_profile 404 def invisible_profile
app/controllers/public/search_controller.rb
@@ -90,10 +90,14 @@ class SearchController &lt; PublicController @@ -90,10 +90,14 @@ class SearchController &lt; PublicController
90 end 90 end
91 91
92 def events 92 def events
93 - year = (params[:year] ? params[:year].to_i : Date.today.year)  
94 - month = (params[:month] ? params[:month].to_i : Date.today.month)  
95 - day = (params[:day] ? params[:day].to_i : Date.today.day)  
96 - @date = build_date(year, month, day) 93 + if params[:year].blank? && params[:year].blank? && params[:day].blank?
  94 + @date = Date.today
  95 + else
  96 + year = (params[:year] ? params[:year].to_i : Date.today.year)
  97 + month = (params[:month] ? params[:month].to_i : Date.today.month)
  98 + day = (params[:day] ? params[:day].to_i : 1)
  99 + @date = build_date(year, month, day)
  100 + end
97 date_range = (@date - 1.month).at_beginning_of_month..(@date + 1.month).at_end_of_month 101 date_range = (@date - 1.month).at_beginning_of_month..(@date + 1.month).at_end_of_month
98 102
99 @events = [] 103 @events = []
app/helpers/application_helper.rb
@@ -304,7 +304,7 @@ module ApplicationHelper @@ -304,7 +304,7 @@ module ApplicationHelper
304 def partial_for_class(klass, prefix=nil, suffix=nil) 304 def partial_for_class(klass, prefix=nil, suffix=nil)
305 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil? 305 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil?
306 name = klass.name.underscore 306 name = klass.name.underscore
307 - controller.view_paths.reverse_each do |view_path| 307 + controller.view_paths.each do |view_path|
308 partial = partial_for_class_in_view_path(klass, view_path, prefix, suffix) 308 partial = partial_for_class_in_view_path(klass, view_path, prefix, suffix)
309 return partial if partial 309 return partial if partial
310 end 310 end
@@ -1388,7 +1388,7 @@ module ApplicationHelper @@ -1388,7 +1388,7 @@ module ApplicationHelper
1388 # are old things that do not support it we are keeping this hot spot. 1388 # are old things that do not support it we are keeping this hot spot.
1389 html = @plugins.pipeline(:parse_content, html, source).first 1389 html = @plugins.pipeline(:parse_content, html, source).first
1390 end 1390 end
1391 - html 1391 + html && html.html_safe
1392 end 1392 end
1393 1393
1394 def convert_macro(html, source) 1394 def convert_macro(html, source)
app/helpers/article_helper.rb
@@ -3,6 +3,12 @@ module ArticleHelper @@ -3,6 +3,12 @@ module ArticleHelper
3 include PrototypeHelper 3 include PrototypeHelper
4 include TokenHelper 4 include TokenHelper
5 5
  6 + def article_reported_version(article)
  7 + search_path = Rails.root.join('app', 'views', 'shared', 'reported_versions')
  8 + partial_path = File.join('shared', 'reported_versions', 'profile', partial_for_class_in_view_path(article.class, search_path))
  9 + render_to_string(:partial => partial_path, :locals => {:article => article})
  10 + end
  11 +
6 def custom_options_for_article(article, tokenized_children) 12 def custom_options_for_article(article, tokenized_children)
7 @article = article 13 @article = article
8 14
app/helpers/profile_helper.rb
1 module ProfileHelper 1 module ProfileHelper
2 2
3 - def display_field(title, profile, field, force = false) 3 + COMMON_CATEGORIES = ActiveSupport::OrderedHash.new
  4 + COMMON_CATEGORIES[:content] = [:blogs, :image_galleries, :events, :article_tags]
  5 + COMMON_CATEGORIES[:interests] = [:interests]
  6 + COMMON_CATEGORIES[:general] = nil
  7 +
  8 + PERSON_CATEGORIES = ActiveSupport::OrderedHash.new
  9 + PERSON_CATEGORIES[:basic_information] = [:nickname, :sex, :birth_date, :location, :privacy_setting, :created_at]
  10 + PERSON_CATEGORIES[:contact] = [:contact_phone, :cell_phone, :comercial_phone, :contact_information, :email, :personal_website, :jabber_id]
  11 + PERSON_CATEGORIES[:location] = [:address, :address_reference, :zip_code, :city, :state, :district, :country, :nationality]
  12 + PERSON_CATEGORIES[:work] = [:organization, :organization_website, :professional_activity]
  13 + PERSON_CATEGORIES[:study] = [:schooling, :formation, :area_of_study]
  14 + PERSON_CATEGORIES[:network] = [:friends, :communities, :enterprises]
  15 + PERSON_CATEGORIES.merge!(COMMON_CATEGORIES)
  16 +
  17 + ORGANIZATION_CATEGORIES = ActiveSupport::OrderedHash.new
  18 + ORGANIZATION_CATEGORIES[:basic_information] = [:display_name, :created_at, :foundation_year, :type, :language, :members_count, :location, :address_reference, :historic_and_current_context, :admins]
  19 + ORGANIZATION_CATEGORIES[:contact] = [:contact_person, :contact_phone, :contact_email, :organization_website, :jabber_id]
  20 + ORGANIZATION_CATEGORIES[:economic] = [:business_name, :acronym, :economic_activity, :legal_form, :products, :activities_short_description, :management_information]
  21 + ORGANIZATION_CATEGORIES.merge!(COMMON_CATEGORIES)
  22 +
  23 + CATEGORY_MAP = ActiveSupport::OrderedHash.new
  24 + CATEGORY_MAP[:person] = PERSON_CATEGORIES
  25 + CATEGORY_MAP[:organization] = ORGANIZATION_CATEGORIES
  26 +
  27 + FORCE = {
  28 + :person => [:privacy_setting],
  29 + :organization => [:privacy_setting, :location],
  30 + }
  31 +
  32 + MULTIPLE = {
  33 + :person => [:blogs, :image_galleries, :interests],
  34 + :organization => [:blogs, :image_galleries, :interests],
  35 + }
  36 +
  37 + CUSTOM_LABELS = {
  38 + :zip_code => _('ZIP code'),
  39 + :email => _('e-Mail'),
  40 + :jabber_id => _('Jabber'),
  41 + :birth_date => _('Date of birth'),
  42 + :created_at => _('Profile created at'),
  43 + :members_count => _('Members'),
  44 + :privacy_setting => _('Privacy setting'),
  45 + :article_tags => _('Tags')
  46 + }
  47 +
  48 + EXCEPTION = {
  49 + :person => [:image, :preferred_domain, :description, :tag_list],
  50 + :organization => [:image, :preferred_domain, :description, :tag_list, :address, :zip_code, :city, :state, :country, :district]
  51 + }
  52 +
  53 + def general_fields
  54 + categorized_fields = CATEGORY_MAP[kind].values.flatten
  55 + profile.class.fields.map(&:to_sym) - categorized_fields - EXCEPTION[kind]
  56 + end
  57 +
  58 + def kind
  59 + if profile.kind_of?(Person)
  60 + :person
  61 + else
  62 + :organization
  63 + end
  64 + end
  65 +
  66 + def title(field, entry = nil)
  67 + return self.send("#{field}_custom_title", entry) if MULTIPLE[kind].include?(field) && entry.present?
  68 + CUSTOM_LABELS[field.to_sym] || _(field.to_s.humanize)
  69 + end
  70 +
  71 + def display_field(field)
  72 + force = FORCE[kind].include?(field)
  73 + multiple = MULTIPLE[kind].include?(field)
4 unless force || profile.may_display_field_to?(field, user) 74 unless force || profile.may_display_field_to?(field, user)
5 return '' 75 return ''
6 end 76 end
7 - value = profile.send(field)  
8 - if !value.blank?  
9 - if block_given?  
10 - value = yield(value)  
11 - end  
12 - content_tag('tr', content_tag('td', title, :class => 'field-name') + content_tag('td', value)) 77 + value = begin profile.send(field) rescue nil end
  78 + return '' if value.blank?
  79 + if value.kind_of?(Hash)
  80 + content = self.send("treat_#{field}", value)
  81 + content_tag('tr', content_tag('td', title(field), :class => 'field-name') + content_tag('td', content))
13 else 82 else
14 - '' 83 + entries = multiple ? value : [] << value
  84 + entries.map do |entry|
  85 + content = self.send("treat_#{field}", entry)
  86 + unless content.blank?
  87 + content_tag('tr', content_tag('td', title(field, entry), :class => 'field-name') + content_tag('td', content))
  88 + end
  89 + end.join("\n")
15 end 90 end
16 end 91 end
17 92
18 - def display_contact(profile)  
19 - fields = []  
20 - fields << display_field(_('Address:'), profile, :address).html_safe  
21 - fields << display_field(_('ZIP code:'), profile, :zip_code).html_safe  
22 - fields << display_field(_('Contact phone:'), profile, :contact_phone).html_safe  
23 - fields << display_field(_('e-Mail:'), profile, :email) { |email| link_to_email(email) }.html_safe  
24 - fields << display_field(_('Personal website:'), profile, :personal_website).html_safe  
25 - fields << display_field(_('Jabber:'), profile, :jabber_id).html_safe  
26 - if fields.reject!(&:blank?).empty?  
27 - ''  
28 - else  
29 - content_tag('tr', content_tag('th', _('Contact'), { :colspan => 2 })) + fields.join.html_safe 93 + def treat_email(email)
  94 + link_to_email(email)
  95 + end
  96 +
  97 + def treat_organization_website(url)
  98 + link_to(url, url)
  99 + end
  100 +
  101 + def treat_sex(gender)
  102 + { 'male' => _('Male'), 'female' => _('Female') }[gender]
  103 + end
  104 +
  105 + def treat_date(date)
  106 + show_date(date.to_date)
  107 + end
  108 + alias :treat_birth_date :treat_date
  109 + alias :treat_created_at :treat_date
  110 +
  111 + def treat_friends(friends)
  112 + link_to friends.count, :controller => 'profile', :action => 'friends'
  113 + end
  114 +
  115 + def treat_communities(communities)
  116 + link_to communities.count, :controller => "profile", :action => 'communities'
  117 + end
  118 +
  119 + def treat_enterprises(enterprises)
  120 + if environment.disabled?('disable_asset_enterprises')
  121 + link_to enterprises.count, :controller => "profile", :action => 'enterprises'
  122 + end
  123 + end
  124 +
  125 + def treat_members_count(count)
  126 + link_to count, :controller => 'profile', :action => 'members'
  127 + end
  128 +
  129 + def treat_products(products)
  130 + if profile.kind_of?(Enterprise) && profile.environment.enabled?('products_for_enterprises')
  131 + link_to _('Products/Services'), :controller => 'catalog', :action => 'index'
30 end 132 end
31 end 133 end
32 134
33 - def display_work_info(profile)  
34 - organization = display_field(_('Organization:'), profile, :organization)  
35 - organization_site = display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }  
36 - if organization.blank? && organization_site.blank?  
37 - '' 135 + def treat_admins(admins)
  136 + profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ')
  137 + end
  138 +
  139 + def treat_blogs(blog)
  140 + link_to(n_('One post', '%{num} posts', blog.posts.published.count) % { :num => blog.posts.published.count }, blog.url)
  141 + end
  142 +
  143 + def treat_image_galleries(gallery)
  144 + link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url)
  145 + end
  146 +
  147 + def treat_events(events)
  148 + link_to events.published.count, :controller => 'events', :action => 'events'
  149 + end
  150 +
  151 + def treat_article_tags(tags)
  152 + tag_cloud @tags, :id, { :action => 'tags' }, :max_size => 18, :min_size => 10
  153 + end
  154 +
  155 + def treat_interests(interest)
  156 + link_to interest.name, :controller => 'search', :action => 'category_index', :category_path => interest.explode_path
  157 + end
  158 +
  159 + def article_custom_title(article)
  160 + article.name
  161 + end
  162 + alias :blogs_custom_title :article_custom_title
  163 + alias :image_galleries_custom_title :article_custom_title
  164 +
  165 + def interests_custom_title(interest)
  166 + ''
  167 + end
  168 +
  169 + def method_missing(method, *args, &block)
  170 + if method.to_s =~ /^treat_(.+)$/
  171 + args[0]
  172 + elsif method.to_s =~ /^display_(.+)$/ && CATEGORY_MAP[kind].has_key?($1.to_sym)
  173 + category = $1.to_sym
  174 + fields = category == :general ? general_fields : CATEGORY_MAP[kind][category]
  175 + contents = []
  176 +
  177 + fields.each do |field|
  178 + contents << display_field(field).html_safe
  179 + end
  180 +
  181 + contents = contents.delete_if(&:blank?)
  182 +
  183 + unless contents.empty?
  184 + content_tag('tr', content_tag('th', title(category), { :colspan => 2 })) + contents.join.html_safe
  185 + else
  186 + ''
  187 + end
38 else 188 else
39 - content_tag('tr', content_tag('th', _('Work'), { :colspan => 2 })) + organization + organization_site 189 + super
40 end 190 end
41 end 191 end
42 192
app/helpers/sweeper_helper.rb
@@ -56,12 +56,12 @@ module SweeperHelper @@ -56,12 +56,12 @@ module SweeperHelper
56 if profile 56 if profile
57 profile.blocks.each {|block| 57 profile.blocks.each {|block|
58 conditions = block.class.expire_on 58 conditions = block.class.expire_on
59 - blocks_to_expire << block unless (conditions[:profile] & causes).empty? 59 + blocks_to_expire << block unless (conditions[:profile] & causes).blank?
60 } 60 }
61 end 61 end
62 environment.blocks.each {|block| 62 environment.blocks.each {|block|
63 conditions = block.class.expire_on 63 conditions = block.class.expire_on
64 - blocks_to_expire << block unless (conditions[:environment] & causes).empty? 64 + blocks_to_expire << block unless (conditions[:environment] & causes).blank?
65 } 65 }
66 66
67 blocks_to_expire.uniq! 67 blocks_to_expire.uniq!
app/helpers/tinymce_helper.rb 0 → 100644
@@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
  1 +module TinymceHelper
  2 + include MacrosHelper
  3 +
  4 + def tinymce_js
  5 + output = ''
  6 + output += javascript_include_tag 'tinymce/js/tinymce/tinymce.min.js'
  7 + output += javascript_include_tag 'tinymce/js/tinymce/jquery.tinymce.min.js'
  8 + output += javascript_include_tag 'tinymce.js'
  9 + output += include_macro_js_files.to_s
  10 + output
  11 + end
  12 +
  13 + def tinymce_init_js options = {}
  14 + options.merge! :document_base_url => environment.top_url,
  15 + :content_css => "/stylesheets/tinymce.css,#{macro_css_files}",
  16 + :plugins => %w[compat3x advlist autolink lists link image charmap print preview hr anchor pagebreak
  17 + searchreplace wordcount visualblocks visualchars code fullscreen
  18 + insertdatetime media nonbreaking save table contextmenu directionality
  19 + emoticons template paste textcolor colorpicker textpattern],
  20 + :language => tinymce_language
  21 +
  22 + options[:toolbar1] = "insertfile undo redo | copy paste | bold italic underline | styleselect fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
  23 + if options[:mode] == 'simple'
  24 + options[:menubar] = false
  25 + else
  26 + options[:menubar] = 'edit insert view tools'
  27 + options[:toolbar2] = 'print preview code media | table'
  28 +
  29 + options[:toolbar2] += ' | macros'
  30 + macros_with_buttons.each do |macro|
  31 + options[:toolbar2] += " #{macro.identifier}"
  32 + end
  33 + end
  34 +
  35 + options[:macros_setup] = macros_with_buttons.map do |macro|
  36 + <<-EOS
  37 + ed.addButton('#{macro.identifier}', {
  38 + title: #{macro_title(macro).to_json},
  39 + onclick: #{generate_macro_config_dialog macro},
  40 + image : '#{macro.configuration[:icon_path]}'
  41 + });
  42 + EOS
  43 + end
  44 +
  45 + #cleanup non tinymce options
  46 + options = options.except :mode
  47 +
  48 + "noosfero.tinymce.init(#{options.to_json})"
  49 + end
  50 +
  51 +end
app/models/article.rb
@@ -282,13 +282,6 @@ class Article &lt; ActiveRecord::Base @@ -282,13 +282,6 @@ class Article &lt; ActiveRecord::Base
282 end 282 end
283 end 283 end
284 284
285 - def reported_version(options = {})  
286 - article = self  
287 - search_path = Rails.root.join('app', 'views', 'shared', 'reported_versions')  
288 - partial_path = File.join('shared', 'reported_versions', partial_for_class_in_view_path(article.class, search_path))  
289 - lambda { render_to_string(:partial => partial_path, :locals => {:article => article}) }  
290 - end  
291 -  
292 # returns the data of the article. Must be overriden in each subclass to 285 # returns the data of the article. Must be overriden in each subclass to
293 # provide the correct content for the article. 286 # provide the correct content for the article.
294 def data 287 def data
app/models/block.rb
@@ -192,7 +192,7 @@ class Block &lt; ActiveRecord::Base @@ -192,7 +192,7 @@ class Block &lt; ActiveRecord::Base
192 192
193 # Override in your subclasses. 193 # Override in your subclasses.
194 # Define which events and context should cause the block cache to expire 194 # Define which events and context should cause the block cache to expire
195 - # Possible events are: :article, :profile, :friendship, :category 195 + # Possible events are: :article, :profile, :friendship, :category, :role_assignment
196 # Possible contexts are: :profile, :environment 196 # Possible contexts are: :profile, :environment
197 def self.expire_on 197 def self.expire_on
198 { 198 {
@@ -234,4 +234,9 @@ class Block &lt; ActiveRecord::Base @@ -234,4 +234,9 @@ class Block &lt; ActiveRecord::Base
234 duplicated_block 234 duplicated_block
235 end 235 end
236 236
  237 + def copy_from(block)
  238 + self.settings = block.settings
  239 + self.position = block.position
  240 + end
  241 +
237 end 242 end
app/models/community.rb
@@ -50,16 +50,6 @@ class Community &lt; Organization @@ -50,16 +50,6 @@ class Community &lt; Organization
50 super + FIELDS 50 super + FIELDS
51 end 51 end
52 52
53 - validate :presence_of_required_fieds  
54 -  
55 - def presence_of_required_fieds  
56 - self.required_fields.each do |field|  
57 - if self.send(field).blank?  
58 - self.errors.add_on_blank(field)  
59 - end  
60 - end  
61 - end  
62 -  
63 def active_fields 53 def active_fields
64 environment ? environment.active_community_fields : [] 54 environment ? environment.active_community_fields : []
65 end 55 end
app/models/enterprise.rb
@@ -62,16 +62,6 @@ class Enterprise &lt; Organization @@ -62,16 +62,6 @@ class Enterprise &lt; Organization
62 super + FIELDS 62 super + FIELDS
63 end 63 end
64 64
65 - validate :presence_of_required_fieds  
66 -  
67 - def presence_of_required_fieds  
68 - self.required_fields.each do |field|  
69 - if self.send(field).blank?  
70 - self.errors.add_on_blank(field)  
71 - end  
72 - end  
73 - end  
74 -  
75 def active_fields 65 def active_fields
76 environment ? environment.active_enterprise_fields : [] 66 environment ? environment.active_enterprise_fields : []
77 end 67 end
app/models/environment.rb
@@ -662,6 +662,7 @@ class Environment &lt; ActiveRecord::Base @@ -662,6 +662,7 @@ class Environment &lt; ActiveRecord::Base
662 url = 'http://' 662 url = 'http://'
663 url << (Noosfero.url_options.key?(:host) ? Noosfero.url_options[:host] : default_hostname) 663 url << (Noosfero.url_options.key?(:host) ? Noosfero.url_options[:host] : default_hostname)
664 url << ':' << Noosfero.url_options[:port].to_s if Noosfero.url_options.key?(:port) 664 url << ':' << Noosfero.url_options[:port].to_s if Noosfero.url_options.key?(:port)
  665 + url << Noosfero.root('')
665 url 666 url
666 end 667 end
667 668
@@ -806,7 +807,7 @@ class Environment &lt; ActiveRecord::Base @@ -806,7 +807,7 @@ class Environment &lt; ActiveRecord::Base
806 end 807 end
807 808
808 def notification_emails 809 def notification_emails
809 - [noreply_email.blank? ? nil : noreply_email].compact + admins.map(&:email) 810 + [contact_email].select(&:present?) + admins.map(&:email)
810 end 811 end
811 812
812 after_create :create_templates 813 after_create :create_templates
app/models/external_feed.rb
@@ -13,6 +13,7 @@ class ExternalFeed &lt; ActiveRecord::Base @@ -13,6 +13,7 @@ class ExternalFeed &lt; ActiveRecord::Base
13 attr_accessible :address, :enabled 13 attr_accessible :address, :enabled
14 14
15 def add_item(title, link, date, content) 15 def add_item(title, link, date, content)
  16 + return if content.blank?
16 doc = Hpricot(content) 17 doc = Hpricot(content)
17 doc.search('*').each do |p| 18 doc.search('*').each do |p|
18 if p.instance_of? Hpricot::Elem 19 if p.instance_of? Hpricot::Elem
app/models/link_list_block.rb
@@ -78,8 +78,13 @@ class LinkListBlock &lt; Block @@ -78,8 +78,13 @@ class LinkListBlock &lt; Block
78 address 78 address
79 end 79 end
80 if add !~ /^[a-z]+:\/\// && add !~ /^\// 80 if add !~ /^[a-z]+:\/\// && add !~ /^\//
81 - 'http://' + add 81 + '//' + add
82 else 82 else
  83 + if root = Noosfero.root
  84 + if !add.starts_with?(root)
  85 + add = root + add
  86 + end
  87 + end
83 add 88 add
84 end 89 end
85 end 90 end
@@ -96,4 +101,5 @@ class LinkListBlock &lt; Block @@ -96,4 +101,5 @@ class LinkListBlock &lt; Block
96 sanitizer = HTML::WhiteListSanitizer.new 101 sanitizer = HTML::WhiteListSanitizer.new
97 sanitizer.sanitize(text) 102 sanitizer.sanitize(text)
98 end 103 end
  104 +
99 end 105 end
app/models/organization.rb
@@ -31,6 +31,16 @@ class Organization &lt; Profile @@ -31,6 +31,16 @@ class Organization &lt; Profile
31 31
32 scope :more_popular, :order => 'members_count DESC' 32 scope :more_popular, :order => 'members_count DESC'
33 33
  34 + validate :presence_of_required_fieds, :unless => :is_template
  35 +
  36 + def presence_of_required_fieds
  37 + self.required_fields.each do |field|
  38 + if self.send(field).blank?
  39 + self.errors.add_on_blank(field)
  40 + end
  41 + end
  42 + end
  43 +
34 def validation_methodology 44 def validation_methodology
35 self.validation_info ? self.validation_info.validation_methodology : nil 45 self.validation_info ? self.validation_info.validation_methodology : nil
36 end 46 end
@@ -136,7 +146,11 @@ class Organization &lt; Profile @@ -136,7 +146,11 @@ class Organization &lt; Profile
136 end 146 end
137 147
138 def notification_emails 148 def notification_emails
139 - [contact_email.blank? ? nil : contact_email].compact + admins.map(&:email) 149 + emails = [contact_email].select(&:present?) + admins.map(&:email)
  150 + if emails.empty?
  151 + emails << environment.contact_email
  152 + end
  153 + emails
140 end 154 end
141 155
142 def already_request_membership?(person) 156 def already_request_membership?(person)
app/models/person.rb
@@ -172,7 +172,7 @@ class Person &lt; Profile @@ -172,7 +172,7 @@ class Person &lt; Profile
172 FIELDS 172 FIELDS
173 end 173 end
174 174
175 - validate :presence_of_required_fields 175 + validate :presence_of_required_fields, :unless => :is_template
176 176
177 def presence_of_required_fields 177 def presence_of_required_fields
178 self.required_fields.each do |field| 178 self.required_fields.each do |field|
app/models/profile.rb
@@ -358,16 +358,17 @@ class Profile &lt; ActiveRecord::Base @@ -358,16 +358,17 @@ class Profile &lt; ActiveRecord::Base
358 end 358 end
359 359
360 def copy_blocks_from(profile) 360 def copy_blocks_from(profile)
  361 + template_boxes = profile.boxes.select{|box| box.position}
361 self.boxes.destroy_all 362 self.boxes.destroy_all
362 - profile.boxes.each do |box|  
363 - new_box = Box.new 363 + self.boxes = template_boxes.size.times.map { Box.new }
  364 +
  365 + template_boxes.each_with_index do |box, i|
  366 + new_box = self.boxes[i]
364 new_box.position = box.position 367 new_box.position = box.position
365 - self.boxes << new_box  
366 box.blocks.each do |block| 368 box.blocks.each do |block|
367 new_block = block.class.new(:title => block[:title]) 369 new_block = block.class.new(:title => block[:title])
368 - new_block.settings = block.settings  
369 - new_block.position = block.position  
370 - self.boxes[-1].blocks << new_block 370 + new_block.copy_from(block)
  371 + new_box.blocks << new_block
371 end 372 end
372 end 373 end
373 end 374 end
app/models/task.rb
@@ -285,8 +285,9 @@ class Task &lt; ActiveRecord::Base @@ -285,8 +285,9 @@ class Task &lt; ActiveRecord::Base
285 # If 285 # If
286 def send_notification(action) 286 def send_notification(action)
287 if sends_email? 287 if sends_email?
288 - if self.requestor  
289 - TaskMailer.generic_message("task_#{action}", self) 288 + if self.requestor && !self.requestor.notification_emails.empty?
  289 + message = TaskMailer.generic_message("task_#{action}", self)
  290 + message.deliver if message
290 end 291 end
291 end 292 end
292 end 293 end
app/sweepers/role_assignment_sweeper.rb
@@ -13,20 +13,22 @@ class RoleAssignmentSweeper &lt; ActiveRecord::Observer @@ -13,20 +13,22 @@ class RoleAssignmentSweeper &lt; ActiveRecord::Observer
13 protected 13 protected
14 14
15 def expire_caches(role_assignment) 15 def expire_caches(role_assignment)
16 - expire_cache(role_assignment.accessor)  
17 - expire_cache(role_assignment.resource) if role_assignment.resource.respond_to?(:cache_keys) 16 + expire_cache(role_assignment.accessor) if role_assignment.accessor.kind_of?(Profile)
  17 + expire_cache(role_assignment.resource) if role_assignment.resource.kind_of?(Profile)
18 end 18 end
19 19
20 def expire_cache(profile) 20 def expire_cache(profile)
21 per_page = Noosfero::Constants::PROFILE_PER_PAGE 21 per_page = Noosfero::Constants::PROFILE_PER_PAGE
22 - profile.cache_keys(:per_page => per_page).each { |ck|  
23 - expire_timeout_fragment(ck)  
24 - } 22 +
  23 + profile.cache_keys(:per_page => per_page).each { |ck| expire_timeout_fragment(ck) }
  24 + expire_timeout_fragment(profile.members_cache_key(:per_page => per_page))
25 25
26 profile.blocks_to_expire_cache.each { |block| 26 profile.blocks_to_expire_cache.each { |block|
27 blocks = profile.blocks.select{|b| b.kind_of?(block)} 27 blocks = profile.blocks.select{|b| b.kind_of?(block)}
28 BlockSweeper.expire_blocks(blocks) 28 BlockSweeper.expire_blocks(blocks)
29 } 29 }
  30 +
  31 + expire_blocks_cache(profile, [:role_assignment])
30 end 32 end
31 33
32 end 34 end
app/views/events/_month.html.erb
@@ -13,8 +13,8 @@ @@ -13,8 +13,8 @@
13 date.day, 13 date.day,
14 :url => {:action => 'events_by_day', :year => date.year, :month => date.month, :day => date.day, :category_id => @category_id}, 14 :url => {:action => 'events_by_day', :year => date.year, :month => date.month, :day => date.day, :category_id => @category_id},
15 :update => 'events-of-the-day', 15 :update => 'events-of-the-day',
16 - :loading => '$("events-of-the-day").addClassName("loading")',  
17 - :complete => '$("events-of-the-day").removeClassName("loading")' 16 + :loading => "$('events-of-the-day').addClassName('loading')",
  17 + :complete => "$('events-of-the-day').removeClassName('loading')"
18 ) : 18 ) :
19 date.day 19 date.day
20 %> 20 %>
app/views/layouts/application-ng.html.erb
@@ -19,6 +19,9 @@ @@ -19,6 +19,9 @@
19 <meta property="og:site_name" content="<%= profile ? profile.name : @environment.name %>"> 19 <meta property="og:site_name" content="<%= profile ? profile.name : @environment.name %>">
20 <meta property="og:description" content="<%= @page ? truncate(strip_tags(@page.body.to_s), :length => 200) : @environment.name %>"> 20 <meta property="og:description" content="<%= @page ? truncate(strip_tags(@page.body.to_s), :length => 200) : @environment.name %>">
21 21
  22 + <!-- site root -->
  23 + <meta property="noosfero:root" content="<%= Noosfero.root %>"/>
  24 +
22 <% if @page %> 25 <% if @page %>
23 <meta property="article:published_time" content="<%= show_date(@page.published_at) %>"> 26 <meta property="article:published_time" content="<%= show_date(@page.published_at) %>">
24 <% @page.body_images_paths.each do |img| %> 27 <% @page.body_images_paths.each do |img| %>
app/views/manage_products/_edit_description.html.erb
1 <%= render :file => 'shared/tiny_mce', :locals => {:mode => 'simple'} %> 1 <%= render :file => 'shared/tiny_mce', :locals => {:mode => 'simple'} %>
2 <%= remote_form_for(@product, 2 <%= remote_form_for(@product,
3 :loading => "small_loading('product-description-form')", 3 :loading => "small_loading('product-description-form')",
4 - :before => ("tinyMCE.triggerSave()" unless Rails.env == 'test'),  
5 :update => 'product-description', 4 :update => 'product-description',
6 :url => {:controller => 'manage_products', :action => 'edit', :id => @product, :field => 'description'}, 5 :url => {:controller => 'manage_products', :action => 'edit', :id => @product, :field => 'description'},
7 :html => {:id => 'product-description-form', :method => 'post'}) do |f| %> 6 :html => {:id => 'product-description-form', :method => 'post'}) do |f| %>
app/views/profile/_common.html.erb
1 <% unless @action %> 1 <% unless @action %>
2 <% cache_timeout(profile.cache_key + '-profile-general-info', 4.hours) do %> 2 <% cache_timeout(profile.cache_key + '-profile-general-info', 4.hours) do %>
3 - <tr>  
4 - <th colspan='2'>  
5 - <%= _('Content') %>  
6 - </th>  
7 - </tr>  
8 -  
9 - <% profile.blogs.each do |blog| %>  
10 - <tr>  
11 - <td><%= blog.name + ':' %></td>  
12 - <td>  
13 - <%= link_to(n_('One post', '%{num} posts', blog.posts.published.count) % { :num => blog.posts.published.count }, blog.url) %>  
14 - </td>  
15 - </tr>  
16 - <% end %>  
17 - <% profile.image_galleries.each do |gallery| %>  
18 - <tr>  
19 - <td><%= gallery.name + ':' %></td>  
20 - <td>  
21 - <%= link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url) %>  
22 - </td>  
23 - </tr>  
24 - <% end %>  
25 -  
26 - <tr>  
27 - <td><%= _('Events:') %></td>  
28 - <td>  
29 - <%= link_to profile.events.published.count, :controller => 'events', :action => 'events' %>  
30 - </td>  
31 - </tr>  
32 - <tr>  
33 - <td>  
34 - <%= _('Tags:') %>  
35 - </td>  
36 - <td>  
37 - <%= tag_cloud @tags, :id, { :action => 'tags' }, :max_size => 18, :min_size => 10%>  
38 - </td>  
39 - </tr> 3 + <%= display_content %>
40 4
41 <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %> 5 <% if !environment.enabled?('disable_categories') && !profile.interests.empty? %>
42 - <tr>  
43 - <th colspan='2'><%= _('Interests') %></th>  
44 - </tr>  
45 - <% profile.interests.each do |item| %>  
46 - <tr>  
47 - <td></td>  
48 - <td><%= link_to item.name, :controller => 'search', :action => 'category_index', :category_path => item.explode_path %></td>  
49 - </tr>  
50 - <% end %> 6 + <%= display_interests %>
51 <% end %> 7 <% end %>
  8 +
  9 + <%= display_general %>
52 <% end %> 10 <% end %>
53 <% end %> 11 <% end %>
app/views/profile/_organization_profile.html.erb
1 <table> 1 <table>
2 - <tr>  
3 - <th colspan='2'><%= _('Basic information')%></th>  
4 - </tr>  
5 -  
6 - <tr>  
7 - <td class='field-name'><%= _('Members') %></td>  
8 - <td>  
9 - <%= link_to profile.members_count, :controller => 'profile', :action => 'members' %>  
10 - </td>  
11 - </tr>  
12 -  
13 - <%= display_field(_('Type:'), profile, :privacy_setting, true) %>  
14 -  
15 - <%= display_field(_('Location:'), profile, :location, true) %>  
16 -  
17 - <tr>  
18 - <td class='field-name'><%= _('Profile created at:') %></td>  
19 - <td><%= show_date(profile.created_at) %></td>  
20 - </tr>  
21 -  
22 - <% if profile.kind_of?(Enterprise) && profile.environment.enabled?('products_for_enterprises') %>  
23 - <tr>  
24 - <td></td>  
25 - <td>  
26 - <%= link_to _('Products/Services'), :controller => 'catalog', :action => 'index' %>  
27 - </td>  
28 - </tr>  
29 - <% end %>  
30 -  
31 - <tr>  
32 - <td class='field-name'><%= _('Administrators:') %></td>  
33 - <td>  
34 - <%= profile.admins.map { |admin| link_to(admin.short_name, admin.url)}.join(', ') %>  
35 - </td>  
36 - </tr>  
37 - 2 + <%= display_basic_information %>
  3 + <%= display_contact %>
  4 + <%= display_economic %>
38 <%= render :partial => 'common' %> 5 <%= render :partial => 'common' %>
39 </table> 6 </table>
app/views/profile/_person_profile.html.erb
1 <table> 1 <table>
2 - <tr>  
3 - <th colspan='2'><%= _('Basic information')%></th>  
4 - </tr>  
5 - <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %>  
6 - <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%>  
7 - <%= display_field _('Location:'), profile, :location %>  
8 -  
9 - <%= display_field(_('Type:'), profile, :privacy_setting, true) %>  
10 -  
11 - <tr>  
12 - <td class='field-name'><%= _('Profile created at:') %></td>  
13 - <td><%= show_date(profile.created_at) %></td>  
14 - </tr>  
15 -  
16 - <%= display_contact profile %> 2 + <%= display_basic_information %>
  3 + <%= display_contact %>
  4 + <%= display_location %>
17 5
18 <% cache_timeout(profile.relationships_cache_key, 4.hours) do %> 6 <% cache_timeout(profile.relationships_cache_key, 4.hours) do %>
19 - <%= display_work_info profile %>  
20 -  
21 - <tr>  
22 - <th colspan='2'><%= _('Network')%></th>  
23 - </tr>  
24 - <tr>  
25 - <td><%= _('Friends') + ':' %></td>  
26 - <td><%= link_to profile.friends.count, { :controller => 'profile', :action => 'friends' } %></td>  
27 - </tr>  
28 - <tr>  
29 - <td><%= _('Communities') + ':' %></td>  
30 - <td><%= link_to profile.communities.count, :controller => "profile", :action => 'communities' %></td>  
31 - </tr>  
32 - <% if environment.disabled?('disable_asset_enterprises') %>  
33 - <tr id="person-profile-network-enterprises">  
34 - <td><%= _('Enterprises') + ':' %></td>  
35 - <td><%= link_to profile.enterprises.count, :controller => "profile", :action => 'enterprises' %></td>  
36 - </tr>  
37 - <% end %> 7 + <%= display_work %>
  8 + <%= display_study %>
  9 + <%= display_network %>
38 10
39 <%= render :partial => 'common' %> 11 <%= render :partial => 'common' %>
40 -  
41 <% end %> 12 <% end %>
42 </table> 13 </table>
43 14
app/views/profile/_profile_comment_form.html.erb
@@ -10,8 +10,8 @@ @@ -10,8 +10,8 @@
10 :rows => 1, 10 :rows => 1,
11 :class => 'submit-with-keypress', 11 :class => 'submit-with-keypress',
12 :title => _('Leave your comment'), 12 :title => _('Leave your comment'),
13 - :onfocus => ('if(this.value==this.title){this.value="";this.style.color="#000"};this.style.backgroundImage="url(' + profile_icon(current_person, :icon, false) + ')"' if logged_in?),  
14 - :onblur => ('if(this.value==""){this.value=this.title;this.style.color="#ccc"};this.style.backgroundImage="none"' if logged_in?), 13 + :onfocus => ("if(this.value==this.title){this.value='';this.style.color='#000'};this.style.backgroundImage='url(" + profile_icon(current_person, :icon, false) + ")'" if logged_in?),
  14 + :onblur => ("if(this.value==''){this.value=this.title;this.style.color='#ccc'};this.style.backgroundImage='none'" if logged_in?),
15 :value => _('Leave your comment'), 15 :value => _('Leave your comment'),
16 :style => 'color: #ccc' %> 16 :style => 'color: #ccc' %>
17 <%= hidden_field_tag :source_id, activity.id, :id => "activity_id_#{activity.id}" %> 17 <%= hidden_field_tag :source_id, activity.id, :id => "activity_id_#{activity.id}" %>
app/views/profile/_profile_scrap_reply_form.html.erb
@@ -9,8 +9,8 @@ @@ -9,8 +9,8 @@
9 :rows => 1, 9 :rows => 1,
10 :class => 'submit-with-keypress', 10 :class => 'submit-with-keypress',
11 :title => _('Leave your comment'), 11 :title => _('Leave your comment'),
12 - :onfocus => ('if(this.value==this.title){this.value="";this.style.color="#000"};this.style.backgroundImage="url(' + profile_icon(current_person, :icon, false) + ')"' if logged_in?),  
13 - :onblur => ('if(this.value==""){this.value=this.title;this.style.color="#ccc"};this.style.backgroundImage="none"' if logged_in?), 12 + :onfocus => ("if(this.value==this.title){this.value='';this.style.color='#000'};this.style.backgroundImage='url(" + profile_icon(current_person, :icon, false) + ")'" if logged_in?),
  13 + :onblur => ("if(this.value==''){this.value=this.title;this.style.color='#ccc'};this.style.backgroundImage='none'" if logged_in?),
14 :value => _('Leave your comment') %> 14 :value => _('Leave your comment') %>
15 <%= hidden_field_tag 'scrap[scrap_id]', scrap.id %> 15 <%= hidden_field_tag 'scrap[scrap_id]', scrap.id %>
16 <%= hidden_field_tag 'receiver_id', scrap.sender.id %> 16 <%= hidden_field_tag 'receiver_id', scrap.sender.id %>
app/views/profile_editor/_person_form.html.erb
@@ -25,6 +25,7 @@ @@ -25,6 +25,7 @@
25 <%= optional_field(@person, 'address', labelled_form_field(_('Address (street and number)'), text_field(:profile_data, :address, :rel => _('Address')))) %> 25 <%= optional_field(@person, 'address', labelled_form_field(_('Address (street and number)'), text_field(:profile_data, :address, :rel => _('Address')))) %>
26 <%= optional_field(@person, 'address_reference', labelled_form_field(_('Address reference'), text_field(:profile_data, :address_reference, :rel => _('Address reference')))) %> 26 <%= optional_field(@person, 'address_reference', labelled_form_field(_('Address reference'), text_field(:profile_data, :address_reference, :rel => _('Address reference')))) %>
27 <%= optional_field(@person, 'district', labelled_form_field(_('District'), text_field(:profile_data, :district, :rel => _('District')))) %> 27 <%= optional_field(@person, 'district', labelled_form_field(_('District'), text_field(:profile_data, :district, :rel => _('District')))) %>
  28 +<%= optional_field(@person, 'image', labelled_form_field(_('Image'), file_field(:file, :image, :rel => _('Image')))) %>
28 29
29 <% optional_field(@person, 'schooling') do %> 30 <% optional_field(@person, 'schooling') do %>
30 <div class="formfieldline"> 31 <div class="formfieldline">
app/views/shared/reported_versions/_article.html.erb
@@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
1 -<ul>  
2 - <li><%= (content_tag('strong', _('Title') + ': ') + article.title) if article.title %> </li>  
3 - <li><%= (content_tag('strong', _('Type') + ': ') + article.class.short_description) %> </li>  
4 - <li>  
5 - <%= (content_tag('strong', _('Original content') + ': ') + link_to(article.name, article.url)) %> <br />  
6 - <%= content_tag('small', _('This link might be unavailable if the content is removed')) %>  
7 - </li>  
8 -</ul>  
9 -  
10 -<%= article_to_html(article) %>  
app/views/shared/reported_versions/_comment.html.erb
@@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
1 -<ul>  
2 - <li><%= (content_tag('strong', _('Title') + ': ') + comment.title) if comment.title %> </li>  
3 - <li><%= content_tag('strong', _('Type') + ': ') + _('Comment') %> </li>  
4 - <li>  
5 - <%= (content_tag('strong', _('Original content') + ': ') + link_to(comment.title || url_for(comment.url), comment.url)) %> <br />  
6 - <%= content_tag('small', _('This link might be unavailable if the content is removed')) %>  
7 - </li>  
8 -</ul>  
9 -  
10 -<p><%= article_to_html(comment) %></p>  
app/views/shared/reported_versions/_folder.html.erb
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -<%= _('Reported folder') + ': ' + link_to(article.name, article.url) %>  
app/views/shared/reported_versions/profile/_article.html.erb 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +<ul>
  2 + <li><%= (content_tag('strong', _('Title') + ': ') + article.title) if article.title %> </li>
  3 + <li><%= (content_tag('strong', _('Type') + ': ') + article.class.short_description) %> </li>
  4 + <li>
  5 + <%= (content_tag('strong', _('Original content') + ': ') + link_to(article.name, article.url)) %> <br />
  6 + <%= content_tag('small', _('This link might be unavailable if the content is removed')) %>
  7 + </li>
  8 +</ul>
  9 +
  10 +<%= article_to_html(article) %>
app/views/shared/reported_versions/profile/_comment.html.erb 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +<ul>
  2 + <li><%= (content_tag('strong', _('Title') + ': ') + comment.title) if comment.title %> </li>
  3 + <li><%= content_tag('strong', _('Type') + ': ') + _('Comment') %> </li>
  4 + <li>
  5 + <%= (content_tag('strong', _('Original content') + ': ') + link_to(comment.title || url_for(comment.url), comment.url)) %> <br />
  6 + <%= content_tag('small', _('This link might be unavailable if the content is removed')) %>
  7 + </li>
  8 +</ul>
  9 +
  10 +<p><%= article_to_html(comment) %></p>
app/views/shared/reported_versions/profile/_folder.html.erb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +<%= _('Reported folder') + ': ' + link_to(article.name, article.url) %>
app/views/shared/tiny_mce.html.erb
1 -<% extend MacrosHelper %>  
2 -<%= javascript_include_tag 'tinymce/jscripts/tiny_mce/tiny_mce.js' %>  
3 -<%= include_macro_js_files %>  
4 -<script type="text/javascript">  
5 - var myplugins = "searchreplace,print,table,contextmenu,-macrosPlugin";  
6 - var first_line, second_line;  
7 - var mode = '<%= mode ||= false %>'  
8 - <% if mode %>  
9 - first_line = "fontsizeselect,bold,italic,underline,bullist,numlist,justifyleft,justifycenter,justifyright,link,unlink"  
10 - second_line = ""  
11 - <% else %>  
12 - first_line = "print,separator,copy,paste,separator,undo,redo,separator,search,replace,separator,forecolor,fontsizeselect,formatselect"  
13 - second_line = "bold,italic,underline,strikethrough,separator,bullist,numlist,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,link,unlink,image,table,separator,cleanup,code,macros"  
14 - <% macros_with_buttons.each do |macro| %>  
15 - second_line += ',<%=macro.identifier %>'  
16 - <% end %>  
17 - <% end %>  
18 -  
19 - if (tinymce.isIE) {  
20 - // the paste plugin is only useful in Internet Explorer  
21 - myplugins = "paste," + myplugins;  
22 - } 1 +<%
  2 +extend TinymceHelper
  3 +mode ||= false
  4 +%>
23 5
  6 +<%= tinymce_js %>
  7 +<script type="text/javascript">
24 tinymce.create('tinymce.plugins.MacrosPlugin', { 8 tinymce.create('tinymce.plugins.MacrosPlugin', {
25 createControl: function(n, cm) { 9 createControl: function(n, cm) {
26 switch (n) { 10 switch (n) {
@@ -49,56 +33,21 @@ tinymce.create(&#39;tinymce.plugins.MacrosPlugin&#39;, { @@ -49,56 +33,21 @@ tinymce.create(&#39;tinymce.plugins.MacrosPlugin&#39;, {
49 } 33 }
50 }); 34 });
51 35
  36 +function tinymce_macros_setup(editor) {
  37 + <% macros_with_buttons.each do |macro| %>
  38 + editor.addButton('<%= macro.identifier %>', {
  39 + title: <%= macro_title(macro).to_json %>,
  40 + onclick: <%= generate_macro_config_dialog(macro) %>,
  41 + image : '<%= macro.configuration[:icon_path]%>'
  42 + });
  43 + <% end %>
  44 +}
  45 +
52 // Register plugin with a short name 46 // Register plugin with a short name
53 tinymce.PluginManager.add('macrosPlugin', tinymce.plugins.MacrosPlugin); 47 tinymce.PluginManager.add('macrosPlugin', tinymce.plugins.MacrosPlugin);
54 48
55 -tinyMCE.init({  
56 - mode : "textareas",  
57 - editor_selector : "mceEditor",  
58 - theme : "advanced",  
59 - relative_urls : false,  
60 - remove_script_host : false,  
61 - document_base_url : <%= environment.top_url.to_json %>,  
62 - plugins: myplugins,  
63 - theme_advanced_toolbar_location : "top",  
64 - theme_advanced_layout_manager: 'SimpleLayout',  
65 - theme_advanced_buttons1 : first_line,  
66 - theme_advanced_buttons2 : second_line,  
67 - theme_advanced_buttons3 : "",  
68 - theme_advanced_blockformats :"p,address,pre,h2,h3,h4,h5,h6",  
69 - paste_auto_cleanup_on_paste : true,  
70 - paste_insert_word_content_callback : "convertWord",  
71 - paste_use_dialog: false,  
72 - apply_source_formatting : true,  
73 - extended_valid_elements : "applet[style|archive|codebase|code|height|width],comment,iframe[src|style|allowtransparency|frameborder|width|height|scrolling],embed[title|src|type|height|width],audio[controls|autoplay],video[controls|autoplay],source[src|type]",  
74 - content_css: '/stylesheets/tinymce.css,<%= macro_css_files %>',  
75 - language: <%= tinymce_language.inspect %>,  
76 - entity_encoding: 'raw',  
77 - setup : function(ed) {  
78 - <% macros_with_buttons.each do |macro| %>  
79 - ed.addButton('<%= macro.identifier %>', {  
80 - title: <%= macro_title(macro).to_json %>,  
81 - onclick: <%= generate_macro_config_dialog(macro) %>,  
82 - image : '<%= macro.configuration[:icon_path]%>'  
83 - });  
84 - <% end %>  
85 - } 49 +jQuery(document).ready(function () {
  50 + <%= tinymce_init_js :mode => mode %>
86 }); 51 });
87 -  
88 -function convertWord(type, content) {  
89 - switch (type) {  
90 - // Gets executed before the built in logic performes it's cleanups  
91 - case "before":  
92 - //content = content.toLowerCase(); // Some dummy logic  
93 - break;  
94 -  
95 - // Gets executed after the built in logic performes it's cleanups  
96 - case "after":  
97 - content = content.replace(/<!--\s*-->/, '');  
98 - break;  
99 - }  
100 -  
101 - return content;  
102 -}  
103 -  
104 </script> 52 </script>
  53 +
app/views/task_mailer/generic_message.text.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<%= _('Dear %s,') % @requestor %>
  2 +
  3 +<%= word_wrap(@message) %>
  4 +
  5 +<%= _('Greetings,') %>
  6 +
  7 +--
  8 +<%= _('%s team.') % @environment %>
  9 +<%= @url %>
app/views/task_mailer/task_activated.text.erb
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -task_cancelled.text.erb  
2 \ No newline at end of file 0 \ No newline at end of file
app/views/task_mailer/task_cancelled.text.erb
@@ -1,9 +0,0 @@ @@ -1,9 +0,0 @@
1 -<%= _('Dear %s,') % @requestor %>  
2 -  
3 -<%= word_wrap(@message) %>  
4 -  
5 -<%= _('Greetings,') %>  
6 -  
7 ---  
8 -<%= _('%s team.') % @environment %>  
9 -<%= @url %>  
app/views/task_mailer/task_created.text.erb
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -task_cancelled.text.erb  
2 \ No newline at end of file 0 \ No newline at end of file
app/views/task_mailer/task_finished.text.erb
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -task_cancelled.text.erb  
2 \ No newline at end of file 0 \ No newline at end of file
config/application.rb
@@ -115,6 +115,12 @@ module Noosfero @@ -115,6 +115,12 @@ module Noosfero
115 :key => '_noosfero_session', 115 :key => '_noosfero_session',
116 } 116 }
117 117
  118 + config.time_zone = File.read('/etc/timezone').split("\n").first
  119 + config.active_record.default_timezone = :local
  120 +
  121 + config.paths['db/migrate'] += Dir.glob "#{Rails.root}/{baseplugins,config/plugins}/*/db/migrate"
  122 + config.i18n.load_path += Dir.glob "#{Rails.root}/{baseplugins,config/plugins}/*/locales/*.{rb,yml}"
  123 +
118 Noosfero::Plugin.setup(config) 124 Noosfero::Plugin.setup(config)
119 125
120 end 126 end
config/cucumber.yml
1 <% base_requires = '-r features/support -r features/step_definitions' %> 1 <% base_requires = '-r features/support -r features/step_definitions' %>
2 -<% default_options = "--color --format progress --strict --tags ~@selenium --tags ~@selenium-fixme --tags ~@fixme --exclude features/support/selenium.rb #{base_requires}" %> 2 +<% default_options = "--format progress --strict --tags ~@selenium --tags ~@selenium-fixme --tags ~@fixme --exclude features/support/selenium.rb #{base_requires}" %>
  3 +<%
  4 + default_options += ' --color' if $stdout.isatty
  5 +%>
3 <% selenium_options = "--strict --tags @selenium #{base_requires}" %> 6 <% selenium_options = "--strict --tags @selenium #{base_requires}" %>
4 7
5 default: <%= default_options %> 8 default: <%= default_options %>
config/initializers/i18n.rb
1 # necessary for I18n.default_locale to work 1 # necessary for I18n.default_locale to work
2 require 'i18n/backend/fallbacks' 2 require 'i18n/backend/fallbacks'
3 I18n.backend.class.send :include, I18n::Backend::Fallbacks 3 I18n.backend.class.send :include, I18n::Backend::Fallbacks
4 - 4 +I18n.enforce_available_locales = false
db/migrate/20140708121356_index_articles_filtered_fields.rb
1 class IndexArticlesFilteredFields < ActiveRecord::Migration 1 class IndexArticlesFilteredFields < ActiveRecord::Migration
2 def self.up 2 def self.up
3 %w[articles article_versions].each do |table| 3 %w[articles article_versions].each do |table|
4 - add_index table, [:path]  
5 - add_index table, [:path, :profile_id] 4 + add_index table.to_sym, [:path]
  5 + add_index table.to_sym, [:path, :profile_id]
6 end 6 end
7 add_index :articles, [:type] 7 add_index :articles, [:type]
8 add_index :articles, [:type, :parent_id] 8 add_index :articles, [:type, :parent_id]
db/migrate/20140709212646_add_spam_comments_counter_cache_to_articles.rb
@@ -2,7 +2,10 @@ class AddSpamCommentsCounterCacheToArticles &lt; ActiveRecord::Migration @@ -2,7 +2,10 @@ class AddSpamCommentsCounterCacheToArticles &lt; ActiveRecord::Migration
2 def self.up 2 def self.up
3 add_column :articles, :spam_comments_count, :integer, :default => 0 3 add_column :articles, :spam_comments_count, :integer, :default => 0
4 add_column :article_versions, :spam_comments_count, :integer, :default => 0 4 add_column :article_versions, :spam_comments_count, :integer, :default => 0
5 - execute "update articles set spam_comments_count = (select count(*) from comments where comments.source_id = articles.id and comments.source_type = 'Article' and comments.spam = 't');" 5 +
  6 + execute("SELECT comments.source_id as source_id, count(comments.id) as comments_count FROM comments LEFT OUTER JOIN articles ON articles.id = source_id WHERE comments.source_type = 'Article' AND comments.spam = 't' GROUP BY comments.source_id;").each do |data|
  7 + execute("UPDATE articles SET spam_comments_count = '#{data['comments_count']}' WHERE id = #{data['source_id']}")
  8 + end
6 end 9 end
7 10
8 def self.down 11 def self.down
db/migrate/20140709224246_create_real_relation_between_article_and_author.rb
@@ -4,8 +4,10 @@ class CreateRealRelationBetweenArticleAndAuthor &lt; ActiveRecord::Migration @@ -4,8 +4,10 @@ class CreateRealRelationBetweenArticleAndAuthor &lt; ActiveRecord::Migration
4 add_column :article_versions, :author_id, :integer 4 add_column :article_versions, :author_id, :integer
5 5
6 # Set article's author as the first version's last_changed_by_id. 6 # Set article's author as the first version's last_changed_by_id.
7 - execute "update articles set author_id = (select article_versions.last_changed_by_id from article_versions where article_versions.article_id = articles.id and article_versions.version = 1 limit 1)"  
8 - end 7 + execute("UPDATE article_versions SET author_id = last_changed_by_id")
  8 +
  9 + execute("UPDATE articles SET author_id = article_versions.author_id FROM article_versions WHERE article_versions.article_id = articles.id AND article_versions.version = 1")
  10 + end
9 11
10 def self.down 12 def self.down
11 remove_column :articles, :author_id 13 remove_column :articles, :author_id
db/migrate/20140724134601_fix_yaml_encoding.rb
1 class FixYamlEncoding < ActiveRecord::Migration 1 class FixYamlEncoding < ActiveRecord::Migration
2 def self.up 2 def self.up
3 - fix_encoding(Block, 'settings')  
4 - fix_encoding(Product, 'data')  
5 - fix_encoding(Environment, 'settings')  
6 - fix_encoding(Profile, 'data')  
7 - fix_encoding(ActionTracker::Record, 'params')  
8 - fix_encoding(Article, 'setting')  
9 - fix_encoding(Task, 'data') 3 + ActiveRecord::Base.transaction do
  4 + fix_encoding(Environment, 'settings')
  5 + fix_encoding(Profile, 'data')
  6 + fix_encoding(Product, 'data')
  7 + fix_encoding(ActionTracker::Record, 'params')
  8 + fix_encoding(Article, 'setting')
  9 + fix_encoding(Task, 'data')
  10 + fix_encoding(Block, 'settings')
  11 + end
10 end 12 end
11 13
12 def self.down 14 def self.down
@@ -16,15 +18,34 @@ class FixYamlEncoding &lt; ActiveRecord::Migration @@ -16,15 +18,34 @@ class FixYamlEncoding &lt; ActiveRecord::Migration
16 private 18 private
17 19
18 def self.fix_encoding(model, param) 20 def self.fix_encoding(model, param)
19 - result = model.find(:all, :conditions => "#{param} LIKE '%!binary%'") 21 + result = model.all
20 puts "Fixing #{result.count} rows of #{model} (#{param})" 22 puts "Fixing #{result.count} rows of #{model} (#{param})"
21 - result.each {|r| r.update_column(param, deep_fix(r.send(param)).to_yaml)} 23 + result.each do |r|
  24 + begin
  25 + yaml = r.send(param)
  26 + # if deserialization failed then a string is returned
  27 + if yaml.is_a? String
  28 + yaml.gsub! ': `', ': '
  29 + yaml = YAML.load yaml
  30 + end
  31 + r.update_column param, deep_fix(yaml).to_yaml
  32 + rescue => e
  33 + puts "FAILED #{r.inspect}"
  34 + puts e.message
  35 + end
  36 + end
22 end 37 end
23 38
24 def self.deep_fix(hash) 39 def self.deep_fix(hash)
25 hash.each do |value| 40 hash.each do |value|
26 - value.force_encoding('UTF-8') if value.is_a?(String) && !value.frozen? && value.encoding == Encoding::ASCII_8BIT  
27 deep_fix(value) if value.respond_to?(:each) 41 deep_fix(value) if value.respond_to?(:each)
  42 + if value.is_a? String and not value.frozen?
  43 + if value.encoding == Encoding::ASCII_8BIT
  44 + value.force_encoding "utf-8"
  45 + else
  46 + value.encode!("iso-8859-1").force_encoding("utf-8")
  47 + end
  48 + end
28 end 49 end
29 end 50 end
30 51
db/migrate/20140724180943_add_index_to_blog_posts_sort.rb
1 class AddIndexToBlogPostsSort < ActiveRecord::Migration 1 class AddIndexToBlogPostsSort < ActiveRecord::Migration
2 def self.up 2 def self.up
3 %w[articles article_versions].each do |table| 3 %w[articles article_versions].each do |table|
4 - add_index table, [:published_at, :id] 4 + add_index table.to_sym, [:published_at, :id]
5 end 5 end
6 end 6 end
7 7
8 def self.down 8 def self.down
9 %w[articles article_versions].each do |table| 9 %w[articles article_versions].each do |table|
10 - remove_index table, [:published_at, :id] 10 + remove_index table.to_sym, [:published_at, :id]
11 end 11 end
12 end 12 end
13 end 13 end
debian/bundle/config
@@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
1 ----  
2 -BUNDLE_WITHOUT: test:cucumber  
3 -  
debian/changelog
  1 +noosfero (1.0~rc4) wheezy-test; urgency=low
  2 +
  3 + * Fourth release candidate
  4 +
  5 + -- Antonio Terceiro <vagrant@wheezy-base> Wed, 19 Nov 2014 10:31:16 -0300
  6 +
1 noosfero (1.0~rc3) wheezy-test; urgency=low 7 noosfero (1.0~rc3) wheezy-test; urgency=low
2 8
3 * Third release candidate to Noosfero 1.0 9 * Third release candidate to Noosfero 1.0
@@ -22,6 +28,12 @@ noosfero (0.99.0~rc20140618202455) wheezy-test; urgency=low @@ -22,6 +28,12 @@ noosfero (0.99.0~rc20140618202455) wheezy-test; urgency=low
22 28
23 -- Rodrigo Souto <rodrigo@colivre.coop.br> Wed, 18 Jun 2014 20:25:01 +0000 29 -- Rodrigo Souto <rodrigo@colivre.coop.br> Wed, 18 Jun 2014 20:25:01 +0000
24 30
  31 +noosfero (0.47.5) unstable; urgency=low
  32 +
  33 + * Bugfixes release
  34 +
  35 + -- Daniela Soares Feitosa <daniela@colivre.coop.br> Thu, 23 Oct 2014 02:24:14 +0000
  36 +
25 noosfero (0.47.4) unstable; urgency=low 37 noosfero (0.47.4) unstable; urgency=low
26 38
27 * Bugfixes and performance optimizations 39 * Bugfixes and performance optimizations
debian/control
@@ -9,7 +9,7 @@ Build-Depends: @@ -9,7 +9,7 @@ Build-Depends:
9 ruby-gettext, 9 ruby-gettext,
10 ruby-sqlite3, 10 ruby-sqlite3,
11 rake, 11 rake,
12 - rails3 (>= 3.2.6-1~), 12 + rails3 (>= 3.2.19-1~),
13 ruby-rspec, 13 ruby-rspec,
14 ruby-rspec-rails, 14 ruby-rspec-rails,
15 ruby-will-paginate, 15 ruby-will-paginate,
@@ -51,6 +51,7 @@ Depends: @@ -51,6 +51,7 @@ Depends:
51 ruby-hpricot, 51 ruby-hpricot,
52 ruby-nokogiri, 52 ruby-nokogiri,
53 ruby-acts-as-taggable-on, 53 ruby-acts-as-taggable-on,
  54 + ruby-progressbar,
54 ruby-prototype-rails, 55 ruby-prototype-rails,
55 ruby-rails-autolink, 56 ruby-rails-autolink,
56 memcached, 57 memcached,
debian/filter-gemfile 0 → 100755
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +#!/bin/sh
  2 +
  3 +set -e
  4 +
  5 +sed -e '/^group\s*:\(test\|cucumber\)/,/^end/ d' Gemfile
debian/noosfero.install
@@ -7,10 +7,6 @@ util usr/share/noosfero @@ -7,10 +7,6 @@ util usr/share/noosfero
7 Rakefile usr/share/noosfero 7 Rakefile usr/share/noosfero
8 vendor usr/share/noosfero 8 vendor usr/share/noosfero
9 9
10 -Gemfile usr/share/noosfero  
11 -Gemfile.lock usr/share/noosfero  
12 -debian/bundle/config usr/share/noosfero/.bundle  
13 -  
14 config/application.rb usr/share/noosfero/config 10 config/application.rb usr/share/noosfero/config
15 config/boot.rb usr/share/noosfero/config 11 config/boot.rb usr/share/noosfero/config
16 config/environment.rb usr/share/noosfero/config 12 config/environment.rb usr/share/noosfero/config
debian/noosfero.links
@@ -15,3 +15,4 @@ var/lib/noosfero-data/public/thumbnails usr/share/noosfero/public/th @@ -15,3 +15,4 @@ var/lib/noosfero-data/public/thumbnails usr/share/noosfero/public/th
15 usr/share/noosfero/public/designs/themes/noosfero usr/share/noosfero/public/designs/themes/default 15 usr/share/noosfero/public/designs/themes/noosfero usr/share/noosfero/public/designs/themes/default
16 usr/share/noosfero/public/designs/icons/tango usr/share/noosfero/public/designs/icons/default 16 usr/share/noosfero/public/designs/icons/tango usr/share/noosfero/public/designs/icons/default
17 usr/share/noosfero/script/noosfero-plugins usr/sbin/noosfero-plugins 17 usr/share/noosfero/script/noosfero-plugins usr/sbin/noosfero-plugins
  18 +/dev/null usr/share/noosfero/Gemfile.lock
@@ -20,6 +20,10 @@ override_dh_link: @@ -20,6 +20,10 @@ override_dh_link:
20 dh_link usr/lib/noosfero/dbinstall usr/share/dbconfig-common/scripts/noosfero/install/$$db; \ 20 dh_link usr/lib/noosfero/dbinstall usr/share/dbconfig-common/scripts/noosfero/install/$$db; \
21 done 21 done
22 22
  23 +override_dh_auto_install:
  24 + dh_auto_install
  25 + debian/filter-gemfile > $(CURDIR)/debian/noosfero/usr/share/noosfero/Gemfile
  26 +
23 override_dh_installinit: 27 override_dh_installinit:
24 dh_installinit -pnoosfero --onlyscripts 28 dh_installinit -pnoosfero --onlyscripts
25 29
etc/noosfero/varnish-accept-language.vcl
@@ -6,7 +6,14 @@ C{ @@ -6,7 +6,14 @@ C{
6 /* 6 /*
7 * Accept-language header normalization 7 * Accept-language header normalization
8 * 8 *
9 - * Cosimo, 21/01/2010 9 + * - Parses client Accept-Language HTTP header
  10 + * - Tries to find the best match with the supported languages
  11 + * - Writes the best match as req.http.X-Varnish-Accept-Language
  12 + *
  13 + * First version: Cosimo, 21/Jan/2010
  14 + * Last update: Cosimo, 03/Nov/2011
  15 + *
  16 + * http://github.com/cosimo/varnish-accept-language
10 * 17 *
11 */ 18 */
12 19
@@ -16,11 +23,12 @@ C{ @@ -16,11 +23,12 @@ C{
16 #include <string.h> 23 #include <string.h>
17 24
18 #define DEFAULT_LANGUAGE "en" 25 #define DEFAULT_LANGUAGE "en"
19 -#define SUPPORTED_LANGUAGES ":de:fr:es:ru:pt:hy:en:" 26 +#define SUPPORTED_LANGUAGES ":de:eo:es:fr:hy:it:pt:ru:"
20 27
21 #define vcl_string char 28 #define vcl_string char
22 -#define LANG_LIST_SIZE 16  
23 -#define LANG_MAXLEN 16 29 +#define LANG_LIST_SIZE 16
  30 +#define HDR_MAXLEN 256
  31 +#define LANG_MAXLEN 8
24 #define RETURN_LANG(x) { \ 32 #define RETURN_LANG(x) { \
25 strncpy(lang, x, LANG_MAXLEN); \ 33 strncpy(lang, x, LANG_MAXLEN); \
26 return; \ 34 return; \
@@ -64,9 +72,8 @@ int is_supported(vcl_string *lang) { @@ -64,9 +72,8 @@ int is_supported(vcl_string *lang) {
64 strncat(match_str, lang, LANG_MAXLEN); 72 strncat(match_str, lang, LANG_MAXLEN);
65 strncat(match_str, ":\0", 2); 73 strncat(match_str, ":\0", 2);
66 74
67 - if (strstr(supported_languages, match_str)) { 75 + if (strstr(supported_languages, match_str))
68 is_supported = 1; 76 is_supported = 1;
69 - }  
70 77
71 return is_supported; 78 return is_supported;
72 } 79 }
@@ -90,6 +97,7 @@ void select_language(const vcl_string *incoming_header, char *lang) { @@ -90,6 +97,7 @@ void select_language(const vcl_string *incoming_header, char *lang) {
90 vcl_string *lang_tok = NULL; 97 vcl_string *lang_tok = NULL;
91 vcl_string root_lang[3]; 98 vcl_string root_lang[3];
92 vcl_string *header; 99 vcl_string *header;
  100 + vcl_string header_copy[HDR_MAXLEN];
93 vcl_string *pos = NULL; 101 vcl_string *pos = NULL;
94 vcl_string *q_spec = NULL; 102 vcl_string *q_spec = NULL;
95 unsigned int curr_lang = 0, i = 0; 103 unsigned int curr_lang = 0, i = 0;
@@ -106,7 +114,7 @@ void select_language(const vcl_string *incoming_header, char *lang) { @@ -106,7 +114,7 @@ void select_language(const vcl_string *incoming_header, char *lang) {
106 RETURN_DEFAULT_LANG; 114 RETURN_DEFAULT_LANG;
107 115
108 /* Tokenize Accept-Language */ 116 /* Tokenize Accept-Language */
109 - header = (vcl_string *) incoming_header; 117 + header = strncpy(header_copy, incoming_header, sizeof(header_copy));
110 118
111 while ((lang_tok = strtok_r(header, " ,", &pos))) { 119 while ((lang_tok = strtok_r(header, " ,", &pos))) {
112 120
@@ -137,7 +145,8 @@ void select_language(const vcl_string *incoming_header, char *lang) { @@ -137,7 +145,8 @@ void select_language(const vcl_string *incoming_header, char *lang) {
137 header = NULL; 145 header = NULL;
138 146
139 /* Break out if stored max no. of languages */ 147 /* Break out if stored max no. of languages */
140 - if (curr_lang >= LANG_MAXLEN) break; 148 + if (curr_lang >= LANG_LIST_SIZE)
  149 + break;
141 } 150 }
142 151
143 /* Sort by priority */ 152 /* Sort by priority */
@@ -157,12 +166,11 @@ void vcl_rewrite_accept_language(const struct sess *sp) { @@ -157,12 +166,11 @@ void vcl_rewrite_accept_language(const struct sess *sp) {
157 vcl_string *in_hdr; 166 vcl_string *in_hdr;
158 vcl_string lang[LANG_MAXLEN]; 167 vcl_string lang[LANG_MAXLEN];
159 168
160 - memset(lang, 0, LANG_MAXLEN);  
161 -  
162 /* Get Accept-Language header from client */ 169 /* Get Accept-Language header from client */
163 in_hdr = VRT_GetHdr(sp, HDR_REQ, "\020Accept-Language:"); 170 in_hdr = VRT_GetHdr(sp, HDR_REQ, "\020Accept-Language:");
164 171
165 /* Normalize and filter out by list of supported languages */ 172 /* Normalize and filter out by list of supported languages */
  173 + memset(lang, 0, sizeof(lang));
166 select_language(in_hdr, lang); 174 select_language(in_hdr, lang);
167 175
168 /* By default, use a different header name: don't mess with backend logic */ 176 /* By default, use a different header name: don't mess with backend logic */
@@ -191,3 +199,4 @@ sub vcl_fetch { @@ -191,3 +199,4 @@ sub vcl_fetch {
191 set beresp.http.Vary = "X-Varnish-Accept-Language"; 199 set beresp.http.Vary = "X-Varnish-Accept-Language";
192 } 200 }
193 } 201 }
  202 +
features/change_organization_name.feature
@@ -1,36 +0,0 @@ @@ -1,36 +0,0 @@
1 -Feature: change organization name  
2 - As an organization's admin  
3 - I want to change it's name  
4 - In order to keep it's name consistent  
5 -  
6 - Scenario: changing community's name  
7 - Given the following communities  
8 - | identifier | name |  
9 - | sample-community | Sample Community |  
10 - And the following users  
11 - | login | name |  
12 - | joaosilva | Joao Silva |  
13 - And "Joao Silva" is admin of "Sample Community"  
14 - And I am logged in as "joaosilva"  
15 - And I am on sample-community's control panel  
16 - And I follow "Community Info and settings"  
17 - And I fill in "Name" with "New Sample Community"  
18 - When I press "Save"  
19 - Then I should be on sample-community's control panel  
20 - And I should see "New Sample Community" within "title"  
21 -  
22 - Scenario: changing enterprise's name  
23 - Given the following enterprises  
24 - | identifier | name |  
25 - | sample-enterprise | Sample Enterprise |  
26 - And the following users  
27 - | login | name |  
28 - | joaosilva | Joao Silva |  
29 - And "Joao Silva" is admin of "Sample Enterprise"  
30 - And I am logged in as "joaosilva"  
31 - And I am on sample-enterprise's control panel  
32 - And I follow "Enterprise Info and settings"  
33 - And I fill in "Name" with "New Sample Enterprise"  
34 - When I press "Save"  
35 - Then I should be on sample-enterprise's control panel  
36 - And I should see "New Sample Enterprise" within "title"  
features/chat.feature
@@ -1,145 +0,0 @@ @@ -1,145 +0,0 @@
1 -Feature: chat  
2 - As a Noosfero user  
3 - I want to chat with my friends  
4 -  
5 - Background:  
6 - Given the following users  
7 - | login | name |  
8 - | tame | Tame |  
9 - | mariasilva | Maria Silva |  
10 - | josesilva | Jose Silva |  
11 - And "tame" is online in chat  
12 - And "mariasilva" is online in chat  
13 - And "josesilva" is online in chat  
14 - And "tame" is friend of "mariasilva"  
15 - And "tame" is friend of "josesilva"  
16 -  
17 - @selenium  
18 - Scenario: provide link to open chat  
19 - Given feature "xmpp_chat" is enabled on environment  
20 - And I am logged in as "tame"  
21 - Then I should see "Open chat" within "#user"  
22 -  
23 - @selenium  
24 - Scenario: provide the chat online users content  
25 - Given feature "xmpp_chat" is enabled on environment  
26 - And I am logged in as "tame"  
27 - Then I should see "Friends in chat "  
28 -  
29 - @selenium  
30 - Scenario: provide the chat online users list  
31 - Given the profile "tame" has no blocks  
32 - And feature "xmpp_chat" is enabled on environment  
33 - And I am logged in as "tame"  
34 - And I go to tame's profile  
35 - When I follow "chat-online-users-title"  
36 - Then I should see "Maria Silva"  
37 - And I should see "Jose Silva"  
38 -  
39 - Scenario: not provide link to chat when environment not support that  
40 - Given I am logged in as "tame"  
41 - Then I should not see "Open chat" within "#user"  
42 -  
43 - Scenario: not provide link to chat when the user is logged out  
44 - Given I am on tame's homepage  
45 - Then I should not see "Open chat" within "#user"  
46 -  
47 - @selenium  
48 - Scenario: not provide the chat online users list when environment not support that  
49 - Given I am logged in as "tame"  
50 - Then I should not see "Friends in chat "  
51 -  
52 - Scenario: block access to chat when environment not support that  
53 - Given I am logged in as "tame"  
54 - When I go to chat  
55 - Then I should see "There is no such page"  
56 -  
57 - Scenario: block access to chat for guest users  
58 - Given feature "xmpp_chat" is enabled on environment  
59 - When I go to chat  
60 - Then I should be on login page  
61 -  
62 - @selenium  
63 - Scenario: open chat in a new window  
64 - Given feature "xmpp_chat" is enabled on environment  
65 - And I am logged in as "tame"  
66 - When I follow "Open chat"  
67 - And I select window "noosfero_chat"  
68 - Then I should see "Chat - Colivre.net - Friends online (0)"  
69 -  
70 - @selenium  
71 - Scenario: open chat with an online user in a new window  
72 - Given the profile "tame" has no blocks  
73 - And feature "xmpp_chat" is enabled on environment  
74 - And I am logged in as "tame"  
75 - And I go to tame's profile  
76 - When I follow "chat-online-users-title"  
77 - And I follow "Maria Silva"  
78 - And I select window "noosfero_chat"  
79 - Then I should see "Chat - Colivre.net - Friends online (0)"  
80 -  
81 - @selenium  
82 - Scenario: chat starts disconnected by default  
83 - Given feature "xmpp_chat" is enabled on environment  
84 - And I am logged in as "tame"  
85 - When I follow "Open chat"  
86 - And I select window "noosfero_chat"  
87 - Then I should see "Offline" within "a"  
88 -  
89 - @selenium  
90 - Scenario: view options to change my chat status through menu  
91 - Given feature "xmpp_chat" is enabled on environment  
92 - And I am logged in as "tame"  
93 - And I follow "Open chat"  
94 - When I select window "noosfero_chat"  
95 - Then "Online" should not be visible within "#user-status"  
96 - And "Busy" should not be visible within "#user-status"  
97 - And "Sign out of chat" should not be visible within "#user-status"  
98 - When I follow "Offline"  
99 - Then "Online" should be visible within "#user-status"  
100 - And "Busy" should be visible within "#user-status"  
101 - And "Sign out of chat" should be visible within "#user-status"  
102 -  
103 - @selenium  
104 - Scenario: link to open chatroom of a community  
105 - Given the following communities  
106 - | identifier | name |  
107 - | autoramas | Autoramas |  
108 - And "Tame" is a member of "Autoramas"  
109 - And feature "xmpp_chat" is enabled on environment  
110 - And I am logged in as "tame"  
111 - When I go to autoramas's profile  
112 - Then I should see "Enter chat room"  
113 -  
114 - @selenium  
115 - Scenario: not see link to open chatroom of a community if not a member  
116 - Given the following communities  
117 - | identifier | name |  
118 - | autoramas | Autoramas |  
119 - And feature "xmpp_chat" is enabled on environment  
120 - And I am logged in as "tame"  
121 - When I go to autoramas's profile  
122 - Then I should not see "Enter chat room" within "a"  
123 -  
124 - @selenium  
125 - Scenario: not see link to open chatroom of a community if xmpp_chat disabled  
126 - Given the following communities  
127 - | identifier | name |  
128 - | autoramas | Autoramas |  
129 - And "Tame" is a member of "Autoramas"  
130 - And I am logged in as "tame"  
131 - When I go to autoramas's profile  
132 - Then I should not see "Enter chat room" within "a"  
133 -  
134 - @selenium  
135 - Scenario: open chatroom of a community in a new window  
136 - Given feature "xmpp_chat" is enabled on environment  
137 - And the following communities  
138 - | identifier | name |  
139 - | autoramas | Autoramas |  
140 - And "Tame" is a member of "Autoramas"  
141 - And I am logged in as "tame"  
142 - When I go to autoramas's profile  
143 - And I follow "Enter chat room"  
144 - And I select window "noosfero_chat"  
145 - Then I should see "Chat - Colivre.net - Friends online (0)"  
features/edit_profile.feature
@@ -27,44 +27,3 @@ Feature: edit profile @@ -27,44 +27,3 @@ Feature: edit profile
27 And I press "Save" 27 And I press "Save"
28 Then I should not see "Birth date is invalid" 28 Then I should not see "Birth date is invalid"
29 And I should not see "Birth date is mandatory" 29 And I should not see "Birth date is mandatory"
30 -  
31 - @selenium  
32 - Scenario: Alert about url change  
33 - Given the following community  
34 - | identifier | name | owner |  
35 - | o-rappa | O Rappa | joao |  
36 - And feature "enable_organization_url_change" is enabled on environment  
37 - And I go to o-rappa's control panel  
38 - When I follow "Community Info and settings"  
39 - Then I should not see "WARNING" within "#identifier-change-confirmation"  
40 - And I fill in "Address" with "banda-o-rappa"  
41 - When I leave the "#profile_data_identifier" field  
42 - Then I should see "WARNING" within "#identifier-change-confirmation"  
43 -  
44 - @selenium  
45 - Scenario: Confirm url change  
46 - Given the following community  
47 - | identifier | name | owner |  
48 - | o-rappa | O Rappa | joao |  
49 - And feature "enable_organization_url_change" is enabled on environment  
50 - And I go to o-rappa's control panel  
51 - And I follow "Community Info and settings"  
52 - And I fill in "Address" with "banda-o-rappa"  
53 - When I leave the "#profile_data_identifier" field  
54 - Then I should see "WARNING" within "#identifier-change-confirmation"  
55 - When I follow "Yes"  
56 - Then I should not see "WARNING" within "#identifier-change-confirmation"  
57 -  
58 - @selenium  
59 - Scenario: Cancel url change  
60 - Given the following community  
61 - | identifier | name | owner |  
62 - | o-rappa | O Rappa | joao |  
63 - And feature "enable_organization_url_change" is enabled on environment  
64 - And I go to o-rappa's control panel  
65 - And I follow "Community Info and settings"  
66 - And I fill in "Address" with "banda-o-rappa"  
67 - When I leave the "#profile_data_identifier" field  
68 - Then I should see "WARNING" within "#identifier-change-confirmation"  
69 - When I follow "No"  
70 - Then I should not see "WARNING" within "#identifier-change-confirmation"  
features/gallery_navigation.feature
@@ -1,86 +0,0 @@ @@ -1,86 +0,0 @@
1 -Feature: gallery_navigation  
2 - As a noosfero user  
3 - I want to navigate over image gallery  
4 -  
5 - Background:  
6 - Given the following users  
7 - | login |  
8 - | marciopunk |  
9 - And the following galleries  
10 - | owner | name |  
11 - | marciopunk | my-gallery |  
12 - | marciopunk | other-gallery |  
13 - And the following files  
14 - | owner | file | mime | parent |  
15 - | marciopunk | rails.png | image/png | my-gallery |  
16 - | marciopunk | rails.png | image/png | other-gallery |  
17 - | marciopunk | other-pic.jpg | image/jpeg | my-gallery |  
18 -  
19 - Scenario: provide link to go to next image  
20 - Given I am on /marciopunk/my-gallery/other-pic.jpg?view=true  
21 - Then I should see "Next »"  
22 -  
23 - @selenium  
24 - Scenario: view next image when follow next link  
25 - Given I am on /marciopunk/my-gallery/other-pic.jpg?view=true  
26 - When I follow "Next »"  
27 - Then I should see "rails.png" within ".title"  
28 -  
29 - Scenario: not link to next when in last image  
30 - When I am on /marciopunk/my-gallery/rails.png?view=true  
31 - Then I should see "« Previous" within ".gallery-navigation a"  
32 - And I should not see "Next »" within ".gallery-navigation a"  
33 -  
34 - Scenario: provide link to go to previous image  
35 - Given I am on /marciopunk/my-gallery/other-pic.jpg?view=true  
36 - Then I should see "« Previous"  
37 -  
38 - @selenium  
39 - Scenario: view previous image when follow previous link  
40 - Given I am on /marciopunk/my-gallery/rails.png?view=true  
41 - When I follow "« Previous"  
42 - Then I should see "other-pic.jpg" within ".title"  
43 -  
44 - Scenario: not link to previous when in first image  
45 - When I am on /marciopunk/my-gallery/other-pic.jpg?view=true  
46 - Then I should see "Next »" within ".gallery-navigation a"  
47 - And I should not see "« Previous" within ".gallery-navigation a"  
48 -  
49 - Scenario: display number of current and total of images  
50 - Given I am on /marciopunk/my-gallery/other-pic.jpg?view=true  
51 - Then I should see "image 1 of 2" within ".gallery-navigation"  
52 -  
53 - Scenario: increment current number when follow next  
54 - Given I am on /marciopunk/my-gallery/other-pic.jpg?view=true  
55 - Then I should see "image 1 of 2" within ".gallery-navigation"  
56 - When I follow "Next »"  
57 - Then I should see "image 2 of 2" within ".gallery-navigation"  
58 -  
59 - Scenario: decrement current number when follow next  
60 - Given I am on /marciopunk/my-gallery/rails.png?view=true  
61 - Then I should see "image 2 of 2" within ".gallery-navigation"  
62 - When I follow "« Previous"  
63 - Then I should see "image 1 of 2" within ".gallery-navigation"  
64 -  
65 - Scenario: provide button to go back to gallery  
66 - Given I am on /marciopunk/my-gallery/rails.png?view=true  
67 - Then I should see "Go back to my-gallery"  
68 - When I follow "Go back to my-gallery"  
69 - Then I should be on /marciopunk/my-gallery  
70 -  
71 - # Looking for page title is problematic on selenium since it considers the  
72 - # title to be invibible. Checkout some information about this:  
73 - # * https://github.com/jnicklas/capybara/issues/863  
74 - # * https://github.com/jnicklas/capybara/pull/953  
75 - @selenium  
76 - Scenario: image title in window title  
77 - Given I am logged in as "marciopunk"  
78 - When I go to /marciopunk/other-gallery/rails.png?view=true  
79 - Then I should see "rails.png" within any "h1"  
80 -# And the page title should be "rails.png"  
81 - And I follow "Edit"  
82 - And I fill in "Title" with "Rails is cool"  
83 - And I press "Save"  
84 - When I go to /marciopunk/other-gallery/rails.png?view=true  
85 - Then I should see "Rails is cool" within any "h1"  
86 - #Then the page title should be "Rails is cool"  
features/highlights_block.feature
@@ -1,44 +0,0 @@ @@ -1,44 +0,0 @@
1 -Feature: Edit Highlight Block  
2 - As a user  
3 - I want to edit the highlight block  
4 -  
5 - Background:  
6 - Given I am on the homepage  
7 - And the following users  
8 - | login | name |  
9 - | jose | Jose Silva |  
10 - And I am logged in as "jose"  
11 -  
12 - @selenium  
13 - Scenario: Add new highlight  
14 - Given I follow "Control panel"  
15 - And I follow "Edit sideboxes"  
16 - And I follow "Add a block"  
17 - And I choose "Highlights"  
18 - And I press "Add"  
19 - And I follow "Edit" within ".highlights-block"#Need to hover the mouse on the box  
20 - And I follow "New highlight"  
21 - And I fill in "block_images__address" with "/"  
22 - And I fill in "block_images__position" with "0"  
23 - And I fill in "block_images__title" with "test highlights"  
24 - And I press "Save"  
25 - And I follow "Edit" within ".highlights-block"  
26 - Then I should see "Title"  
27 -  
28 - @selenium-fixme  
29 - Scenario: Remove one saved highlight  
30 - Given I follow "Control panel"  
31 - And I follow "Edit sideboxes"  
32 - And I follow "Add a block"  
33 - And I choose "Highlights"  
34 - And I press "Add"  
35 - And I follow "Edit" within ".highlights-block"  
36 - And I follow "New highlight"  
37 - And I fill in "block_images__address" with "/"  
38 - And I fill in "block_images__position" with "0"  
39 - And I fill in "block_images__title" with "test highlights"#Need to hover the mouse on the box  
40 - And I press "Save"  
41 - And I follow "Edit" within ".highlights-block"  
42 - And I follow "" within ".delete-highlight"  
43 - And I confirm the browser dialog  
44 - Then I should not see "Title"  
45 \ No newline at end of file 0 \ No newline at end of file
features/http_caching.feature
@@ -1,69 +0,0 @@ @@ -1,69 +0,0 @@
1 -Feature: HTTP caching  
2 -  
3 - As a sysdamin  
4 - I want Noosfero to provide appropriate cache headers  
5 - So that Varnish can serve content from the cache, everything works faster and everyone is happy  
6 -  
7 - Background:  
8 - Given the following user  
9 - | login | name |  
10 - | joao | João Silva |  
11 -  
12 - Scenario: home page, default configuration  
13 - When I go to the homepage  
14 - Then the response should be valid for 5 minutes  
15 - And the cache should be public  
16 -  
17 - Scenario: home page, custom configuration  
18 - Given the following environment configuration  
19 - | home_cache_in_minutes | 10 |  
20 - When I go to the homepage  
21 - Then the response should be valid for 10 minutes  
22 -  
23 - Scenario: search results, default configuration  
24 - Given I am on the search page  
25 - When I fill in "query" with "anything"  
26 - And I press "Search"  
27 - Then the response should be valid for 15 minutes  
28 -  
29 - Scenario: search results, custom configuration  
30 - Given the following environment configuration  
31 - | general_cache_in_minutes | 90 |  
32 - When I go to the search page  
33 - And I fill in "query" with "anything"  
34 - And I press "Search"  
35 - Then the response should be valid for 90 minutes  
36 -  
37 - Scenario: profile pages, default configuaration  
38 - When I go to joao's homepage  
39 - Then the response should be valid for 15 minutes  
40 -  
41 - Scenario: profile pages, custom configuration  
42 - Given the following environment configuration  
43 - | profile_cache_in_minutes | 90 |  
44 - When I go to joao's homepage  
45 - Then the response should be valid for 90 minutes  
46 -  
47 - Scenario: account controller should not be cached at all  
48 - When I go to /account/login  
49 - Then there must be no cache at all  
50 -  
51 - Scenario: profile administration  
52 - Given I am logged in as "joao"  
53 - When I go to joao's control panel  
54 - Then there must be no cache at all  
55 -  
56 - Scenario: environment administration  
57 - Given I am logged in as admin  
58 - When I go to /admin  
59 - Then there must be no cache at all  
60 -  
61 - Scenario: logged in user in the homepage  
62 - Given I am logged in as "joao"  
63 - When I go to the homepage  
64 - Then there must be no cache at all  
65 -  
66 - Scenario: logged in user in a profile page  
67 - Given I am logged in as "joao"  
68 - When I go to /joao  
69 - Then there must be no cache at all  
features/manage_inputs.feature
@@ -1,247 +0,0 @@ @@ -1,247 +0,0 @@
1 -Feature: manage inputs  
2 - As an enterprise owner  
3 - I want to manage my product's inputs  
4 -  
5 - Background:  
6 - Given the following users  
7 - | login | name |  
8 - | joaosilva | Joao Silva |  
9 - And the following enterprises  
10 - | identifier | owner | name | enabled |  
11 - | redemoinho | joaosilva | Rede Moinho | true |  
12 - Given the following product_category  
13 - | name |  
14 - | Music |  
15 - And the following product_categories  
16 - | name | parent |  
17 - | Rock | music |  
18 - | CD Player | music |  
19 - And the following product  
20 - | owner | category | name |  
21 - | redemoinho | rock | Abbey Road |  
22 - And feature "products_for_enterprises" is enabled on environment  
23 - And the following units  
24 - | singular | plural |  
25 - | Meter | Meters |  
26 - | Litre | Litres |  
27 -  
28 - @selenium  
29 - Scenario: add first input to a product  
30 - Given I am logged in as "joaosilva"  
31 - When I go to Rede Moinho's page of product Abbey Road  
32 - And I follow "Inputs"  
33 - Then I should not see "Add new input or raw material"  
34 - And I follow "Add the inputs or raw material used by this product"  
35 - And I select "Music »" from "category_id" within "#categories_container_level0"  
36 - And I select "Rock" from "category_id" within "#categories_container_level1"  
37 - And I press "Save and continue"  
38 - Then I should see "Rock"  
39 -  
40 - @selenium  
41 - Scenario: add input to a product that already has inputs  
42 - Given the following input  
43 - | product | category |  
44 - | Abbey Road | music |  
45 - And I am logged in as "joaosilva"  
46 - When I go to Rede Moinho's page of product Abbey Road  
47 - And I follow "Inputs"  
48 - And I should not see "Add the inputs or raw material used by this product"  
49 - And I follow "Add new input or raw material"  
50 - And I select "Music »" from "category_id" within "#categories_container_level0"  
51 - And I select "Rock" from "category_id" within "#categories_container_level1"  
52 - And I press "Save and continue"  
53 - Then I should see "Rock"  
54 -  
55 - @selenium  
56 - Scenario: cancel addition of a product input  
57 - Given I am logged in as "joaosilva"  
58 - When I go to Rede Moinho's page of product Abbey Road  
59 - And I follow "Inputs"  
60 - And I follow "Add the inputs or raw material used by this product"  
61 - And I should see "Cancel" within "#categories_selection_actionbar"  
62 - And I follow "Cancel" within "#categories_selection_actionbar"  
63 - Then I should see "Abbey Road"  
64 - And I should see "Add the inputs or raw material used by this product"  
65 -  
66 - Scenario: show input name and link to add details  
67 - Given the following input  
68 - | product | category |  
69 - | Abbey Road | music |  
70 - And I am logged in as "joaosilva"  
71 - When I go to Rede Moinho's page of product Abbey Road  
72 - And I follow "Inputs and raw material"  
73 - Then I should see "Music" within ".input-name"  
74 - And I should see "Click here to add price and the amount used"  
75 -  
76 - Scenario: Not show input edit button when dont have details yet  
77 - Given the following input  
78 - | product | category |  
79 - | Abbey Road | music |  
80 - And I am logged in as "joaosilva"  
81 - When I go to Rede Moinho's page of product Abbey Road  
82 - And I follow "Inputs and raw material"  
83 - Then I should not see "Edit" within ".input-item"  
84 -  
85 - Scenario: Show button to edit input  
86 - Given the following input  
87 - | product | category | price_per_unit |  
88 - | Abbey Road | music | 10.0 |  
89 - And I am logged in as "joaosilva"  
90 - When I go to Rede Moinho's page of product Abbey Road  
91 - And I follow "Inputs and raw material"  
92 - Then I should see "Edit" within ".input-item"  
93 -  
94 - @selenium-fixme  
95 - Scenario: Order inputs by position  
96 - Given the following product_categories  
97 - | name |  
98 - | Instrumental |  
99 - And the following inputs  
100 - | product | category | position |  
101 - | Abbey Road | Instrumental | 2 |  
102 - | Abbey Road | Rock | 1 |  
103 - | Abbey Road | CD Player | 3 |  
104 - And I am logged in as "joaosilva"  
105 - When I go to Rede Moinho's page of product Abbey Road  
106 - And I follow "Inputs"  
107 - Then I should see "Rock" above of "Instrumental"  
108 - And I should see "Instrumental" above of "CD Player"  
109 -  
110 - @selenium  
111 - Scenario: Save price of input  
112 - Given the following input  
113 - | product | category |  
114 - | Abbey Road | music |  
115 - And I am logged in as "joaosilva"  
116 - When I go to Rede Moinho's page of product Abbey Road  
117 - And I follow "Inputs"  
118 - Then I should see "Music"  
119 - When I follow "Click here to add price and the amount used"  
120 - And I should see "Price"  
121 - And I fill in "Price" with "10.50"  
122 - And I press "Save"  
123 - Then I should not see "Save"  
124 -  
125 - @selenium  
126 - Scenario: Update label of input price with selected unit  
127 - Given the following input  
128 - | product | category |  
129 - | Abbey Road | music |  
130 - And I am logged in as "joaosilva"  
131 - When I go to Rede Moinho's page of product Abbey Road  
132 - And I follow "Inputs"  
133 - And I follow "Click here to add price and the amount used"  
134 - And I should not see "Price by Meter ($)"  
135 - When I select "Meter" from "input_unit_id" within ".edit_input"  
136 - Then I should see "Price by Meter ($)"  
137 -  
138 - @selenium  
139 - Scenario: Save all price details of input  
140 - Given the following input  
141 - | product | category |  
142 - | Abbey Road | music |  
143 - And I am logged in as "joaosilva"  
144 - When I go to Rede Moinho's page of product Abbey Road  
145 - And I follow "Inputs"  
146 - And I follow "Click here to add price and the amount used"  
147 - And I fill in "Amount used" with "2.5"  
148 - And I fill in "Price" with "11.50"  
149 - And I select "Meter" from "input_unit_id" within ".edit_input"  
150 - And I press "Save"  
151 - Then I should see "2.5"  
152 - And I should see "Meter"  
153 - And I should not see "$ 11.50"  
154 -  
155 - @selenium  
156 - Scenario: Save and then edit price details of input  
157 - Given the following input  
158 - | product | category |  
159 - | Abbey Road | music |  
160 - And I am logged in as "joaosilva"  
161 - When I go to Rede Moinho's page of product Abbey Road  
162 - And I follow "Inputs"  
163 - And I follow "Click here to add price and the amount used"  
164 - And I fill in "Amount used" with "2.5"  
165 - And I fill in "Price" with "11.50"  
166 - And I select "Meter" from "input_unit_id" within ".edit_input"  
167 - And I press "Save"  
168 - Then I should see "2.5"  
169 - And I should see "Meter"  
170 - When I follow "Edit" within ".input-details"  
171 - And I fill in "Amount used" with "3.0"  
172 - And I fill in "Price" with "23.31"  
173 - And I select "Litre" from "input_unit_id" within ".edit_input"  
174 - And I press "Save"  
175 - Then I should see "3"  
176 - And I should see "Litre"  
177 -  
178 - @selenium  
179 - Scenario: Cancel edition of a input  
180 - Given the following input  
181 - | product | category |  
182 - | Abbey Road | music |  
183 - And I am logged in as "joaosilva"  
184 - When I go to Rede Moinho's page of product Abbey Road  
185 - And I follow "Inputs"  
186 - And I follow "Click here to add price and the amount used"  
187 - Then I should see "Cancel"  
188 - And I should see "Amount used"  
189 - And I should see "Price"  
190 - And I should see "This input or raw material inpact on the final price of the product?"  
191 - When I follow "Cancel" within ".edit_input"  
192 - Then I should see "Click here to add price and the amount used"  
193 -  
194 - @selenium  
195 - Scenario: Cancel edition of an input then edit again  
196 - Given the following input  
197 - | product | category | price_per_unit | unit |  
198 - | Abbey Road | music | 10.0 | Meter |  
199 - And I am logged in as "joaosilva"  
200 - When I go to Rede Moinho's page of product Abbey Road  
201 - And I follow "Inputs"  
202 - And I follow "Edit" within ".input-details"  
203 - And I should see "Cancel" within ".edit_input"  
204 - And I follow "Cancel" within ".edit_input"  
205 - And I follow "Edit" within ".input-details"  
206 - Then I should see "Amount used"  
207 - And I should see "Price by Meter"  
208 -  
209 - @selenium  
210 - Scenario: remove input  
211 - Given the following input  
212 - | product | category |  
213 - | Abbey Road | rock |  
214 - And I am logged in as "joaosilva"  
215 - And I go to Rede Moinho's page of product Abbey Road  
216 - And I follow "Inputs"  
217 - Then I should see "Rock"  
218 - And I should not see "Add the inputs or raw material used by this product"  
219 - When I follow "Remove"  
220 - And I confirm the browser dialog  
221 - Then I should see "Add the inputs or raw material used by this product"  
222 -  
223 - @selenium-fixme  
224 - Scenario: Order input list  
225 - Given the following product_category  
226 - | name |  
227 - | Movie |  
228 - And the following product  
229 - | owner | category | name |  
230 - | redemoinho | Movie | Ramones |  
231 - And the following inputs  
232 - | product | category |  
233 - | Ramones | Rock |  
234 - | Ramones | Music |  
235 - | Ramones | CD Player |  
236 - And I am logged in as "joaosilva"  
237 - When I go to Rede Moinho's page of product Ramones  
238 - And I follow "Inputs"  
239 - Then I should see "Rock" above of "Music"  
240 - And I should see "Music" above of "CD Player"  
241 - When I drag "Rock" to "Music"  
242 - Then I should see "Music" above of "Rock"  
243 - And I should see "Rock" above of "CD Player"  
244 - When I follow "Back to the product listing"  
245 - And I go to Rede Moinho's page of product Ramones  
246 - Then I should see "Music" above of "Rock"  
247 - And I should see "Rock" above of "CD Player"  
features/manage_product_price_details.feature
@@ -1,179 +0,0 @@ @@ -1,179 +0,0 @@
1 -Feature: manage product price details  
2 - As an enterprise owner  
3 - I want to manage the details of product's price  
4 -  
5 - Background:  
6 - Given the following users  
7 - | login | name |  
8 - | joaosilva | Joao Silva |  
9 - And the following enterprises  
10 - | identifier | owner | name | enabled |  
11 - | redemoinho | joaosilva | Rede Moinho | true |  
12 - Given the following product_category  
13 - | name |  
14 - | Music |  
15 - And the following product_categories  
16 - | name | parent |  
17 - | Rock | music |  
18 - | CD Player | music |  
19 - And the following product  
20 - | owner | category | name | price |  
21 - | redemoinho | rock | Abbey Road | 80.0 |  
22 - And feature "products_for_enterprises" is enabled on environment  
23 - And the following inputs  
24 - | product | category | price_per_unit | amount_used |  
25 - | Abbey Road | Rock | 10.0 | 2 |  
26 - | Abbey Road | CD Player | 20.0 | 2 |  
27 - And the following production cost  
28 - | name | owner |  
29 - | Taxes | environment |  
30 - And I am logged in as "joaosilva"  
31 -  
32 - @selenium  
33 - Scenario: list total value of inputs as price details  
34 - Given I go to Rede Moinho's page of product Abbey Road  
35 - And I follow "Price composition"  
36 - And I follow "Describe here the cost of production"  
37 - Then I should see "Inputs"  
38 - And I should see "60.0" within ".inputs-cost"  
39 -  
40 - @selenium  
41 - Scenario: return to product after save  
42 - Given I go to Rede Moinho's page of product Abbey Road  
43 - And I follow "Price composition"  
44 - And I follow "Describe here the cost of production"  
45 - And I press "Save"  
46 - Then I should be on Rede Moinho's page of product Abbey Road  
47 -  
48 - @selenium  
49 - Scenario: add first item on price details  
50 - Given I go to Rede Moinho's page of product Abbey Road  
51 - And I follow "Price composition"  
52 - And I follow "Describe here the cost of production"  
53 - And I follow "New cost"  
54 - And I select "Taxes" from "price_details__production_cost_id" within "#display-product-price-details"  
55 - And I fill in "$" with "5.00"  
56 - And I leave the "#price_details__price" field  
57 - And I press "Save"  
58 - Then I should not see "Save"  
59 - And I should see "Describe here the cost of production"  
60 -  
61 - @selenium  
62 - Scenario: edit a production cost  
63 - Given the following production cost  
64 - | name | owner |  
65 - | Energy | environment |  
66 - When I go to Rede Moinho's page of product Abbey Road  
67 - And I follow "Price composition"  
68 - And I follow "Describe here the cost of production"  
69 - And I follow "New cost"  
70 - And I select "Taxes" from "price_details__production_cost_id" within "#display-product-price-details"  
71 - And I fill in "$" with "20.00"  
72 - And I leave the ".price-details-price" field  
73 - And I press "Save"  
74 - Then I should not see "Save"  
75 - And I should see "Taxes" within "#display-price-details"  
76 - When I follow "Describe here the cost of production"  
77 - And I select "Energy" from "price_details__production_cost_id" within "#display-product-price-details"  
78 - And I leave the "#price_details__price" field  
79 - And I press "Save"  
80 - And I should not see "Taxes" within "#display-price-details"  
81 - And I should see "Energy" within "#display-price-details"  
82 -  
83 - Scenario: not display price composition if product does not have input  
84 - Given the following product  
85 - | owner | category | name |  
86 - | redemoinho | rock | Yellow Submarine |  
87 - And the following user  
88 - | login | name |  
89 - | mariasouza | Maria Souza |  
90 - And I am logged in as "mariasouza"  
91 - When I go to Rede Moinho's page of product Yellow Submarine  
92 - Then I should not see "Price composition"  
93 -  
94 - Scenario: not display price composition if price is not fully described  
95 - Given I am not logged in  
96 - And I go to Rede Moinho's page of product Abbey Road  
97 - Then I should not see "Price composition"  
98 -  
99 - @selenium  
100 - Scenario: display price details if price is fully described  
101 - Given I go to Rede Moinho's page of product Abbey Road  
102 - And I follow "Price composition"  
103 - And I follow "Describe here the cost of production"  
104 - And I follow "New cost"  
105 - And I select "Taxes" from "price_details__production_cost_id" within "#display-product-price-details"  
106 - And I fill in "$" with "20.00"  
107 - And I press "Save"  
108 - And I go to Rede Moinho's page of product Abbey Road  
109 - Then I should see "Inputs" within ".price-detail-name"  
110 - And I should see "60.0" within ".price-detail-price"  
111 -  
112 - @selenium  
113 - Scenario: create a new cost clicking on select  
114 - Given I go to Rede Moinho's page of product Abbey Road  
115 - And I follow "Price composition"  
116 - And I follow "Describe here the cost of production"  
117 - And I follow "New cost"  
118 - And I select "Other cost" from "price_details__production_cost_id" within "#display-product-price-details"  
119 - And I want to add "Energy" as cost  
120 - And I fill in "$" with "10.00"  
121 - And I leave the "#price_details__price" field  
122 - And I press "Save"  
123 - When I follow "Describe here the cost of production"  
124 - Then I should see "Energy" within ".production-cost-selection"  
125 -  
126 - @selenium  
127 - Scenario: add created cost on new-cost-fields  
128 - Given I go to Rede Moinho's page of product Abbey Road  
129 - And I follow "Price composition"  
130 - And I follow "Describe here the cost of production"  
131 - And I follow "New cost"  
132 - And I select "Other cost" from "price_details__production_cost_id" within "#display-product-price-details"  
133 - And I want to add "Energy" as cost  
134 - Then I should see "Energy" within "#display-product-price-details"  
135 -  
136 - @selenium  
137 - Scenario: remove price detail  
138 - Given the following price detail  
139 - | product | production_cost | price |  
140 - | Abbey Road | Taxes | 20.0 |  
141 - And I go to Rede Moinho's page of product Abbey Road  
142 - And I follow "Price composition"  
143 - And I follow "Describe here the cost of production"  
144 - And I should see "Taxes" within "#manage-product-details-form"  
145 - When I follow "Remove" within "#manage-product-details-form"  
146 - And I confirm the browser dialog  
147 - And I press "Save"  
148 - And I follow "Describe here the cost of production"  
149 - Then I should not see "Taxes" within "#manage-product-details-form"  
150 -  
151 - Scenario: display progressbar  
152 - Given I go to Rede Moinho's page of product Abbey Road  
153 - And I follow "Price composition"  
154 - And I follow "Describe here the cost of production"  
155 - Then I should see "$ 60.00 of $ 80.00" within "#progressbar-text"  
156 -  
157 - @selenium  
158 - Scenario: update value on progressbar after addition of new cost  
159 - Given I go to Rede Moinho's page of product Abbey Road  
160 - And I follow "Price composition"  
161 - And I follow "Describe here the cost of production"  
162 - Then I should see "$ 60.00 of $ 80.00" within "#progressbar-text"  
163 - And I follow "New cost"  
164 - And I fill in "$" with "10.00"  
165 - And I leave the "#price_details__price" field  
166 - Then I should see "$ 70.00 of $ 80.00" within "#progressbar-text"  
167 -  
168 - @selenium  
169 - Scenario: update value on progressbar after editing an input  
170 - Given I go to Rede Moinho's page of product Abbey Road  
171 - And I follow "Price composition"  
172 - And I follow "Describe here the cost of production"  
173 - Then I should see "$ 60.00 of $ 80.00" within "#progressbar-text"  
174 - When I follow "Inputs"  
175 - And I follow "Edit" within ".input-details"  
176 - And I fill in "Price" with "23.31"  
177 - And I press "Save"  
178 - Then I follow "Price composition"  
179 - And I should see "$ 86.62 of $ 80.00" within "#progressbar-text"  
features/manage_products.feature
@@ -1,520 +0,0 @@ @@ -1,520 +0,0 @@
1 -Feature: manage products  
2 - As an enterprise owner  
3 - I want to manage my products  
4 -  
5 - Background:  
6 - Given the following users  
7 - | login | name |  
8 - | joaosilva | Joao Silva |  
9 - And the following enterprises  
10 - | identifier | owner | name | enabled |  
11 - | redemoinho | joaosilva | Rede Moinho | true |  
12 - And feature "products_for_enterprises" is enabled on environment  
13 -  
14 - Scenario: display "create new product" button  
15 - Given I am logged in as "joaosilva"  
16 - And I am on redemoinho's control panel  
17 - When I follow "Manage Products/Services"  
18 - Then I should see "New product or service"  
19 -  
20 - Scenario: paginate public listing products and services  
21 - Given the following product_category  
22 - | name |  
23 - | Bicycle |  
24 - And the following products  
25 - | owner | category | name | description | created_at |  
26 - | redemoinho | bicycle | Bike A | bicycle 1 | 2014-04-01 01:00:00 |  
27 - | redemoinho | bicycle | Bike B | bicycle 2 | 2014-04-01 02:00:00 |  
28 - | redemoinho | bicycle | Bike C | bicycle 3 | 2014-04-01 03:00:00 |  
29 - | redemoinho | bicycle | Bike D | bicycle 4 | 2014-04-01 04:00:00 |  
30 - | redemoinho | bicycle | Bike E | bicycle 5 | 2014-04-01 05:00:00 |  
31 - | redemoinho | bicycle | Bike F | bicycle 6 | 2014-04-01 06:00:00 |  
32 - | redemoinho | bicycle | Bike G | bicycle 7 | 2014-04-01 07:00:00 |  
33 - | redemoinho | bicycle | Bike H | bicycle 8 | 2014-04-01 08:00:00 |  
34 - | redemoinho | bicycle | Bike I | bicycle 9 | 2014-04-01 09:00:00 |  
35 - | redemoinho | bicycle | Bike J | bicycle 10 | 2014-04-01 10:00:00 |  
36 - | redemoinho | bicycle | Bike K | bicycle 11 | 2014-04-01 11:00:00 |  
37 - When I go to redemoinho's products page  
38 - Then I should see "Bike A" within "#product-list"  
39 - And I should see "Bike B" within "#product-list"  
40 - And I should see "Bike C" within "#product-list"  
41 - And I should see "Bike D" within "#product-list"  
42 - And I should see "Bike E" within "#product-list"  
43 - And I should see "Bike F" within "#product-list"  
44 - And I should not see "Bike G" within "#product-list"  
45 - And I should not see "Bike H" within "#product-list"  
46 - And I should not see "Bike I" within "#product-list"  
47 - And I should not see "Bike J" within "#product-list"  
48 - And I should not see "Bike K" within "#product-list"  
49 - When I follow "Next"  
50 - Then I should see "Bike G" within "#product-list"  
51 - Then I should see "Bike H" within "#product-list"  
52 - Then I should see "Bike I" within "#product-list"  
53 - Then I should see "Bike J" within "#product-list"  
54 - Then I should see "Bike K" within "#product-list"  
55 -  
56 - Scenario: listing products and services  
57 - Given I am logged in as "joaosilva"  
58 - And I am on redemoinho's control panel  
59 - And I follow "Manage Products/Services"  
60 - Then I should see "Listing products and services"  
61 -  
62 - Scenario: see button to back in categories hierarchy  
63 - Given I am logged in as "joaosilva"  
64 - And I am on redemoinho's control panel  
65 - And I follow "Manage Products/Services"  
66 - When I follow "New product or service"  
67 - Then I should see "Back to the product listing" link  
68 -  
69 - Scenario: see toplevel categories  
70 - Given the following product_categories  
71 - | name |  
72 - | Products |  
73 - | Services |  
74 - Given I am logged in as "joaosilva"  
75 - And I go to redemoinho's new product page  
76 - Then I should see "Products"  
77 - And I should see "Service"  
78 -  
79 - @selenium  
80 - Scenario: select a toplevel category and see subcategories  
81 - Given the following product_categories  
82 - | name |  
83 - | Products level0 |  
84 - And the following product_categories  
85 - | name | parent |  
86 - | Computers level1 | products-level0 |  
87 - | DVDs level1 | products-level0 |  
88 - Given I am logged in as "joaosilva"  
89 - And I go to redemoinho's new product page  
90 - And I select "Products level0 »" from "category_id" within "#categories_container_level0"  
91 - Then I should see "Computers level1"  
92 - And I should see "DVDs level1"  
93 -  
94 - @selenium  
95 - Scenario: hide subcategories when select other toplevel category  
96 - Given the following product_categories  
97 - | name |  
98 - | Products level0 |  
99 - | Services level0 |  
100 - And the following product_categories  
101 - | name | parent |  
102 - | Computers level1 | products-level0 |  
103 - | Software development level1 | services-level0 |  
104 - Given I am logged in as "joaosilva"  
105 - And I go to redemoinho's new product page  
106 - And I should not see /Computers level/  
107 - And I select "Products level0 »" from "category_id" within "#categories_container_wrapper"  
108 - And I should see /Computers level/  
109 - And I should not see /Software develop/  
110 - And I select "Services level0 »" from "category_id" within "#categories_container_wrapper"  
111 - Then I should see /Software develop/  
112 - And I should not see /Computers level/  
113 -  
114 - @selenium  
115 - Scenario: show hierarchy of categories  
116 - Given the following product_categories  
117 - | name |  
118 - | Products |  
119 - And the following product_category  
120 - | name | parent |  
121 - | Computers | products |  
122 - Given I am logged in as "joaosilva"  
123 - And I go to redemoinho's new product page  
124 - And I select "Products »" from "category_id" within "#categories_container_level0"  
125 - And I select "Computers" from "category_id" within "#categories_container_level1"  
126 - Then I should see "Products → Computers"  
127 -  
128 - @selenium  
129 - Scenario: show links in hierarchy of categories and not link current category  
130 - Given the following product_category  
131 - | name |  
132 - | Toplevel Product Categories |  
133 - Given the following product_category  
134 - | name | parent |  
135 - | Category Level 1 | toplevel-product-categories |  
136 - Given I am logged in as "joaosilva"  
137 - And I go to redemoinho's new product page  
138 - And I select "Toplevel Product Categories »" from "category_id" within "#categories_container_level0"  
139 - And I select "Category Level 1" from "category_id" within "#categories_container_level1"  
140 - Then I should see "Toplevel Product Categories" link  
141 - And I should not see "Category Level 1" link  
142 -  
143 - @selenium  
144 - Scenario: save button come initialy disabled  
145 - Given the following product_category  
146 - | name |  
147 - | Only for test |  
148 - And I am logged in as "joaosilva"  
149 - When I go to redemoinho's new product page  
150 - Then the "#save_and_continue" button should be disabled  
151 -  
152 - @selenium  
153 - Scenario: enable save button when select one category  
154 - Given I am logged in as "joaosilva"  
155 - And the following product_category  
156 - | name |  
157 - | Browsers (accept categories) |  
158 - When I go to redemoinho's new product page  
159 - And I select "Browsers (accept categories)" from "category_id" within "#categories_container_wrapper"  
160 - Then the "#save_and_continue" button should be enabled  
161 -  
162 - @selenium  
163 - Scenario: dont enable save button when select category with not accept products  
164 - Given the following product_category  
165 - | name | accept_products |  
166 - | Browsers | false |  
167 - Given I am logged in as "joaosilva"  
168 - When I go to redemoinho's new product page  
169 - And I select "Browsers" from "category_id" within "#categories_container_wrapper"  
170 - Then the "#save_and_continue" button should be disabled  
171 -  
172 - @selenium  
173 - Scenario: save product  
174 - Given the following product_category  
175 - | name |  
176 - | Bicycle |  
177 - Given I am logged in as "joaosilva"  
178 - When I go to redemoinho's new product page  
179 - And I select "Bicycle" from "category_id" within "#categories_container_wrapper"  
180 - And I press "Save and continue"  
181 - When I go to redemoinho's products page  
182 - And I follow "Bicycle" within "#product-list"  
183 - Then I should see "Bicycle" within "#show_product"  
184 - And I should see "Change category"  
185 -  
186 - Scenario: a user with permission can see edit links  
187 - Given the following product_category  
188 - | name |  
189 - | Bicycle |  
190 - And the following products  
191 - | owner | category | name | description |  
192 - | redemoinho | bicycle | Bike | Red bicycle |  
193 - And I am logged in as "joaosilva"  
194 - When I go to Rede Moinho's page of product Bike  
195 - Then I should see "Change category"  
196 - And I should see "Edit name"  
197 - And I should see "Edit description"  
198 - And I should see "Change image"  
199 -  
200 - Scenario: an allowed user will see a different button when has no description  
201 - Given the following product_category  
202 - | name |  
203 - | Bicycle |  
204 - And the following products  
205 - | owner | category | name |  
206 - | redemoinho | bicycle | Bike |  
207 - And I am logged in as "joaosilva"  
208 - When I go to Rede Moinho's page of product Bike  
209 - Then I should see "Change category"  
210 - And I should see "Edit name"  
211 - And I should see "Add some description to your product"  
212 - And I should see "Add price and other basic information"  
213 - And I should see "Change image"  
214 -  
215 - Scenario: an allowed user will see a different button when has no basic info  
216 - Given the following product_category  
217 - | name |  
218 - | Bicycle |  
219 - And the following products  
220 - | owner | category | name |  
221 - | redemoinho | bicycle | Bike |  
222 - And I am logged in as "joaosilva"  
223 - When I go to Rede Moinho's page of product Bike  
224 - Then I should see "Change category"  
225 - And I should see "Edit name"  
226 - And I should see "Add price and other basic information"  
227 - And I should see "Change image"  
228 -  
229 - Scenario: a not logged user cannot see edit links  
230 - Given I am not logged in  
231 - And the following product_category  
232 - | name |  
233 - | Bicycle |  
234 - And the following products  
235 - | owner | category | name | description |  
236 - | redemoinho | bicycle | Bike | Red bicycle |  
237 - When I go to Rede Moinho's page of product Bike  
238 - Then I should not see "Change category"  
239 - And I should not see "Edit name"  
240 - And I should not see "Edit description"  
241 - And I should not see "Edit basic information"  
242 - And I should not see "Change image"  
243 -  
244 - Scenario: a not allowed user cannot see edit links  
245 - Given the following users  
246 - | login | name |  
247 - | mariasantos | Maria Santos |  
248 - And the following product_category  
249 - | name |  
250 - | Bicycle |  
251 - And the following products  
252 - | owner | category | name | description |  
253 - | redemoinho | bicycle | Bike | Red bicycle |  
254 - And I am logged in as "mariasantos"  
255 - When I go to Rede Moinho's page of product Bike  
256 - Then I should not see "Change category"  
257 - And I should not see "Edit name"  
258 - And I should not see "Edit description"  
259 - And I should not see "Edit basic information"  
260 - And I should not see "Change image"  
261 -  
262 - @selenium  
263 - Scenario: edit name of a product  
264 - Given the following product_category  
265 - | name |  
266 - | Bicycle |  
267 - And the following products  
268 - | owner | category | name |  
269 - | redemoinho | bicycle | Bike |  
270 - And I am logged in as "joaosilva"  
271 - When I go to Rede Moinho's page of product Bike  
272 - And I follow "Edit name"  
273 - And I fill in "Red bicycle" for "product_name"  
274 - And I press "Save"  
275 - Then I should see "Red bicycle"  
276 - And I should be on Rede Moinho's page of product Red bicycle  
277 -  
278 - @selenium  
279 - Scenario: cancel edition of a product name  
280 - Given the following product_category  
281 - | name |  
282 - | Bicycle |  
283 - And the following products  
284 - | owner | category | name |  
285 - | redemoinho | bicycle | Bike |  
286 - And I am logged in as "joaosilva"  
287 - When I go to Rede Moinho's page of product Bike  
288 - And I follow "Edit name"  
289 - When I follow "Cancel"  
290 - Then I should see "Bike"  
291 -  
292 - @selenium  
293 - Scenario: edit category of a product  
294 - Given the following product_category  
295 - | name |  
296 - | Eletronics |  
297 - And the following product_categories  
298 - | name | parent |  
299 - | Computers | eletronics |  
300 - | DVDs | eletronics |  
301 - And the following products  
302 - | owner | category | name |  
303 - | redemoinho | computers | Generic pc |  
304 - And I am logged in as "joaosilva"  
305 - When I go to Rede Moinho's page of product Generic pc  
306 - And I follow "Change category"  
307 - And I select "Eletronics »" from "category_id" within "#categories_container_level0"  
308 - Then I select "DVDs" from "category_id" within "#categories_container_level1"  
309 - And I press "Save and continue"  
310 - When I go to Rede Moinho's page of product Generic pc  
311 - Then I should see "Eletronics → DVDs" within ".hierarchy-category"  
312 -  
313 - @selenium  
314 - Scenario: cancel edition of a product category  
315 - Given the following product_category  
316 - | name |  
317 - | Eletronics |  
318 - And the following product_categories  
319 - | name | parent |  
320 - | Computers | eletronics |  
321 - | DVDs | eletronics |  
322 - And the following products  
323 - | owner | category | name |  
324 - | redemoinho | computers | Generic pc |  
325 - And I am logged in as "joaosilva"  
326 - When I go to Rede Moinho's page of product Generic pc  
327 - And I follow "Change category"  
328 - When I follow "Back to product"  
329 - Then I should see "Eletronics → Computers"  
330 -  
331 -  
332 - @selenium  
333 - Scenario: edit image of a product  
334 - Given the following product_category  
335 - | name |  
336 - | Eletronics |  
337 - And the following product_categories  
338 - | name | parent |  
339 - | Computers | eletronics |  
340 - | DVDs | eletronics |  
341 - And the following products  
342 - | owner | category | name |  
343 - | redemoinho | computers | Generic pc |  
344 - And I am logged in as "joaosilva"  
345 - When I go to Rede Moinho's page of product Generic pc  
346 - And I follow "Change image"  
347 - When I follow "Cancel"  
348 - Then I should be on Rede Moinho's page of product Generic pc  
349 -  
350 - # FIXME Not working because of tinyMCE plus selenium  
351 - # @selenium  
352 - # Scenario: edit description of a product  
353 - # Given the following product_category  
354 - # | name |  
355 - # | Bicycle |  
356 - # And the following products  
357 - # | owner | category | name | description |  
358 - # | redemoinho | bicycle | Bike | A new red bicycle |  
359 - # And I am logged in as "joaosilva"  
360 - # When I go to Rede Moinho's page of product Bike  
361 - # Then I should see "A new red bicycle"  
362 - # And I follow "Edit basic information"  
363 - # And I type in tinyMCE field "Description" the text "An used red bicycle"  
364 - # And I press "Save"  
365 - # Then I should not see "A new red bicycle"  
366 - # And I should see "An used red bicycle"  
367 - # And I should be on Rede Moinho's page of product Bike  
368 -  
369 - @selenium  
370 - Scenario: cancel edition of a product description  
371 - Given the following product_category  
372 - | name |  
373 - | Bicycle |  
374 - And the following products  
375 - | owner | category | name | description |  
376 - | redemoinho | bicycle | Bike | A new red bicycle |  
377 - And I am logged in as "joaosilva"  
378 - When I go to Rede Moinho's page of product Bike  
379 - Then I should see "A new red bicycle"  
380 - And I follow "Edit description"  
381 - When I follow "Cancel"  
382 - Then I should see "A new red bicycle"  
383 - And I should be on Rede Moinho's page of product Bike  
384 -  
385 - @selenium  
386 - Scenario: Edit product category and save without select any category  
387 - Given the following product_category  
388 - | name |  
389 - | Eletronics |  
390 - And the following product_category  
391 - | name | parent |  
392 - | Computers | eletronics |  
393 - And the following products  
394 - | owner | category | name |  
395 - | redemoinho | computers | Generic pc |  
396 - And I am logged in as "joaosilva"  
397 - When I go to Rede Moinho's page of product Generic pc  
398 - And I follow "Change category"  
399 - And I press "Save and continue"  
400 - Then I should not see "Product category can't be blank"  
401 - When I go to Rede Moinho's page of product Generic pc  
402 - Then I should see "Eletronics → Computers" within ".hierarchy-category"  
403 -  
404 - And I should see "Generic pc"  
405 -  
406 - @selenium  
407 - Scenario: Scroll categories selection to right when editing  
408 - Given the following product_category  
409 - | name |  
410 - | Eletronics |  
411 - And the following product_category  
412 - | name | parent |  
413 - | Quantum Computers | eletronics |  
414 - And the following product_category  
415 - | name | parent |  
416 - | Laptops from Mars | Quantum Computers |  
417 - And the following product_category  
418 - | name | parent |  
419 - | Netbook from Venus | Laptops from Mars |  
420 - And the following product_category  
421 - | name | parent |  
422 - | Nanonote nanotech with long name | Netbook from Venus |  
423 - And the following products  
424 - | owner | category | name |  
425 - | redemoinho | Nanonote nanotech with long name | Generic pc |  
426 - And I am logged in as "joaosilva"  
427 - When I go to Rede Moinho's page of product Generic pc  
428 - And I follow "Change category"  
429 - Then "Netbook from Venus" should be visible within "#categories_container_wrapper"  
430 -  
431 - @selenium  
432 - Scenario: Truncate long category name in selection of category  
433 - Given the following product_category  
434 - | name |  
435 - | Super Quantum Computers with teraflops |  
436 - | Nanonote nanotech with long long name |  
437 - And the following product_category  
438 - | name | parent |  
439 - | Netbook Quantum | Super Quantum Computers with teraflops |  
440 - And I am logged in as "joaosilva"  
441 - When I go to redemoinho's new product page  
442 - Then I should see "Nanonote nanotech with long lo..."  
443 - And I should see "Super Quantum Computers with t... »"  
444 -  
445 - @selenium  
446 - Scenario: Edit unit of a product together your name  
447 - Given the following product_category  
448 - | name |  
449 - | Bicycle |  
450 - And the following products  
451 - | owner | category | name |  
452 - | redemoinho | bicycle | Bike |  
453 - And the following units  
454 - | singular | plural |  
455 - | Kilo | Kilos |  
456 - And I am logged in as "joaosilva"  
457 - When I go to Rede Moinho's page of product Bike  
458 - And I follow "Edit name and unit"  
459 - And I fill in "Red bicycle" for "product_name"  
460 - And I select "Kilo" from "product_unit_id" within "#product-name-form"  
461 - And I press "Save"  
462 - Then I should see "Red bicycle - kilo"  
463 -  
464 - @selenium  
465 - Scenario: Show info about unavailable product  
466 - Given the following product_category  
467 - | name |  
468 - | Bicycle |  
469 - And the following products  
470 - | owner | category | name |  
471 - | redemoinho | bicycle | Bike |  
472 - And I am logged in as "joaosilva"  
473 - When I go to Rede Moinho's page of product Bike  
474 - And I follow "Add price and other basic information"  
475 - And I fill in "10" for "product_price"  
476 - And I choose "No"  
477 - And I press "Save"  
478 - Then I should see "Product not available!"  
479 -  
480 - @selenium  
481 - Scenario: Add and remove some qualifiers  
482 - Given the following product_category  
483 - | name |  
484 - | Bicycle |  
485 - And the following products  
486 - | owner | category | name |  
487 - | redemoinho | bicycle | Bike |  
488 - And the following qualifiers  
489 - | name |  
490 - | Organic |  
491 - And the following certifiers  
492 - | name | qualifiers |  
493 - | Colivre | Organic |  
494 - And I am logged in as "joaosilva"  
495 - When I go to Rede Moinho's page of product Bike  
496 - And I follow "Add price and other basic information"  
497 - And I follow "Add new qualifier"  
498 - And I select "Organic" from "selected_qualifier" within "#product-qualifiers-list"  
499 - And I press "Save"  
500 - And I go to Rede Moinho's page of product Bike  
501 - Then I should see "Organic (Self declared)"  
502 - When I follow "Edit basic information"  
503 - And I follow "Delete qualifier"  
504 - And I press "Save"  
505 - And I go to Rede Moinho's page of product Bike  
506 - Then I should not see "Organic (Self declared)"  
507 -  
508 - @selenium  
509 - Scenario: Show checkbox to mark product as highlight  
510 - Given the following product_category  
511 - | name |  
512 - | Bicycle |  
513 - And the following products  
514 - | owner | category | name |  
515 - | redemoinho | bicycle | Bike |  
516 - And I am logged in as "joaosilva"  
517 - When I go to Rede Moinho's page of product Bike  
518 - And I follow "Add price and other basic information"  
519 - Then I should see "Highlight this product?"  
520 - And I check "Highlight this product?"  
features/my_network_block.feature
@@ -1,79 +0,0 @@ @@ -1,79 +0,0 @@
1 -Feature: my_network_block  
2 - As a blog owner  
3 - I want to see a summary of my network  
4 -  
5 - Background:  
6 - Given the following users  
7 - | login | name |  
8 - | joaosilva | Joao Silva |  
9 - And the following blocks  
10 - | owner | type |  
11 - | joaosilva | MyNetworkBlock |  
12 - | joaosilva | FriendsBlock |  
13 - And the following communities  
14 - | identifier | name | public_profile |  
15 - | public-community | Public Community | true |  
16 - And plugin FriendsBlock is enabled on environment  
17 -  
18 - @selenium  
19 - Scenario: display how many public/private communities I am member  
20 - Given I am logged in as "joaosilva"  
21 - And the following communities  
22 - | identifier | name | owner | public_profile |  
23 - | other-public-community | Other Public Community | joaosilva | true |  
24 - | private-community | Private Community | joaosilva | false |  
25 - And I am on joaosilva's homepage  
26 - And I should see "2 communities"  
27 - When I go to public-community's homepage  
28 - And I follow "Join"  
29 - And I go to joaosilva's homepage  
30 - Then I should see "3 communities"  
31 -  
32 - @selenium  
33 - Scenario: not display how many invisible communities I am member  
34 - Given I am logged in as "joaosilva"  
35 - And the following communities  
36 - | identifier | name | owner | visible |  
37 - | visible-community | Visible Community | joaosilva | true |  
38 - | not-visible-community | Not Visible Community | joaosilva | false |  
39 - And I am on joaosilva's homepage  
40 - And I should see "One community"  
41 - When I go to public-community's homepage  
42 - And I follow "Join"  
43 - And I go to joaosilva's homepage  
44 - Then I should see "2 communities"  
45 -  
46 - Scenario: display how many public/private friends I have  
47 - Given the following users  
48 - | login | name | public_profile |  
49 - | mariasilva | Maria Silva | true |  
50 - | josesilva | Jose Silva | false |  
51 - And the following blocks  
52 - | owner | type |  
53 - | mariasilva | FriendsBlock |  
54 - | josesilva | FriendsBlock |  
55 - And "joaosilva" is friend of "mariasilva"  
56 - And I am logged in as "joaosilva"  
57 - And I am on joaosilva's homepage  
58 - Then I should see "1 friend"  
59 - And "joaosilva" is friend of "josesilva"  
60 - When I go to joaosilva's homepage  
61 - Then I should see "2 friends"  
62 -  
63 - Scenario: not display how many invisible friends I have  
64 - Given the following users  
65 - | login | name |  
66 - | mariasilva | Maria Silva |  
67 - | josesilva | Jose Silva |  
68 - And the following blocks  
69 - | owner | type |  
70 - | mariasilva | FriendsBlock |  
71 - | josesilva | FriendsBlock |  
72 - And "josesilva" is invisible  
73 - And "joaosilva" is friend of "mariasilva"  
74 - And I am logged in as "joaosilva"  
75 - When I go to joaosilva's homepage  
76 - Then I should see "1 friend"  
77 - And "joaosilva" is friend of "josesilva"  
78 - When I go to joaosilva's homepage  
79 - Then I should see "1 friend"  
features/profile_tags.feature
@@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
1 -Feature: profile tags  
2 - As a Noosfero user  
3 - I want to to view content tagged  
4 - So that I can follow the subjects I care about  
5 -  
6 - Background:  
7 - Given the following users  
8 - | login |  
9 - | terceiro |  
10 - And the following articles  
11 - | owner | name | body | tag_list |  
12 - | terceiro | text 1 | text 1 content | tag1, tag2 |  
13 - | terceiro | text 2 | text 2 content | tag1, tag3 |  
14 -  
15 - Scenario: tag feed  
16 - When I go to terceiro's profile  
17 - And I follow "tag1"  
18 - And I follow "Feed for this tag"  
19 - Then I should see "text 1"  
20 - And I should see "text 2"  
features/session_and_cookies_handling.feature
@@ -1,40 +0,0 @@ @@ -1,40 +0,0 @@
1 -Feature: session and cookies handling  
2 -  
3 - As a Noosfero system administrator  
4 - I want Noosfero to manage well it usage of sessions and cookies  
5 - So that we can use HTTP caching effectively  
6 -  
7 - @fixme  
8 - Scenario: home page, logged in  
9 - Given the following users  
10 - | login |  
11 - | joaosilva |  
12 - When I am logged in as "joaosilva"  
13 - And I go to the homepage  
14 - Then there must be a cookie "_noosfero_session"  
15 -  
16 - Scenario: home page, not logged in  
17 - When I go to the homepage  
18 - Then there must be no cookies  
19 -  
20 - @fixme  
21 - Scenario: user_data, not logged in  
22 - When I make a AJAX request to the user data path  
23 - Then there must be no cookies  
24 -  
25 - @fixme  
26 - Scenario: user_data, logged in  
27 - Given I am logged in as admin  
28 - When I make a AJAX request to the user data path  
29 - Then there must be a cookie "_noosfero_session"  
30 -  
31 - # FIXME for some reason I could not test this scenario, although manual tests  
32 - # indicate this works!  
33 - # Scenario: logout  
34 - # Given the following users  
35 - # | login |  
36 - # | joao |  
37 - # When I am logged in as "joao"  
38 - # And I log off  
39 - # And I go to the homepage  
40 - # Then there must be no cookies  
features/signup.feature
@@ -293,28 +293,6 @@ Feature: signup @@ -293,28 +293,6 @@ Feature: signup
293 Then "José da Silva" should be a member of "Free Software" 293 Then "José da Silva" should be a member of "Free Software"
294 294
295 @selenium 295 @selenium
296 - Scenario: join community on direct signup  
297 - Given the following users  
298 - | login | name |  
299 - | mariasilva | Maria Silva |  
300 - And the following communities  
301 - | name | identifier | owner |  
302 - | Free Software | freesoftware | mariasilva |  
303 - And feature "skip_new_user_email_confirmation" is enabled on environment  
304 - And I am on /freesoftware  
305 - When I follow "Join"  
306 - And I follow "New user"  
307 - And I fill in the following within ".no-boxes":  
308 - | e-Mail | josesilva@example.com |  
309 - | Username | josesilva |  
310 - | Password | secret |  
311 - | Password confirmation | secret |  
312 - | Full name | José da Silva |  
313 - And wait for the captcha signup time  
314 - And I press "Create my account"  
315 - Then "José da Silva" should be a member of "Free Software"  
316 -  
317 - @selenium  
318 Scenario: user registration is moderated by admin 296 Scenario: user registration is moderated by admin
319 Given feature "admin_must_approve_new_users" is enabled on environment 297 Given feature "admin_must_approve_new_users" is enabled on environment
320 And feature "skip_new_user_email_confirmation" is disabled on environment 298 And feature "skip_new_user_email_confirmation" is disabled on environment
@@ -364,4 +342,4 @@ Feature: signup @@ -364,4 +342,4 @@ Feature: signup
364 And I fill in "Username / Email" with "teste" 342 And I fill in "Username / Email" with "teste"
365 And I fill in "Password" with "123456" 343 And I fill in "Password" with "123456"
366 And I press "Log in" 344 And I press "Log in"
367 - Then I should not see "teste"  
368 \ No newline at end of file 345 \ No newline at end of file
  346 + Then I should not see "teste"
features/step_definitions/http_caching_steps.rb
@@ -1,21 +0,0 @@ @@ -1,21 +0,0 @@
1 -Then /^the response should be valid for (.+) minutes$/ do |n|  
2 - page.response_headers['Cache-Control'].split(/,\s*/).should include("max-age=#{n.to_i * 60}")  
3 -end  
4 -  
5 -Then /^the cache should be public/ do  
6 - page.response_headers['Cache-Control'].split(/,\s*/).should include("public")  
7 -end  
8 -  
9 -Then /^there must be no cache at all$/ do  
10 - parts = page.response_headers['Cache-Control'].split(/,\s*/)  
11 - parts.should include('must-revalidate')  
12 - parts.should include('max-age=0')  
13 -end  
14 -  
15 -Then 'there must be no cookies' do  
16 - cookies.to_hash.should == {}  
17 -end  
18 -  
19 -Then /^there must be a cookie "(.+)"$/ do |cookie_name|  
20 - cookies.keys.should include(cookie_name)  
21 -end  
features/step_definitions/noosfero_steps.rb
@@ -692,11 +692,6 @@ Given /^the cache is turned (on|off)$/ do |state| @@ -692,11 +692,6 @@ Given /^the cache is turned (on|off)$/ do |state|
692 ActionController::Base.perform_caching = (state == 'on') 692 ActionController::Base.perform_caching = (state == 'on')
693 end 693 end
694 694
695 -When /^I make a AJAX request to (.*)$/ do |page|  
696 - header 'X-Requested-With', 'XMLHttpRequest'  
697 - visit(path_to(page))  
698 -end  
699 -  
700 Given /^the environment is configured to (.*) after login$/ do |option| 695 Given /^the environment is configured to (.*) after login$/ do |option|
701 redirection = case option 696 redirection = case option
702 when 'stay on the same page' 697 when 'stay on the same page'
gitignore.example
@@ -43,3 +43,4 @@ debian/noosfero-apache/ @@ -43,3 +43,4 @@ debian/noosfero-apache/
43 features/plugins/* 43 features/plugins/*
44 plugins/solr/config/solr.yml 44 plugins/solr/config/solr.yml
45 /solr 45 /solr
  46 +/Gemfile.lock
lib/noosfero.rb
@@ -3,6 +3,10 @@ @@ -3,6 +3,10 @@
3 require 'fast_gettext' 3 require 'fast_gettext'
4 module Noosfero 4 module Noosfero
5 5
  6 + def self.root(default = nil)
  7 + ENV.fetch('RAILS_RELATIVE_URL_ROOT', default)
  8 + end
  9 +
6 def self.pattern_for_controllers_in_directory(dir) 10 def self.pattern_for_controllers_in_directory(dir)
7 disjunction = controllers_in_directory(dir).join('|') 11 disjunction = controllers_in_directory(dir).join('|')
8 pattern = disjunction.blank? ? '' : ('(' + disjunction + ')') 12 pattern = disjunction.blank? ? '' : ('(' + disjunction + ')')
lib/noosfero/plugin.rb
@@ -12,6 +12,12 @@ class Noosfero::Plugin @@ -12,6 +12,12 @@ class Noosfero::Plugin
12 12
13 attr_writer :should_load 13 attr_writer :should_load
14 14
  15 + # Called for each ActiveRecord class with parents
  16 + # See http://apidock.com/rails/ActiveRecord/ModelSchema/ClassMethods/full_table_name_prefix
  17 + def table_name_prefix
  18 + @table_name_prefix ||= "#{name.to_s.underscore}_"
  19 + end
  20 +
15 def should_load 21 def should_load
16 @should_load.nil? && true || @boot 22 @should_load.nil? && true || @boot
17 end 23 end
lib/noosfero/version.rb
1 module Noosfero 1 module Noosfero
2 PROJECT = 'noosfero' 2 PROJECT = 'noosfero'
3 - VERSION = '1.0~rc3' 3 + VERSION = '1.0~rc4'
4 end 4 end
lib/tasks/ci.rake 0 → 100644
@@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
  1 +namespace :ci do
  2 +
  3 + desc 'Continuous integration smoke test'
  4 + task :smoke do
  5 +
  6 + current_branch = `git rev-parse --abbrev-ref HEAD`.strip
  7 + from = ENV['PREV_HEAD'] || "origin/#{current_branch}"
  8 + to = ENV['HEAD'] || current_branch
  9 + changed_files = `git diff --name-only #{from}..#{to}`.split.select do |f|
  10 + File.exist?(f) && f.split(File::SEPARATOR).first != 'vendor'
  11 + end
  12 +
  13 + changed_plugin_files = changed_files.select do |f|
  14 + f.split(File::SEPARATOR).first == 'plugins'
  15 + end
  16 + changed_plugins = changed_plugin_files.map do |f|
  17 + f.split(File::SEPARATOR)[1]
  18 + end.uniq
  19 +
  20 + changed_files -= changed_plugin_files
  21 +
  22 + # explicitly changed tests
  23 + tests = changed_files.select { |f| f =~ /test\/.*_test\.rb$/ }
  24 + features = changed_files.select { |f| f =~ /\.feature$/ }
  25 +
  26 + # match changed code files to their respective tests
  27 + changed_files.each do |f|
  28 + if f =~ /^(app|lib)\//
  29 + basename = File.basename(f, '.rb')
  30 + Dir.glob("test/**/#{basename}_test.rb").each do |t|
  31 + tests << t unless tests.include?(t)
  32 + end
  33 + end
  34 + end
  35 +
  36 + sh 'testrb', '-Itest', *tests unless tests.empty?
  37 + sh 'cucumber', *features unless features.empty?
  38 + sh 'cucumber', '-p', 'selenium', *features unless features.empty?
  39 +
  40 + changed_plugins.each do |plugin|
  41 + task = "test:noosfero_plugins:#{plugin}"
  42 + puts "Running #{task}"
  43 + Rake::Task[task].execute
  44 + end
  45 +
  46 + end
  47 +
  48 +end
lib/tasks/plugins.rake
@@ -6,17 +6,6 @@ namespace :noosfero do @@ -6,17 +6,6 @@ namespace :noosfero do
6 6
7 plugin_migration_dirs = Dir.glob(Rails.root.join('{baseplugins,config/plugins}', '*', 'db', 'migrate')) 7 plugin_migration_dirs = Dir.glob(Rails.root.join('{baseplugins,config/plugins}', '*', 'db', 'migrate'))
8 8
9 - task :load_config do  
10 - dirs = Dir.glob("{baseplugins,config/plugins}/*").uniq do |dir|  
11 - File.basename(dir)  
12 - end.map do |dir|  
13 - File.join(dir, 'db/migrate')  
14 - end  
15 - dirs.each do |dir|  
16 - ActiveRecord::Migrator.migrations_paths << dir  
17 - end  
18 - end  
19 -  
20 task :migrate do 9 task :migrate do
21 plugin_migration_dirs.each do |path| 10 plugin_migration_dirs.each do |path|
22 ActiveRecord::Migrator.migrate(path, ENV["VERSION"] ? 11 ActiveRecord::Migrator.migrate(path, ENV["VERSION"] ?
@@ -26,5 +15,3 @@ namespace :noosfero do @@ -26,5 +15,3 @@ namespace :noosfero do
26 end 15 end
27 end 16 end
28 17
29 -task 'db:migrate' => 'noosfero:plugins:load_config'  
30 -task 'db:schema:load' => 'noosfero:plugins:load_config'  
lib/tasks/plugins_tests.rake
  1 +@broken_plugins = %w[
  2 + anti_spam
  3 + bsc
  4 + comment_classification
  5 + ldap
  6 + send_email
  7 + shopping_cart
  8 + solr
  9 + tolerance_time
  10 +]
  11 +
1 @all_plugins = Dir.glob('plugins/*').map { |f| File.basename(f) } - ['template'] 12 @all_plugins = Dir.glob('plugins/*').map { |f| File.basename(f) } - ['template']
2 @all_plugins.sort! 13 @all_plugins.sort!
3 @all_tasks = [:units, :functionals, :integration, :cucumber, :selenium] 14 @all_tasks = [:units, :functionals, :integration, :cucumber, :selenium]
@@ -104,7 +115,7 @@ def run_test(name, files) @@ -104,7 +115,7 @@ def run_test(name, files)
104 end 115 end
105 116
106 def run_testrb(files) 117 def run_testrb(files)
107 - sh 'testrb', '-Itest', *files 118 + sh 'testrb', '-I.:test', *files
108 end 119 end
109 120
110 def run_cucumber(profile, files) 121 def run_cucumber(profile, files)
@@ -167,6 +178,7 @@ def test_sequence(plugins, tasks) @@ -167,6 +178,7 @@ def test_sequence(plugins, tasks)
167 end 178 end
168 end 179 end
169 rollback_plugins_state 180 rollback_plugins_state
  181 + yield(failed) if block_given?
170 fail 'There are broken tests to be fixed!' if fail_flag 182 fail 'There are broken tests to be fixed!' if fail_flag
171 end 183 end
172 184
@@ -195,13 +207,39 @@ namespace :test do @@ -195,13 +207,39 @@ namespace :test do
195 @all_tasks.each do |taskname| 207 @all_tasks.each do |taskname|
196 desc "Run #{taskname} tests for all plugins" 208 desc "Run #{taskname} tests for all plugins"
197 task taskname do 209 task taskname do
198 - test_sequence(@all_plugins, taskname) 210 + test_sequence(@all_plugins - @broken_plugins, taskname)
199 end 211 end
200 end 212 end
201 end 213 end
202 214
203 desc "Run all tests for all plugins" 215 desc "Run all tests for all plugins"
204 task :noosfero_plugins do 216 task :noosfero_plugins do
205 - test_sequence(@all_plugins, @all_tasks) 217 + test_sequence(@all_plugins - @broken_plugins, @all_tasks) do |failed|
  218 + plugins_status_report(failed)
  219 + end
206 end 220 end
207 end 221 end
  222 +
  223 +def plugins_status_report(failed)
  224 + w = @all_plugins.map { |s| s.size }.max
  225 +
  226 + puts
  227 + printf ('=' * (w + 21)) + "\n"
  228 + puts 'Plugins status report'
  229 + printf ('=' * (w + 21)) + "\n"
  230 + printf "%-#{w}s %s\n", "Plugin", "Status"
  231 + printf ('-' * w) + ' ' + ('-' * 20) + "\n"
  232 +
  233 + @all_plugins.each do |plugin|
  234 + if @broken_plugins.include?(plugin)
  235 + status = "SKIP"
  236 + elsif !failed[plugin] || failed[plugin].empty?
  237 + status = "PASS"
  238 + else
  239 + status = "FAIL: #{failed[plugin].join(', ')}"
  240 + end
  241 + printf "%-#{w}s %s\n", plugin, status
  242 + end
  243 + printf ('=' * (w + 21)) + "\n"
  244 + puts
  245 +end
lib/tasks/release.rake
@@ -222,7 +222,7 @@ EOF @@ -222,7 +222,7 @@ EOF
222 puts "I: please upload the package manually!" 222 puts "I: please upload the package manually!"
223 end 223 end
224 224
225 - rm_f "rm tmp/pending-release" 225 + rm_f "tmp/pending-release"
226 end 226 end
227 227
228 desc 'Build Debian packages' 228 desc 'Build Debian packages'
lib/white_list_filter.rb
@@ -9,7 +9,7 @@ module WhiteListFilter @@ -9,7 +9,7 @@ module WhiteListFilter
9 unless iframe =~ /src=['"].*src=['"]/ 9 unless iframe =~ /src=['"].*src=['"]/
10 trusted_sites.each do |trusted_site| 10 trusted_sites.each do |trusted_site|
11 re_dom = trusted_site.gsub('.', '\.') 11 re_dom = trusted_site.gsub('.', '\.')
12 - if iframe =~ /src=["']https?:\/\/(www\.)?#{re_dom}\// 12 + if iframe =~ /src=["'](https?:)?\/\/(www\.)?#{re_dom}\//
13 result = iframe 13 result = iframe
14 end 14 end
15 end 15 end
plugins/comment_group/views/comment_group_plugin_profile/view_comments.rjs
@@ -8,5 +8,5 @@ page.replace_html &quot;comment-count-#{@group_id}&quot;, @comments_count @@ -8,5 +8,5 @@ page.replace_html &quot;comment-count-#{@group_id}&quot;, @comments_count
8 if @no_more_pages 8 if @no_more_pages
9 page.replace_html "comments_list_group_#{@group_id}_more", "" 9 page.replace_html "comments_list_group_#{@group_id}_more", ""
10 else 10 else
11 - page.replace_html "comments_list_group_#{@group_id}_more", link_to_remote(_('More'), :url => { :profile => profile.identifier, :controller => 'comment_group_plugin_profile', :action => 'view_comments', :group_id => @group_id, :article_id => @article_id, :group_comment_page => @group_comment_page + 1}, :loaded => visual_effect(:highlight, "comments_list_group_#{@group_id}"), :method => :post, :complete => "loadCompleted(#{@group_id})") 11 + page.replace_html "comments_list_group_#{@group_id}_more", link_to_remote(_('More'), :url => { :profile => profile.identifier, :controller => 'comment_group_plugin_profile', :action => 'view_comments', :group_id => @group_id, :article_id => @article_id, :group_comment_page => @group_comment_page + 1}, :method => :get)
12 end 12 end
plugins/community_block/test/functional/commmunity_block_plugin_profile_controller_test.rb 0 → 100644
@@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +# Re-raise errors caught by the controller.
  4 +class ProfileController
  5 + append_view_path File.join(File.dirname(__FILE__) + '/../../views')
  6 + def rescue_action(e)
  7 + raise e
  8 + end
  9 +end
  10 +
  11 +class ProfileControllerTest < ActionController::TestCase
  12 +
  13 + def setup
  14 + @user = create_user('testinguser').person
  15 + login_as(@user.identifier)
  16 +
  17 + @community = fast_create(Community, :environment_id => Environment.default)
  18 + @community.add_member @user
  19 + @community.add_admin @user
  20 +
  21 + @environment = @community.environment
  22 + @environment.enabled_plugins = ['CommunityBlock']
  23 + @environment.save!
  24 +
  25 + CommunityBlock.delete_all
  26 + @box1 = create(Box, :owner => @community)
  27 + @community.boxes = [@box1]
  28 +
  29 + @block = CommunityBlock.new
  30 + @block.box = @box1
  31 + @block.save!
  32 +
  33 + @community.blocks<<@block
  34 + @community.save!
  35 + end
  36 +
  37 + should 'display community-block' do
  38 + get :index, :profile => @community.identifier
  39 + assert_tag :div, :attributes => {:class => 'community-block-logo'}
  40 + assert_tag :div, :attributes => {:class => 'community-block-info'}
  41 + assert_tag :div, :attributes => {:class => 'community-block-title'}
  42 + assert_tag :div, :attributes => {:class => 'community-block-description'}
  43 + end
  44 +
  45 + should 'display *leave* button when the user is logged in and is a member of the community' do
  46 + get :index, :profile => @community.identifier
  47 + assert_tag :span, :attributes => {:class => 'community-block-button icon-remove'}
  48 + end
  49 +
  50 + should 'display *send email to administrators* button when the user is logged in and is a member of the community' do
  51 + get :index, :profile => @community.identifier
  52 + assert_match /\{&quot;Send an e-mail&quot;:\{&quot;href&quot;:&quot;\/contact\/#{@community.identifier}\/new&quot;\}\}/, @response.body
  53 + end
  54 +
  55 + should 'display *report* button when the user is logged in and is a member of the community' do
  56 + get :index, :profile => @community.identifier
  57 + assert_match /\{&quot;Report abuse&quot;:\{&quot;href&quot;:&quot;\/profile\/#{@community.identifier}\/report_abuse&quot;\}\}/, @response.body
  58 + end
  59 +
  60 + should 'display *join* button when the user is logged in and is not a member of the community' do
  61 + @community.remove_member @user
  62 + get :index, :profile => @community.identifier
  63 + assert_tag :span, :attributes => {:class => 'community-block-button icon-add'}
  64 + end
  65 +
  66 + should 'display *control panel* link option when the user is logged in and is community admin' do
  67 + get :index, :profile => @community.identifier
  68 + assert_match /\{&quot;Control panel&quot;:\{&quot;href&quot;:&quot;\/myprofile\/#{@community.identifier}&quot;\}\}/, @response.body
  69 + end
  70 +
  71 + should 'display *join* button when the user is not logged in' do
  72 + logout
  73 + get :index, :profile => @community.identifier
  74 + assert_tag :span, :attributes => {:class => 'community-block-button icon-add'}
  75 + end
  76 +
  77 + should 'not display *arrow* button when the user is not logged in' do
  78 + logout
  79 + get :index, :profile => @community.identifier
  80 + assert_no_tag :span, :attributes => {:class => 'community-block-button icon-arrow'}
  81 + end
  82 +
  83 +end
plugins/community_block/test/functional/commmunity_block_plugin_profile_design_controller_test.rb
@@ -1,87 +0,0 @@ @@ -1,87 +0,0 @@
1 -require File.dirname(__FILE__) + '/../test_helper'  
2 -  
3 -# Re-raise errors caught by the controller.  
4 -class ProfileController  
5 - append_view_path File.join(File.dirname(__FILE__) + '/../../views')  
6 - def rescue_action(e)  
7 - raise e  
8 - end  
9 -end  
10 -  
11 -class ProfileControllerTest < ActionController::TestCase  
12 -  
13 - def setup  
14 - @controller = ProfileController.new  
15 - @request = ActionController::TestRequest.new  
16 - @response = ActionController::TestResponse.new  
17 -  
18 - @user = create_user('testinguser').person  
19 - login_as(@user.identifier)  
20 -  
21 - @community = fast_create(Community, :environment_id => Environment.default)  
22 - @community.add_member @user  
23 - @community.add_admin @user  
24 -  
25 - @environment = @community.environment  
26 - @environment.enabled_plugins = ['CommunityBlock']  
27 - @environment.save!  
28 -  
29 - CommunityBlock.delete_all  
30 - @box1 = create(Box, :owner => @community)  
31 - @community.boxes = [@box1]  
32 -  
33 - @block = CommunityBlock.new  
34 - @block.box = @box1  
35 - @block.save!  
36 -  
37 - @community.blocks<<@block  
38 - @community.save!  
39 - end  
40 -  
41 - should 'display community-block' do  
42 - get :index, :profile => @community.identifier  
43 - assert_tag :div, :attributes => {:class => 'community-block-logo'}  
44 - assert_tag :div, :attributes => {:class => 'community-block-info'}  
45 - assert_tag :div, :attributes => {:class => 'community-block-title'}  
46 - assert_tag :div, :attributes => {:class => 'community-block-description'}  
47 - end  
48 -  
49 - should 'display *leave* button when the user is logged in and is a member of the community' do  
50 - get :index, :profile => @community.identifier  
51 - assert_tag :span, :attributes => {:class => 'community-block-button icon-remove'}  
52 - end  
53 -  
54 - should 'display *send email to administrators* button when the user is logged in and is a member of the community' do  
55 - get :index, :profile => @community.identifier  
56 - assert_match /\{&quot;Send an e-mail&quot;:\{&quot;href&quot;:&quot;\/contact\/#{@community.identifier}\/new&quot;\}\}/, @response.body  
57 - end  
58 -  
59 - should 'display *report* button when the user is logged in and is a member of the community' do  
60 - get :index, :profile => @community.identifier  
61 - assert_match /\{&quot;Report abuse&quot;:\{&quot;href&quot;:&quot;\/profile\/#{@community.identifier}\/report_abuse&quot;\}\}/, @response.body  
62 - end  
63 -  
64 - should 'display *join* button when the user is logged in and is not a member of the community' do  
65 - @community.remove_member @user  
66 - get :index, :profile => @community.identifier  
67 - assert_tag :span, :attributes => {:class => 'community-block-button icon-add'}  
68 - end  
69 -  
70 - should 'display *control panel* link option when the user is logged in and is community admin' do  
71 - get :index, :profile => @community.identifier  
72 - assert_match /\{&quot;Control panel&quot;:\{&quot;href&quot;:&quot;\/myprofile\/#{@community.identifier}&quot;\}\}/, @response.body  
73 - end  
74 -  
75 - should 'display *join* button when the user is not logged in' do  
76 - logout  
77 - get :index, :profile => @community.identifier  
78 - assert_tag :span, :attributes => {:class => 'community-block-button icon-add'}  
79 - end  
80 -  
81 - should 'not display *arrow* button when the user is not logged in' do  
82 - logout  
83 - get :index, :profile => @community.identifier  
84 - assert_no_tag :span, :attributes => {:class => 'community-block-button icon-arrow'}  
85 - end  
86 -  
87 -end  
plugins/community_block/views/community_block.html.erb
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 <%= link_to( 23 <%= link_to(
24 content_tag('span','',:class => 'community-block-button icon-arrow'), 24 content_tag('span','',:class => 'community-block-button icon-arrow'),
25 '#', 25 '#',
26 - :onclick => "toggleSubmenu(this,'',#{j links.to_json}); return false;", 26 + :onclick => "toggleSubmenu(this,'',#{CGI::escapeHTML(links.to_json)}); return false;",
27 :class => 'simplemenu-trigger') %> 27 :class => 'simplemenu-trigger') %>
28 28
29 <% end %> 29 <% end %>
@@ -32,11 +32,11 @@ @@ -32,11 +32,11 @@
32 <% if profile.members.include?(user) || profile.already_request_membership?(user) %> 32 <% if profile.members.include?(user) || profile.already_request_membership?(user) %>
33 <%= link_to( 33 <%= link_to(
34 content_tag('span', '', :class => 'community-block-button icon-remove'), 34 content_tag('span', '', :class => 'community-block-button icon-remove'),
35 - profile.leave_url) %> 35 + profile.leave_url, :class => 'join-community') %>
36 <% else %> 36 <% else %>
37 <%= link_to( 37 <%= link_to(
38 content_tag('span', '', :class => 'community-block-button icon-add'), 38 content_tag('span', '', :class => 'community-block-button icon-add'),
39 - profile.join_url) %> 39 + profile.join_url, :class => 'join-community') %>
40 <% end %> 40 <% end %>
41 <% else %> 41 <% else %>
42 <%= link_to( 42 <%= link_to(
plugins/community_track/test/functional/community_track_plugin_content_viewer_controller_test.rb
@@ -101,9 +101,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -101,9 +101,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
101 should 'render tracks in track list block' do 101 should 'render tracks in track list block' do
102 @block = CommunityTrackPlugin::TrackListBlock.create!(:box => @profile.boxes.last) 102 @block = CommunityTrackPlugin::TrackListBlock.create!(:box => @profile.boxes.last)
103 get :view_page, @step.url 103 get :view_page, @step.url
104 - file = File.open('result.html', 'w+')  
105 - file.write(@response.body)  
106 - file.close  
107 assert_tag :tag => 'div', :attributes => { :class => "item category_#{@track.category_name}" }, :descendant => { :tag => 'div', :attributes => { :class => 'steps' }, :descendant => { :tag => 'span', :attributes => { :class => "step #{@block.status_class(@step)}" } } } 104 assert_tag :tag => 'div', :attributes => { :class => "item category_#{@track.category_name}" }, :descendant => { :tag => 'div', :attributes => { :class => 'steps' }, :descendant => { :tag => 'span', :attributes => { :class => "step #{@block.status_class(@step)}" } } }
108 end 105 end
109 106
plugins/community_track/test/unit/community_track_plugin_test.rb
@@ -6,10 +6,13 @@ class CommunityTrackPluginTest &lt; ActiveSupport::TestCase @@ -6,10 +6,13 @@ class CommunityTrackPluginTest &lt; ActiveSupport::TestCase
6 @plugin = CommunityTrackPlugin.new 6 @plugin = CommunityTrackPlugin.new
7 @profile = fast_create(Community) 7 @profile = fast_create(Community)
8 @params = {} 8 @params = {}
9 - @plugin.stubs(:context).returns(self) 9 + @context = mock
  10 + @context.stubs(:profile).returns(@profile)
  11 + @context.stubs(:params).returns(@params)
  12 + @plugin.stubs(:context).returns(@context)
10 end 13 end
11 14
12 - attr_reader :profile, :params 15 + attr_reader :profile, :params, :context
13 16
14 should 'has name' do 17 should 'has name' do
15 assert CommunityTrackPlugin.plugin_name 18 assert CommunityTrackPlugin.plugin_name
@@ -28,37 +31,37 @@ class CommunityTrackPluginTest &lt; ActiveSupport::TestCase @@ -28,37 +31,37 @@ class CommunityTrackPluginTest &lt; ActiveSupport::TestCase
28 end 31 end
29 32
30 should 'do not return Track as a content type if profile is not a community' do 33 should 'do not return Track as a content type if profile is not a community' do
31 - @profile = Organization.new 34 + context.stubs(:profile).returns(Organization.new)
32 assert_not_includes @plugin.content_types, CommunityTrackPlugin::Track 35 assert_not_includes @plugin.content_types, CommunityTrackPlugin::Track
33 end 36 end
34 37
35 should 'do not return Track as a content type if there is a parent' do 38 should 'do not return Track as a content type if there is a parent' do
36 - parent = fast_create(Blog, :profile_id => @profile.id)  
37 - @params[:parent_id] = parent.id 39 + parent = fast_create(Blog, :profile_id => profile.id)
  40 + params[:parent_id] = parent.id
38 assert_not_includes @plugin.content_types, CommunityTrackPlugin::Track 41 assert_not_includes @plugin.content_types, CommunityTrackPlugin::Track
39 end 42 end
40 43
41 should 'return Step as a content type if parent is a Track' do 44 should 'return Step as a content type if parent is a Track' do
42 - parent = fast_create(CommunityTrackPlugin::Track, :profile_id => @profile.id)  
43 - @params[:parent_id] = parent.id 45 + parent = fast_create(CommunityTrackPlugin::Track, :profile_id => profile.id)
  46 + params[:parent_id] = parent.id
44 assert_includes @plugin.content_types, CommunityTrackPlugin::Step 47 assert_includes @plugin.content_types, CommunityTrackPlugin::Step
45 end 48 end
46 49
47 should 'do not return Step as a content type if parent is not a Track' do 50 should 'do not return Step as a content type if parent is not a Track' do
48 - parent = fast_create(Blog, :profile_id => @profile.id)  
49 - @params[:parent_id] = parent.id 51 + parent = fast_create(Blog, :profile_id => profile.id)
  52 + params[:parent_id] = parent.id
50 assert_not_includes @plugin.content_types, CommunityTrackPlugin::Step 53 assert_not_includes @plugin.content_types, CommunityTrackPlugin::Step
51 end 54 end
52 55
53 should 'return Track and Step as a content type if context has no params' do 56 should 'return Track and Step as a content type if context has no params' do
54 - parent = fast_create(Blog, :profile_id => @profile.id)  
55 - expects(:respond_to?).with(:params).returns(false) 57 + parent = fast_create(Blog, :profile_id => profile.id)
  58 + context.expects(:respond_to?).with(:params).returns(false)
56 assert_equivalent [CommunityTrackPlugin::Step, CommunityTrackPlugin::Track], @plugin.content_types 59 assert_equivalent [CommunityTrackPlugin::Step, CommunityTrackPlugin::Track], @plugin.content_types
57 end 60 end
58 61
59 should 'return Track and Step as a content type if params is nil' do 62 should 'return Track and Step as a content type if params is nil' do
60 - parent = fast_create(Blog, :profile_id => @profile.id)  
61 - @params = nil 63 + parent = fast_create(Blog, :profile_id => profile.id)
  64 + context.stubs(:params).returns(nil)
62 assert_equivalent [CommunityTrackPlugin::Step, CommunityTrackPlugin::Track], @plugin.content_types 65 assert_equivalent [CommunityTrackPlugin::Step, CommunityTrackPlugin::Track], @plugin.content_types
63 end 66 end
64 67
plugins/container_block/lib/container_block_plugin/container_block.rb
@@ -38,9 +38,11 @@ class ContainerBlockPlugin::ContainerBlock &lt; Block @@ -38,9 +38,11 @@ class ContainerBlockPlugin::ContainerBlock &lt; Block
38 end 38 end
39 39
40 def create_box 40 def create_box
41 - container_box = Box.create!(:owner => owner)  
42 - container_box.update_attribute(:position, nil) 41 + container_box = Box.new(:owner => owner)
  42 + container_box.save!
43 settings[:container_box_id] = container_box.id 43 settings[:container_box_id] = container_box.id
  44 + copy_blocks unless @blocks_to_copy.blank?
  45 + container_box.update_attribute(:position, nil)
44 save! 46 save!
45 end 47 end
46 48
@@ -71,4 +73,23 @@ class ContainerBlockPlugin::ContainerBlock &lt; Block @@ -71,4 +73,23 @@ class ContainerBlockPlugin::ContainerBlock &lt; Block
71 end 73 end
72 end 74 end
73 75
  76 + def copy_from_with_container(block)
  77 + copy_from_without_container(block)
  78 + children_settings = block.children_settings
  79 + @blocks_to_copy = block.blocks
  80 + end
  81 +
  82 + alias_method_chain :copy_from, :container
  83 +
  84 + def copy_blocks
  85 + new_children_settings = {}
  86 + @blocks_to_copy.map do |child|
  87 + new_block = child.class.new(:title => child[:title])
  88 + new_block.copy_from(child)
  89 + container_box.blocks << new_block
  90 + new_children_settings[new_block.id] = children_settings[child.id] if children_settings[child.id]
  91 + end
  92 + settings[:children_settings] = new_children_settings
  93 + end
  94 +
74 end 95 end
plugins/container_block/test/unit/profile_test.rb 0 → 100644
@@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
  1 +require 'test_helper'
  2 +
  3 +class ProfileTest < ActiveSupport::TestCase
  4 +
  5 + should 'not lose position values for boxes when the template has a container block' do
  6 + template = fast_create(Profile)
  7 + template.boxes = [Box.new, Box.new]
  8 + template.boxes[0].blocks << ContainerBlockPlugin::ContainerBlock.new
  9 + template.boxes[1].blocks << Block.new
  10 + template.is_template = true
  11 + template.save!
  12 +
  13 + p = Profile.new
  14 + p.identifier = 'profile-with-template'
  15 + p.name = p.identifier
  16 + p.template = template.reload
  17 + p.save!
  18 +
  19 + assert_equivalent p.reload.boxes.map(&:position), template.reload.boxes.map(&:position)
  20 + end
  21 +
  22 + should 'copy contents of a container block that belongs to the template' do
  23 + template = fast_create(Profile)
  24 + template.boxes = [Box.new]
  25 + template.boxes[0].blocks << ContainerBlockPlugin::ContainerBlock.new
  26 + template.is_template = true
  27 + template.save!
  28 +
  29 + container = template.blocks.first
  30 + container.container_box.blocks << Block.new
  31 + container.container_box.blocks << Block.new
  32 + container.settings[:children_settings] = {}
  33 + container.settings[:children_settings][container.blocks.last.id] = 999
  34 + container.save!
  35 +
  36 + p = Profile.new
  37 + p.identifier = 'another-profile-with-template'
  38 + p.name = p.identifier
  39 + p.template = template.reload
  40 + p.save!
  41 +
  42 + container_copy = p.blocks.first
  43 + assert_equal({container_copy.blocks.last.id => 999}, container_copy.children_settings)
  44 + assert_equal container.blocks.size, p.blocks.first.blocks.size
  45 + end
  46 +
  47 +end
plugins/custom_forms/controllers/custom_forms_plugin_myprofile_controller.rb
@@ -23,7 +23,7 @@ class CustomFormsPluginMyprofileController &lt; MyProfileController @@ -23,7 +23,7 @@ class CustomFormsPluginMyprofileController &lt; MyProfileController
23 23
24 respond_to do |format| 24 respond_to do |format|
25 if @form.save 25 if @form.save
26 - flash[:notice] = _("Custom form #{@form.name} was successfully created.") 26 + flash[:notice] = _("Custom form %s was successfully created.") % @form.name
27 format.html { redirect_to(:action=>'index') } 27 format.html { redirect_to(:action=>'index') }
28 else 28 else
29 format.html { render :action => 'new' } 29 format.html { render :action => 'new' }
@@ -43,7 +43,7 @@ class CustomFormsPluginMyprofileController &lt; MyProfileController @@ -43,7 +43,7 @@ class CustomFormsPluginMyprofileController &lt; MyProfileController
43 43
44 respond_to do |format| 44 respond_to do |format|
45 if @form.save 45 if @form.save
46 - flash[:notice] = _("Custom form #{@form.name} was successfully updated.") 46 + flash[:notice] = _("Custom form %s was successfully updated.") % @form.name
47 format.html { redirect_to(:action=>'index') } 47 format.html { redirect_to(:action=>'index') }
48 else 48 else
49 session['notice'] = _('Form could not be updated') 49 session['notice'] = _('Form could not be updated')
plugins/custom_forms/test/functional/custom_forms_plugin_myprofile_controller_test.rb
@@ -226,7 +226,7 @@ class CustomFormsPluginMyprofileControllerTest &lt; ActionController::TestCase @@ -226,7 +226,7 @@ class CustomFormsPluginMyprofileControllerTest &lt; ActionController::TestCase
226 end 226 end
227 227
228 should 'list pending submissions for a form' do 228 should 'list pending submissions for a form' do
229 - person = fast_create(Person) 229 + person = create_user('john').person
230 form = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software', :for_admission => true) 230 form = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software', :for_admission => true)
231 task = CustomFormsPlugin::AdmissionSurvey.create!(:form_id => form.id, :target => person, :requestor => profile) 231 task = CustomFormsPlugin::AdmissionSurvey.create!(:form_id => form.id, :target => person, :requestor => profile)
232 232
plugins/custom_forms/test/unit/custom_forms_plugin/admission_survey_test.rb
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + &#39;/../../../../../test/test_helper&#39; @@ -3,7 +3,7 @@ require File.dirname(__FILE__) + &#39;/../../../../../test/test_helper&#39;
3 class CustomFormsPlugin::AdmissionSurveyTest < ActiveSupport::TestCase 3 class CustomFormsPlugin::AdmissionSurveyTest < ActiveSupport::TestCase
4 should 'add member to community on perform' do 4 should 'add member to community on perform' do
5 profile = fast_create(Community) 5 profile = fast_create(Community)
6 - person = fast_create(Person) 6 + person = create_user('john').person
7 form = CustomFormsPlugin::Form.create!(:name => 'Simple Form', :profile => profile) 7 form = CustomFormsPlugin::Form.create!(:name => 'Simple Form', :profile => profile)
8 task = CustomFormsPlugin::AdmissionSurvey.create!(:form_id => form.id, :target => person, :requestor => profile) 8 task = CustomFormsPlugin::AdmissionSurvey.create!(:form_id => form.id, :target => person, :requestor => profile)
9 9
plugins/custom_forms/test/unit/custom_forms_plugin/form_test.rb
@@ -244,7 +244,7 @@ class CustomFormsPlugin::FormTest &lt; ActiveSupport::TestCase @@ -244,7 +244,7 @@ class CustomFormsPlugin::FormTest &lt; ActiveSupport::TestCase
244 244
245 should 'cancel survey tasks after removing a form' do 245 should 'cancel survey tasks after removing a form' do
246 profile = fast_create(Profile) 246 profile = fast_create(Profile)
247 - person = fast_create(Person) 247 + person = create_user('john').person
248 248
249 form1 = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => profile) 249 form1 = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => profile)
250 form2 = CustomFormsPlugin::Form.create!(:name => 'Operation System', :profile => profile) 250 form2 = CustomFormsPlugin::Form.create!(:name => 'Operation System', :profile => profile)