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.

.ackrc
1 1 --ignore-dir=log
2 2 --ignore-dir=tmp
3 3 --ignore-dir=pkg
  4 +--ignore-dir=public/javascripts/cache
  5 +--ignore-dir=public/stylesheets/cache
... ...
AUTHORS.md
... ... @@ -198,6 +198,7 @@ Martín Olivera <molivera@solar.org.ar>
198 198 Moises Machado <moises@colivre.coop.br>
199 199 Naíla Alves <naila@colivre.coop.br>
200 200 Nanda Lopes <nanda.listas+psl@gmail.com>
  201 +Parley Martins <parleypachecomartins@gmail.com>
201 202 Paulo Meirelles + Alessandro Palmeira + João M. M. da Silva <paulo@softwarelivre.org>
202 203 Paulo Meirelles + Alessandro Palmeira <paulo@softwarelivre.org>
203 204 Paulo Meirelles + Carlos Morais <paulo@softwarelivre.org>
... ...
Gemfile
1 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 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 23 gem 'whenever', :require => false
23 24  
... ... @@ -25,25 +26,26 @@ gem &#39;whenever&#39;, :require =&gt; false
25 26 # with their GEM names (not the Debian package names)
26 27  
27 28 group :production do
28   - gem 'dalli'
  29 + gem 'dalli', '~> 2.7.0'
29 30 end
30 31  
31 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 36 end
36 37  
37 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 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 51 end
... ...
Gemfile.lock
... ... @@ -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
INSTALL.md
... ... @@ -186,8 +186,8 @@ Apache instalation
186 186  
187 187 # apt-get install apache2
188 188  
189   -Apache configuration
190   ---------------------
  189 +Configuration - noosfero at /
  190 +-----------------------------
191 191  
192 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 257  
258 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 317 Enabling exception notifications
262 318 ================================
... ...
Vagrantfile
... ... @@ -3,7 +3,7 @@
3 3  
4 4 VAGRANTFILE_API_VERSION = "2"
5 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 7 config.vm.network :forwarded_port, host: 3000, guest: 3000
8 8 config.vm.provision :shell do |shell|
9 9 shell.inline = 'su vagrant -c /vagrant/script/vagrant'
... ...
app/controllers/public/account_controller.rb
... ... @@ -99,6 +99,7 @@ class AccountController &lt; ApplicationController
99 99 @user.return_to = session[:return_to]
100 100 @person = Person.new(params[:profile_data])
101 101 @person.environment = @user.environment
  102 +
102 103 if request.post?
103 104 if may_be_a_bot
104 105 set_signup_start_time_for_now
... ... @@ -117,6 +118,14 @@ class AccountController &lt; ApplicationController
117 118 invitation.update_attributes!({:friend => @user.person})
118 119 invitation.finish
119 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 129 if @user.activated?
121 130 self.current_user = @user
122 131 check_join_in_community(@user)
... ... @@ -186,7 +195,7 @@ class AccountController &lt; ApplicationController
186 195 else
187 196 @change_password.errors[:base] << _('Could not find any user with %s equal to "%s".') % [fields_label, params[:value]]
188 197 end
189   - rescue ActiveRecord::RecordInvald
  198 + rescue ActiveRecord::RecordInvalid
190 199 @change_password.errors[:base] << _('Could not perform password recovery for the user.')
191 200 end
192 201 end
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -126,7 +126,7 @@ class ContentViewerController &lt; ApplicationController
126 126 elsif !@page.display_to?(user)
127 127 if !profile.public?
128 128 private_profile_partial_parameters
129   - render :template => 'profile/_private_profile', :status => 403
  129 + render :template => 'profile/_private_profile', :status => 403, :formats => [:html]
130 130 allowed = false
131 131 else #if !profile.visible?
132 132 render_access_denied
... ...
app/controllers/public/profile_controller.rb
... ... @@ -17,7 +17,11 @@ class ProfileController &lt; PublicController
17 17 end
18 18 @tags = profile.article_tags
19 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 25 end
22 26 end
23 27  
... ... @@ -315,7 +319,7 @@ class ProfileController &lt; PublicController
315 319 abuse_report = AbuseReport.new(params[:abuse_report])
316 320 if !params[:content_type].blank?
317 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 323 end
320 324  
321 325 user.register_report(abuse_report, profile)
... ... @@ -394,6 +398,7 @@ class ProfileController &lt; PublicController
394 398  
395 399 def private_profile
396 400 private_profile_partial_parameters
  401 + render :action => 'index', :status => 403
397 402 end
398 403  
399 404 def invisible_profile
... ...
app/controllers/public/search_controller.rb
... ... @@ -90,10 +90,14 @@ class SearchController &lt; PublicController
90 90 end
91 91  
92 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 101 date_range = (@date - 1.month).at_beginning_of_month..(@date + 1.month).at_end_of_month
98 102  
99 103 @events = []
... ...
app/helpers/application_helper.rb
... ... @@ -304,7 +304,7 @@ module ApplicationHelper
304 304 def partial_for_class(klass, prefix=nil, suffix=nil)
305 305 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil?
306 306 name = klass.name.underscore
307   - controller.view_paths.reverse_each do |view_path|
  307 + controller.view_paths.each do |view_path|
308 308 partial = partial_for_class_in_view_path(klass, view_path, prefix, suffix)
309 309 return partial if partial
310 310 end
... ... @@ -1388,7 +1388,7 @@ module ApplicationHelper
1388 1388 # are old things that do not support it we are keeping this hot spot.
1389 1389 html = @plugins.pipeline(:parse_content, html, source).first
1390 1390 end
1391   - html
  1391 + html && html.html_safe
1392 1392 end
1393 1393  
1394 1394 def convert_macro(html, source)
... ...
app/helpers/article_helper.rb
... ... @@ -3,6 +3,12 @@ module ArticleHelper
3 3 include PrototypeHelper
4 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 12 def custom_options_for_article(article, tokenized_children)
7 13 @article = article
8 14  
... ...
app/helpers/profile_helper.rb
1 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 74 unless force || profile.may_display_field_to?(field, user)
5 75 return ''
6 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 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 90 end
16 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 132 end
31 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 188 else
39   - content_tag('tr', content_tag('th', _('Work'), { :colspan => 2 })) + organization + organization_site
  189 + super
40 190 end
41 191 end
42 192  
... ...
app/helpers/sweeper_helper.rb
... ... @@ -56,12 +56,12 @@ module SweeperHelper
56 56 if profile
57 57 profile.blocks.each {|block|
58 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 61 end
62 62 environment.blocks.each {|block|
63 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 67 blocks_to_expire.uniq!
... ...
app/helpers/tinymce_helper.rb 0 → 100644
... ... @@ -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 282 end
283 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 285 # returns the data of the article. Must be overriden in each subclass to
293 286 # provide the correct content for the article.
294 287 def data
... ...
app/models/block.rb
... ... @@ -192,7 +192,7 @@ class Block &lt; ActiveRecord::Base
192 192  
193 193 # Override in your subclasses.
194 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 196 # Possible contexts are: :profile, :environment
197 197 def self.expire_on
198 198 {
... ... @@ -234,4 +234,9 @@ class Block &lt; ActiveRecord::Base
234 234 duplicated_block
235 235 end
236 236  
  237 + def copy_from(block)
  238 + self.settings = block.settings
  239 + self.position = block.position
  240 + end
  241 +
237 242 end
... ...
app/models/community.rb
... ... @@ -50,16 +50,6 @@ class Community &lt; Organization
50 50 super + FIELDS
51 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 53 def active_fields
64 54 environment ? environment.active_community_fields : []
65 55 end
... ...
app/models/enterprise.rb
... ... @@ -62,16 +62,6 @@ class Enterprise &lt; Organization
62 62 super + FIELDS
63 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 65 def active_fields
76 66 environment ? environment.active_enterprise_fields : []
77 67 end
... ...
app/models/environment.rb
... ... @@ -662,6 +662,7 @@ class Environment &lt; ActiveRecord::Base
662 662 url = 'http://'
663 663 url << (Noosfero.url_options.key?(:host) ? Noosfero.url_options[:host] : default_hostname)
664 664 url << ':' << Noosfero.url_options[:port].to_s if Noosfero.url_options.key?(:port)
  665 + url << Noosfero.root('')
665 666 url
666 667 end
667 668  
... ... @@ -806,7 +807,7 @@ class Environment &lt; ActiveRecord::Base
806 807 end
807 808  
808 809 def notification_emails
809   - [noreply_email.blank? ? nil : noreply_email].compact + admins.map(&:email)
  810 + [contact_email].select(&:present?) + admins.map(&:email)
810 811 end
811 812  
812 813 after_create :create_templates
... ...
app/models/external_feed.rb
... ... @@ -13,6 +13,7 @@ class ExternalFeed &lt; ActiveRecord::Base
13 13 attr_accessible :address, :enabled
14 14  
15 15 def add_item(title, link, date, content)
  16 + return if content.blank?
16 17 doc = Hpricot(content)
17 18 doc.search('*').each do |p|
18 19 if p.instance_of? Hpricot::Elem
... ...
app/models/link_list_block.rb
... ... @@ -78,8 +78,13 @@ class LinkListBlock &lt; Block
78 78 address
79 79 end
80 80 if add !~ /^[a-z]+:\/\// && add !~ /^\//
81   - 'http://' + add
  81 + '//' + add
82 82 else
  83 + if root = Noosfero.root
  84 + if !add.starts_with?(root)
  85 + add = root + add
  86 + end
  87 + end
83 88 add
84 89 end
85 90 end
... ... @@ -96,4 +101,5 @@ class LinkListBlock &lt; Block
96 101 sanitizer = HTML::WhiteListSanitizer.new
97 102 sanitizer.sanitize(text)
98 103 end
  104 +
99 105 end
... ...
app/models/organization.rb
... ... @@ -31,6 +31,16 @@ class Organization &lt; Profile
31 31  
32 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 44 def validation_methodology
35 45 self.validation_info ? self.validation_info.validation_methodology : nil
36 46 end
... ... @@ -136,7 +146,11 @@ class Organization &lt; Profile
136 146 end
137 147  
138 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 154 end
141 155  
142 156 def already_request_membership?(person)
... ...
app/models/person.rb
... ... @@ -172,7 +172,7 @@ class Person &lt; Profile
172 172 FIELDS
173 173 end
174 174  
175   - validate :presence_of_required_fields
  175 + validate :presence_of_required_fields, :unless => :is_template
176 176  
177 177 def presence_of_required_fields
178 178 self.required_fields.each do |field|
... ...
app/models/profile.rb
... ... @@ -358,16 +358,17 @@ class Profile &lt; ActiveRecord::Base
358 358 end
359 359  
360 360 def copy_blocks_from(profile)
  361 + template_boxes = profile.boxes.select{|box| box.position}
361 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 367 new_box.position = box.position
365   - self.boxes << new_box
366 368 box.blocks.each do |block|
367 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 372 end
372 373 end
373 374 end
... ...
app/models/task.rb
... ... @@ -285,8 +285,9 @@ class Task &lt; ActiveRecord::Base
285 285 # If
286 286 def send_notification(action)
287 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 291 end
291 292 end
292 293 end
... ...
app/sweepers/role_assignment_sweeper.rb
... ... @@ -13,20 +13,22 @@ class RoleAssignmentSweeper &lt; ActiveRecord::Observer
13 13 protected
14 14  
15 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 18 end
19 19  
20 20 def expire_cache(profile)
21 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 26 profile.blocks_to_expire_cache.each { |block|
27 27 blocks = profile.blocks.select{|b| b.kind_of?(block)}
28 28 BlockSweeper.expire_blocks(blocks)
29 29 }
  30 +
  31 + expire_blocks_cache(profile, [:role_assignment])
30 32 end
31 33  
32 34 end
... ...
app/views/events/_month.html.erb
... ... @@ -13,8 +13,8 @@
13 13 date.day,
14 14 :url => {:action => 'events_by_day', :year => date.year, :month => date.month, :day => date.day, :category_id => @category_id},
15 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 19 date.day
20 20 %>
... ...
app/views/layouts/application-ng.html.erb
... ... @@ -19,6 +19,9 @@
19 19 <meta property="og:site_name" content="<%= profile ? profile.name : @environment.name %>">
20 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 25 <% if @page %>
23 26 <meta property="article:published_time" content="<%= show_date(@page.published_at) %>">
24 27 <% @page.body_images_paths.each do |img| %>
... ...
app/views/manage_products/_edit_description.html.erb
1 1 <%= render :file => 'shared/tiny_mce', :locals => {:mode => 'simple'} %>
2 2 <%= remote_form_for(@product,
3 3 :loading => "small_loading('product-description-form')",
4   - :before => ("tinyMCE.triggerSave()" unless Rails.env == 'test'),
5 4 :update => 'product-description',
6 5 :url => {:controller => 'manage_products', :action => 'edit', :id => @product, :field => 'description'},
7 6 :html => {:id => 'product-description-form', :method => 'post'}) do |f| %>
... ...
app/views/profile/_common.html.erb
1 1 <% unless @action %>
2 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 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 7 <% end %>
  8 +
  9 + <%= display_general %>
52 10 <% end %>
53 11 <% end %>
... ...
app/views/profile/_organization_profile.html.erb
1 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 5 <%= render :partial => 'common' %>
39 6 </table>
... ...
app/views/profile/_person_profile.html.erb
1 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 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 11 <%= render :partial => 'common' %>
40   -
41 12 <% end %>
42 13 </table>
43 14  
... ...
app/views/profile/_profile_comment_form.html.erb
... ... @@ -10,8 +10,8 @@
10 10 :rows => 1,
11 11 :class => 'submit-with-keypress',
12 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 15 :value => _('Leave your comment'),
16 16 :style => 'color: #ccc' %>
17 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 9 :rows => 1,
10 10 :class => 'submit-with-keypress',
11 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 14 :value => _('Leave your comment') %>
15 15 <%= hidden_field_tag 'scrap[scrap_id]', scrap.id %>
16 16 <%= hidden_field_tag 'receiver_id', scrap.sender.id %>
... ...
app/views/profile_editor/_person_form.html.erb
... ... @@ -25,6 +25,7 @@
25 25 <%= optional_field(@person, 'address', labelled_form_field(_('Address (street and number)'), text_field(:profile_data, :address, :rel => _('Address')))) %>
26 26 <%= optional_field(@person, 'address_reference', labelled_form_field(_('Address reference'), text_field(:profile_data, :address_reference, :rel => _('Address reference')))) %>
27 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 30 <% optional_field(@person, 'schooling') do %>
30 31 <div class="formfieldline">
... ...
app/views/shared/reported_versions/_article.html.erb
... ... @@ -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   -<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   -<%= _('Reported folder') + ': ' + link_to(article.name, article.url) %>
app/views/shared/reported_versions/profile/_article.html.erb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 8 tinymce.create('tinymce.plugins.MacrosPlugin', {
25 9 createControl: function(n, cm) {
26 10 switch (n) {
... ... @@ -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 46 // Register plugin with a short name
53 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 52 </script>
  53 +
... ...
app/views/task_mailer/generic_message.text.erb 0 → 100644
... ... @@ -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   -task_cancelled.text.erb
2 0 \ No newline at end of file
app/views/task_mailer/task_cancelled.text.erb
... ... @@ -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   -task_cancelled.text.erb
2 0 \ No newline at end of file
app/views/task_mailer/task_finished.text.erb
... ... @@ -1 +0,0 @@
1   -task_cancelled.text.erb
2 0 \ No newline at end of file
config/application.rb
... ... @@ -115,6 +115,12 @@ module Noosfero
115 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 124 Noosfero::Plugin.setup(config)
119 125  
120 126 end
... ...
config/cucumber.yml
1 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 6 <% selenium_options = "--strict --tags @selenium #{base_requires}" %>
4 7  
5 8 default: <%= default_options %>
... ...
config/initializers/i18n.rb
1 1 # necessary for I18n.default_locale to work
2 2 require 'i18n/backend/fallbacks'
3 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 1 class IndexArticlesFilteredFields < ActiveRecord::Migration
2 2 def self.up
3 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 6 end
7 7 add_index :articles, [:type]
8 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 2 def self.up
3 3 add_column :articles, :spam_comments_count, :integer, :default => 0
4 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 9 end
7 10  
8 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 4 add_column :article_versions, :author_id, :integer
5 5  
6 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 12 def self.down
11 13 remove_column :articles, :author_id
... ...
db/migrate/20140724134601_fix_yaml_encoding.rb
1 1 class FixYamlEncoding < ActiveRecord::Migration
2 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 12 end
11 13  
12 14 def self.down
... ... @@ -16,15 +18,34 @@ class FixYamlEncoding &lt; ActiveRecord::Migration
16 18 private
17 19  
18 20 def self.fix_encoding(model, param)
19   - result = model.find(:all, :conditions => "#{param} LIKE '%!binary%'")
  21 + result = model.all
20 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 37 end
23 38  
24 39 def self.deep_fix(hash)
25 40 hash.each do |value|
26   - value.force_encoding('UTF-8') if value.is_a?(String) && !value.frozen? && value.encoding == Encoding::ASCII_8BIT
27 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 49 end
29 50 end
30 51  
... ...
db/migrate/20140724180943_add_index_to_blog_posts_sort.rb
1 1 class AddIndexToBlogPostsSort < ActiveRecord::Migration
2 2 def self.up
3 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 5 end
6 6 end
7 7  
8 8 def self.down
9 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 11 end
12 12 end
13 13 end
... ...
debian/bundle/config
... ... @@ -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 7 noosfero (1.0~rc3) wheezy-test; urgency=low
2 8  
3 9 * Third release candidate to Noosfero 1.0
... ... @@ -22,6 +28,12 @@ noosfero (0.99.0~rc20140618202455) wheezy-test; urgency=low
22 28  
23 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 37 noosfero (0.47.4) unstable; urgency=low
26 38  
27 39 * Bugfixes and performance optimizations
... ...
debian/control
... ... @@ -9,7 +9,7 @@ Build-Depends:
9 9 ruby-gettext,
10 10 ruby-sqlite3,
11 11 rake,
12   - rails3 (>= 3.2.6-1~),
  12 + rails3 (>= 3.2.19-1~),
13 13 ruby-rspec,
14 14 ruby-rspec-rails,
15 15 ruby-will-paginate,
... ... @@ -51,6 +51,7 @@ Depends:
51 51 ruby-hpricot,
52 52 ruby-nokogiri,
53 53 ruby-acts-as-taggable-on,
  54 + ruby-progressbar,
54 55 ruby-prototype-rails,
55 56 ruby-rails-autolink,
56 57 memcached,
... ...
debian/filter-gemfile 0 → 100755
... ... @@ -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 7 Rakefile usr/share/noosfero
8 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 10 config/application.rb usr/share/noosfero/config
15 11 config/boot.rb usr/share/noosfero/config
16 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 15 usr/share/noosfero/public/designs/themes/noosfero usr/share/noosfero/public/designs/themes/default
16 16 usr/share/noosfero/public/designs/icons/tango usr/share/noosfero/public/designs/icons/default
17 17 usr/share/noosfero/script/noosfero-plugins usr/sbin/noosfero-plugins
  18 +/dev/null usr/share/noosfero/Gemfile.lock
... ...
debian/rules
... ... @@ -20,6 +20,10 @@ override_dh_link:
20 20 dh_link usr/lib/noosfero/dbinstall usr/share/dbconfig-common/scripts/noosfero/install/$$db; \
21 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 27 override_dh_installinit:
24 28 dh_installinit -pnoosfero --onlyscripts
25 29  
... ...
etc/noosfero/varnish-accept-language.vcl
... ... @@ -6,7 +6,14 @@ C{
6 6 /*
7 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 23 #include <string.h>
17 24  
18 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 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 32 #define RETURN_LANG(x) { \
25 33 strncpy(lang, x, LANG_MAXLEN); \
26 34 return; \
... ... @@ -64,9 +72,8 @@ int is_supported(vcl_string *lang) {
64 72 strncat(match_str, lang, LANG_MAXLEN);
65 73 strncat(match_str, ":\0", 2);
66 74  
67   - if (strstr(supported_languages, match_str)) {
  75 + if (strstr(supported_languages, match_str))
68 76 is_supported = 1;
69   - }
70 77  
71 78 return is_supported;
72 79 }
... ... @@ -90,6 +97,7 @@ void select_language(const vcl_string *incoming_header, char *lang) {
90 97 vcl_string *lang_tok = NULL;
91 98 vcl_string root_lang[3];
92 99 vcl_string *header;
  100 + vcl_string header_copy[HDR_MAXLEN];
93 101 vcl_string *pos = NULL;
94 102 vcl_string *q_spec = NULL;
95 103 unsigned int curr_lang = 0, i = 0;
... ... @@ -106,7 +114,7 @@ void select_language(const vcl_string *incoming_header, char *lang) {
106 114 RETURN_DEFAULT_LANG;
107 115  
108 116 /* Tokenize Accept-Language */
109   - header = (vcl_string *) incoming_header;
  117 + header = strncpy(header_copy, incoming_header, sizeof(header_copy));
110 118  
111 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 145 header = NULL;
138 146  
139 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 152 /* Sort by priority */
... ... @@ -157,12 +166,11 @@ void vcl_rewrite_accept_language(const struct sess *sp) {
157 166 vcl_string *in_hdr;
158 167 vcl_string lang[LANG_MAXLEN];
159 168  
160   - memset(lang, 0, LANG_MAXLEN);
161   -
162 169 /* Get Accept-Language header from client */
163 170 in_hdr = VRT_GetHdr(sp, HDR_REQ, "\020Accept-Language:");
164 171  
165 172 /* Normalize and filter out by list of supported languages */
  173 + memset(lang, 0, sizeof(lang));
166 174 select_language(in_hdr, lang);
167 175  
168 176 /* By default, use a different header name: don't mess with backend logic */
... ... @@ -191,3 +199,4 @@ sub vcl_fetch {
191 199 set beresp.http.Vary = "X-Varnish-Accept-Language";
192 200 }
193 201 }
  202 +
... ...
features/change_organization_name.feature
... ... @@ -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   -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 27 And I press "Save"
28 28 Then I should not see "Birth date is invalid"
29 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   -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   -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 0 \ No newline at end of file
features/http_caching.feature
... ... @@ -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   -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   -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   -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   -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   -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   -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 293 Then "José da Silva" should be a member of "Free Software"
294 294  
295 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 296 Scenario: user registration is moderated by admin
319 297 Given feature "admin_must_approve_new_users" is enabled on environment
320 298 And feature "skip_new_user_email_confirmation" is disabled on environment
... ... @@ -364,4 +342,4 @@ Feature: signup
364 342 And I fill in "Username / Email" with "teste"
365 343 And I fill in "Password" with "123456"
366 344 And I press "Log in"
367   - Then I should not see "teste"
368 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   -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 692 ActionController::Base.perform_caching = (state == 'on')
693 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 695 Given /^the environment is configured to (.*) after login$/ do |option|
701 696 redirection = case option
702 697 when 'stay on the same page'
... ...
gitignore.example
... ... @@ -43,3 +43,4 @@ debian/noosfero-apache/
43 43 features/plugins/*
44 44 plugins/solr/config/solr.yml
45 45 /solr
  46 +/Gemfile.lock
... ...
lib/noosfero.rb
... ... @@ -3,6 +3,10 @@
3 3 require 'fast_gettext'
4 4 module Noosfero
5 5  
  6 + def self.root(default = nil)
  7 + ENV.fetch('RAILS_RELATIVE_URL_ROOT', default)
  8 + end
  9 +
6 10 def self.pattern_for_controllers_in_directory(dir)
7 11 disjunction = controllers_in_directory(dir).join('|')
8 12 pattern = disjunction.blank? ? '' : ('(' + disjunction + ')')
... ...
lib/noosfero/plugin.rb
... ... @@ -12,6 +12,12 @@ class Noosfero::Plugin
12 12  
13 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 21 def should_load
16 22 @should_load.nil? && true || @boot
17 23 end
... ...
lib/noosfero/version.rb
1 1 module Noosfero
2 2 PROJECT = 'noosfero'
3   - VERSION = '1.0~rc3'
  3 + VERSION = '1.0~rc4'
4 4 end
... ...
lib/tasks/ci.rake 0 → 100644
... ... @@ -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 6  
7 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 9 task :migrate do
21 10 plugin_migration_dirs.each do |path|
22 11 ActiveRecord::Migrator.migrate(path, ENV["VERSION"] ?
... ... @@ -26,5 +15,3 @@ namespace :noosfero do
26 15 end
27 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 12 @all_plugins = Dir.glob('plugins/*').map { |f| File.basename(f) } - ['template']
2 13 @all_plugins.sort!
3 14 @all_tasks = [:units, :functionals, :integration, :cucumber, :selenium]
... ... @@ -104,7 +115,7 @@ def run_test(name, files)
104 115 end
105 116  
106 117 def run_testrb(files)
107   - sh 'testrb', '-Itest', *files
  118 + sh 'testrb', '-I.:test', *files
108 119 end
109 120  
110 121 def run_cucumber(profile, files)
... ... @@ -167,6 +178,7 @@ def test_sequence(plugins, tasks)
167 178 end
168 179 end
169 180 rollback_plugins_state
  181 + yield(failed) if block_given?
170 182 fail 'There are broken tests to be fixed!' if fail_flag
171 183 end
172 184  
... ... @@ -195,13 +207,39 @@ namespace :test do
195 207 @all_tasks.each do |taskname|
196 208 desc "Run #{taskname} tests for all plugins"
197 209 task taskname do
198   - test_sequence(@all_plugins, taskname)
  210 + test_sequence(@all_plugins - @broken_plugins, taskname)
199 211 end
200 212 end
201 213 end
202 214  
203 215 desc "Run all tests for all plugins"
204 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 220 end
207 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 222 puts "I: please upload the package manually!"
223 223 end
224 224  
225   - rm_f "rm tmp/pending-release"
  225 + rm_f "tmp/pending-release"
226 226 end
227 227  
228 228 desc 'Build Debian packages'
... ...
lib/white_list_filter.rb
... ... @@ -9,7 +9,7 @@ module WhiteListFilter
9 9 unless iframe =~ /src=['"].*src=['"]/
10 10 trusted_sites.each do |trusted_site|
11 11 re_dom = trusted_site.gsub('.', '\.')
12   - if iframe =~ /src=["']https?:\/\/(www\.)?#{re_dom}\//
  12 + if iframe =~ /src=["'](https?:)?\/\/(www\.)?#{re_dom}\//
13 13 result = iframe
14 14 end
15 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 8 if @no_more_pages
9 9 page.replace_html "comments_list_group_#{@group_id}_more", ""
10 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 12 end
... ...
plugins/community_block/test/functional/commmunity_block_plugin_profile_controller_test.rb 0 → 100644
... ... @@ -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   -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 23 <%= link_to(
24 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 27 :class => 'simplemenu-trigger') %>
28 28  
29 29 <% end %>
... ... @@ -32,11 +32,11 @@
32 32 <% if profile.members.include?(user) || profile.already_request_membership?(user) %>
33 33 <%= link_to(
34 34 content_tag('span', '', :class => 'community-block-button icon-remove'),
35   - profile.leave_url) %>
  35 + profile.leave_url, :class => 'join-community') %>
36 36 <% else %>
37 37 <%= link_to(
38 38 content_tag('span', '', :class => 'community-block-button icon-add'),
39   - profile.join_url) %>
  39 + profile.join_url, :class => 'join-community') %>
40 40 <% end %>
41 41 <% else %>
42 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 101 should 'render tracks in track list block' do
102 102 @block = CommunityTrackPlugin::TrackListBlock.create!(:box => @profile.boxes.last)
103 103 get :view_page, @step.url
104   - file = File.open('result.html', 'w+')
105   - file.write(@response.body)
106   - file.close
107 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 105 end
109 106  
... ...
plugins/community_track/test/unit/community_track_plugin_test.rb
... ... @@ -6,10 +6,13 @@ class CommunityTrackPluginTest &lt; ActiveSupport::TestCase
6 6 @plugin = CommunityTrackPlugin.new
7 7 @profile = fast_create(Community)
8 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 13 end
11 14  
12   - attr_reader :profile, :params
  15 + attr_reader :profile, :params, :context
13 16  
14 17 should 'has name' do
15 18 assert CommunityTrackPlugin.plugin_name
... ... @@ -28,37 +31,37 @@ class CommunityTrackPluginTest &lt; ActiveSupport::TestCase
28 31 end
29 32  
30 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 35 assert_not_includes @plugin.content_types, CommunityTrackPlugin::Track
33 36 end
34 37  
35 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 41 assert_not_includes @plugin.content_types, CommunityTrackPlugin::Track
39 42 end
40 43  
41 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 47 assert_includes @plugin.content_types, CommunityTrackPlugin::Step
45 48 end
46 49  
47 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 53 assert_not_includes @plugin.content_types, CommunityTrackPlugin::Step
51 54 end
52 55  
53 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 59 assert_equivalent [CommunityTrackPlugin::Step, CommunityTrackPlugin::Track], @plugin.content_types
57 60 end
58 61  
59 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 65 assert_equivalent [CommunityTrackPlugin::Step, CommunityTrackPlugin::Track], @plugin.content_types
63 66 end
64 67  
... ...
plugins/container_block/lib/container_block_plugin/container_block.rb
... ... @@ -38,9 +38,11 @@ class ContainerBlockPlugin::ContainerBlock &lt; Block
38 38 end
39 39  
40 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 43 settings[:container_box_id] = container_box.id
  44 + copy_blocks unless @blocks_to_copy.blank?
  45 + container_box.update_attribute(:position, nil)
44 46 save!
45 47 end
46 48  
... ... @@ -71,4 +73,23 @@ class ContainerBlockPlugin::ContainerBlock &lt; Block
71 73 end
72 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 95 end
... ...
plugins/container_block/test/unit/profile_test.rb 0 → 100644
... ... @@ -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 23  
24 24 respond_to do |format|
25 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 27 format.html { redirect_to(:action=>'index') }
28 28 else
29 29 format.html { render :action => 'new' }
... ... @@ -43,7 +43,7 @@ class CustomFormsPluginMyprofileController &lt; MyProfileController
43 43  
44 44 respond_to do |format|
45 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 47 format.html { redirect_to(:action=>'index') }
48 48 else
49 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 226 end
227 227  
228 228 should 'list pending submissions for a form' do
229   - person = fast_create(Person)
  229 + person = create_user('john').person
230 230 form = CustomFormsPlugin::Form.create!(:profile => profile, :name => 'Free Software', :for_admission => true)
231 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 3 class CustomFormsPlugin::AdmissionSurveyTest < ActiveSupport::TestCase
4 4 should 'add member to community on perform' do
5 5 profile = fast_create(Community)
6   - person = fast_create(Person)
  6 + person = create_user('john').person
7 7 form = CustomFormsPlugin::Form.create!(:name => 'Simple Form', :profile => profile)
8 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 244  
245 245 should 'cancel survey tasks after removing a form' do
246 246 profile = fast_create(Profile)
247   - person = fast_create(Person)
  247 + person = create_user('john').person
248 248  
249 249 form1 = CustomFormsPlugin::Form.create!(:name => 'Free Software', :profile => profile)
250 250 form2 = CustomFormsPlugin::Form.create!(:name => 'Operation System', :profile => profile)
... ...