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.

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