Commit 90f4f1c1a162989d3361779b97078849da9a7562

Authored by Rodrigo Souto
2 parents f8c24bf8 79820ea9

Merge branch 'master' of gitlab.com:noosfero/noosfero

Showing 768 changed files with 11745 additions and 55672 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 768 files displayed.

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