Commit 37439649ee6ea45759cf3242012249e3eaeb0d55

Authored by Victor Costa
2 parents ab47509b 423aba17

Merge branch 'rails3' of gitlab.com:noosfero/noosfero into rails3

Conflicts:
	lib/noosfero/plugin/routes.rb
	plugins/container_block/test/unit/block_test.rb
	plugins/container_block/test/unit/environment_test.rb
	plugins/container_block/test/unit/profile_test.rb
	plugins/container_block/views/blocks/container.html.erb
Showing 81 changed files with 1032 additions and 190 deletions   Show diff stats
INSTALL.md
... ... @@ -23,7 +23,7 @@ You need to install some packages Noosfero depends on. On Debian GNU/Linux or De
23 23 # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby1.8 \
24 24 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libhpricot-ruby \
25 25 libwill-paginate-ruby iso-codes libfeedparser-ruby libdaemons-ruby thin \
26   - tango-icon-theme libnokogiri-ruby
  26 + tango-icon-theme
27 27  
28 28 On other systems, they may or may not be available through your regular package management system. Below are the links to their homepages.
29 29  
... ... @@ -41,7 +41,6 @@ On other systems, they may or may not be available through your regular package
41 41 * Thin: http://code.macournoyer.com/thin
42 42 * tango-icon-theme: http://tango.freedesktop.org/Tango_Icon_Library
43 43 * Hpricot: http://hpricot.com
44   -* Nokogiri: http://nokogiri.org/
45 44  
46 45 If you manage to install Noosfero successfully on other systems than Debian,
47 46 please feel free to contact the Noosfero development mailing with the
... ...
app/controllers/admin/users_controller.rb
... ... @@ -7,7 +7,7 @@ class UsersController < AdminController
7 7 include UsersHelper
8 8  
9 9 def index
10   - @filter = params[:filter]
  10 + @filter = params[:filter] || 'all_users'
11 11 scope = environment.people.no_templates
12 12 if @filter == 'admin_users'
13 13 scope = scope.admins
... ... @@ -16,6 +16,7 @@ class UsersController < AdminController
16 16 elsif @filter == 'deactivated_users'
17 17 scope = scope.deactivated
18 18 end
  19 + scope = scope.order('name ASC')
19 20 @q = params[:q]
20 21 @collection = find_by_contents(:people, scope, @q, {:per_page => per_page, :page => params[:npage]})[:results]
21 22 end
... ...
app/controllers/application_controller.rb
... ... @@ -24,6 +24,7 @@ class ApplicationController < ActionController::Base
24 24 include ApplicationHelper
25 25 layout :get_layout
26 26 def get_layout
  27 + return nil if request.format == :js
27 28 theme_layout = theme_option(:layout)
28 29 if theme_layout
29 30 theme_view_file('layouts/'+theme_layout) || theme_layout
... ...
app/controllers/box_organizer_controller.rb
... ... @@ -70,7 +70,7 @@ class BoxOrganizerController < ApplicationController
70 70 else
71 71 @center_block_types = (Box.acceptable_center_blocks & available_blocks) + plugins.dispatch(:extra_blocks, :type => boxes_holder.class, :position => 1)
72 72 @side_block_types = (Box.acceptable_side_blocks & available_blocks) + plugins.dispatch(:extra_blocks, :type => boxes_holder.class, :position => [2,3])
73   - @boxes = boxes_holder.boxes
  73 + @boxes = boxes_holder.boxes.with_position
74 74 render :action => 'add_block', :layout => false
75 75 end
76 76 end
... ...
app/controllers/my_profile/profile_members_controller.rb
... ... @@ -2,7 +2,7 @@ class ProfileMembersController < MyProfileController
2 2 protect 'manage_memberships', :profile
3 3  
4 4 def index
5   - @members = profile.members
  5 + @members = profile.members_by_name
6 6 @member_role = environment.roles.find_by_name('member')
7 7 end
8 8  
... ...
app/controllers/public/profile_controller.rb
... ... @@ -67,7 +67,7 @@ class ProfileController < PublicController
67 67  
68 68 def members
69 69 if is_cache_expired?(profile.members_cache_key(params))
70   - @members = profile.members.includes(relations_to_include).paginate(:per_page => members_per_page, :page => params[:npage])
  70 + @members = profile.members_by_name.includes(relations_to_include).paginate(:per_page => members_per_page, :page => params[:npage])
71 71 end
72 72 end
73 73  
... ...
app/helpers/application_helper.rb
... ... @@ -1415,16 +1415,16 @@ module ApplicationHelper
1415 1415 end
1416 1416  
1417 1417 def convert_macro(html, source)
1418   - doc = Nokogiri::HTML(html)
  1418 + doc = Hpricot(html)
1419 1419 #TODO This way is more efficient but do not support macro inside of
1420 1420 # macro. You must parse them from the inside-out in order to enable
1421 1421 # that.
1422   - doc.css('.macro').each do |macro|
  1422 + doc.search('.macro').each do |macro|
1423 1423 macro_name = macro['data-macro']
1424 1424 result = @plugins.parse_macro(macro_name, macro, source)
1425   - macro.content = result.kind_of?(Proc) ? self.instance_eval(&result) : result
  1425 + macro.inner_html = result.kind_of?(Proc) ? self.instance_eval(&result) : result
1426 1426 end
1427   - CGI.unescapeHTML(doc.xpath('//body/*').to_s)
  1427 + doc.html
1428 1428 end
1429 1429  
1430 1430 def default_folder_for_image_upload(profile)
... ...
app/helpers/boxes_helper.rb
... ... @@ -39,7 +39,7 @@ module BoxesHelper
39 39 end
40 40  
41 41 def display_boxes(holder, main_content)
42   - boxes = holder.boxes.first(holder.boxes_limit)
  42 + boxes = holder.boxes.with_position.first(holder.boxes_limit)
43 43 content = boxes.reverse.map { |item| display_box(item, main_content) }.join("\n")
44 44 content = main_content if (content.blank?)
45 45  
... ...
app/models/box.rb
... ... @@ -7,6 +7,8 @@ class Box < ActiveRecord::Base
7 7  
8 8 include Noosfero::Plugin::HotSpot
9 9  
  10 + named_scope :with_position, :conditions => ['boxes.position > 0']
  11 +
10 12 def environment
11 13 owner ? (owner.kind_of?(Environment) ? owner : owner.environment) : nil
12 14 end
... ...
app/models/environment.rb
... ... @@ -190,7 +190,7 @@ class Environment < ActiveRecord::Base
190 190 has_many :states
191 191 has_many :cities
192 192  
193   - has_many :roles
  193 + has_many :roles, :dependent => :destroy
194 194  
195 195 has_many :qualifiers
196 196 has_many :certifiers
... ...
app/models/link_list_block.rb
... ... @@ -65,7 +65,7 @@ class LinkListBlock < Block
65 65 def link_html(link)
66 66 klass = 'icon-' + link[:icon] if link[:icon]
67 67 sanitize_link(
68   - link_to(link[:name], expand_address(link[:address]), :target => link[:target], :class => klass)
  68 + link_to(link[:name], expand_address(link[:address]), :target => link[:target], :class => klass, :title => link[:title])
69 69 )
70 70 end
71 71  
... ...
app/models/profile.rb
... ... @@ -96,6 +96,10 @@ class Profile < ActiveRecord::Base
96 96 ScopeTool.union *scopes
97 97 end
98 98  
  99 + def members_by_name
  100 + members.order(:name)
  101 + end
  102 +
99 103 def members_count
100 104 members.count
101 105 end
... ...
app/views/blocks/location.html.erb
... ... @@ -3,7 +3,6 @@
3 3 <div class='the-localization-map'>
4 4 <img src="https://maps.google.com/maps/api/staticmap?center=<%=profile.lat%>,<%=profile.lng%>&zoom=<%=block.zoom%>&size=190x250&maptype=<%=block.map_type%>&markers=<%=profile.lat%>,<%=profile.lng%>&sensor=false"/>
5 5 </div>
6   -</div>
7 6 <% else %>
8 7 <i><%= _('This profile has no geographical position registered.') %></i>
9 8 <% end %>
... ...
app/views/box_organizer/_link_list_block.html.erb
1 1 <strong><%= _('Links') %></strong>
2   -<div id='edit-link-list-block' style='width:450px'>
  2 +<div id='edit-link-list-block'>
3 3 <table id='links' class='noborder'>
4   - <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th><th><%= _('Target') %></th></tr>
  4 + <tr>
  5 + <th><%= _('Icon') %></th>
  6 + <th><%= _('Name') %></th>
  7 + <th><%= _('Address') %></th>
  8 + <th><%= _('Title') %></th>
  9 + <th><%= _('Target') %></th>
  10 + </tr>
5 11 <% for link in @block.links do %>
6   - <tr>
7   - <td>
8   - <%= icon_selector(link['icon']) %>
9   - </td>
10   - <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td>
11   - <td class='cel-address'><%= text_field_tag 'block[links][][address]', link[:address], :class => 'link-address' %></td>
12   - <td>
13   - <%= select_tag('block[links][][target]', options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target])) %>
14   - </td>
15   - </tr>
  12 + <tr>
  13 + <td><%= icon_selector(link['icon']) %></td>
  14 + <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td>
  15 + <td class='cel-address'><%= text_field_tag 'block[links][][address]', link[:address], :class => 'link-address' %></td>
  16 + <td><%= text_field_tag 'block[links][][title]', link[:title], :class => 'link-title' %></td>
  17 + <td><%= select_tag('block[links][][target]', options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target])) %></td>
  18 + </tr>
16 19 <% end %>
17 20 </table>
18 21 </div>
... ... @@ -22,8 +25,8 @@
22 25 content_tag('td', icon_selector('ok')) +
23 26 content_tag('td', text_field_tag('block[links][][name]', '', :maxlength => 20)) +
24 27 content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'link-address'), :class => 'cel-address') +
25   - content_tag('td', select_tag('block[links][][target]',
26   -options_for_select(LinkListBlock::TARGET_OPTIONS, '_self')))
  28 + content_tag('td', text_field_tag('block[links][][title]', '', :class => 'link-title')) +
  29 + content_tag('td', select_tag('block[links][][target]', options_for_select(LinkListBlock::TARGET_OPTIONS, '_self')))
27 30 ) +
28 31 javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight"))
29 32 end %>
... ...
app/views/content_viewer/view_page.html.erb
... ... @@ -40,14 +40,6 @@
40 40 </div>
41 41 <% end %>
42 42  
43   -<% if !@page.tags.empty? %>
44   - <div id="article-tags">
45   - <%= _("This article's tags:") %>
46   - <%= @page.tags.map { |t| link_to(t, :controller => 'profile', :profile => @profile.identifier, :action => 'tags', :id => t.name ) }.join("\n") %>
47   - </div>
48   -<% end %>
49   -
50   -
51 43 <%= render :partial => 'shared/disabled_enterprise' %>
52 44  
53 45 <% if NOOSFERO_CONF['addthis_enabled'] %>
... ... @@ -83,6 +75,13 @@
83 75 </div>
84 76 <% end %>
85 77  
  78 +<% if !@page.tags.empty? %>
  79 + <div id="article-tags">
  80 + <%= _("This article's tags:") %>
  81 + <%= @page.tags.map { |t| link_to(t, :controller => 'profile', :profile => @profile.identifier, :action => 'tags', :id => t.name ) }.join("\n") %>
  82 + </div>
  83 +<% end %>
  84 +
86 85 <%= display_source_info(@page) %>
87 86  
88 87 <div class="comments" id="comments_list">
... ...
app/views/profile_members/_members_list.html.erb
1   -<% collection = @collection == :profile_admins ? profile.admins : profile.members %>
  1 +<% collection = @collection == :profile_admins ? profile.admins : profile.members_by_name %>
2 2 <% title = @title ? @title : _('Current members') %>
3 3 <% remove_action = @remove_action ? @remove_action : {:action => 'unassociate'} %>
4 4  
... ...
debian/changelog
  1 +noosfero (0.46.1) unstable; urgency=low
  2 +
  3 + * Bugfixes release
  4 +
  5 + -- Daniela Soares Feitosa <daniela@colivre.coop.br> Fri, 07 Mar 2014 10:33:11 +0000
  6 +
1 7 noosfero (0.46.0) unstable; urgency=low
2 8  
3 9 * New features release
... ...
lib/noosfero.rb
... ... @@ -4,7 +4,7 @@ require &#39;fast_gettext&#39;
4 4  
5 5 module Noosfero
6 6 PROJECT = 'noosfero'
7   - VERSION = '0.46.0'
  7 + VERSION = '0.46.1'
8 8  
9 9 def self.pattern_for_controllers_in_directory(dir)
10 10 disjunction = controllers_in_directory(dir).join('|')
... ...
lib/noosfero/core_ext/array.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class Array
  2 +
  3 + def uniq_by
  4 + hash, array = {}, []
  5 + each { |i| hash[yield(i)] ||= (array << i) }
  6 + array
  7 + end
  8 +
  9 +end
... ...
lib/noosfero/plugin/macro.rb
... ... @@ -33,9 +33,9 @@ class Noosfero::Plugin::Macro
33 33 end
34 34  
35 35 def attributes(macro)
36   - macro.attributes.
  36 + macro.attributes.to_hash.
37 37 select {|key, value| key[0..10] == 'data-macro-'}.
38   - inject({}){|result, a| result.merge({a[0][11..-1] => a[1].value})}.
  38 + inject({}){|result, a| result.merge({a[0][11..-1] => a[1]})}.
39 39 with_indifferent_access
40 40 end
41 41  
... ...
lib/noosfero/plugin/routes.rb
... ... @@ -15,7 +15,11 @@ Dir.glob(Rails.root.join(plugins_root, &#39;*&#39;, &#39;controllers&#39;)) do |controllers_dir|
15 15 controllers_by_folder.each do |folder, controllers|
16 16 controllers.each do |controller|
17 17 controller_name = controller.gsub("#{plugin_name}_plugin_",'')
18   - match "#{prefixes_by_folder[folder]}/#{plugin_name}/#{controller_name}(/:action(/:id))", :controller => controller
  18 + if %w[profile myprofile].include?(folder)
  19 + match "#{prefixes_by_folder[folder]}/#{plugin_name}/#{controller_name}(/:action(/:id))", :controller => controller, :profile => /#{Noosfero.identifier_format}/
  20 + else
  21 + match "#{prefixes_by_folder[folder]}/#{plugin_name}/#{controller_name}(/:action(/:id))", :controller => controller
  22 + end
19 23 end
20 24 end
21 25  
... ...
plugins/comment_classification/README.md 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +README - Comment Classification Plugin
  2 +======================================
  3 +
  4 +This plugin creates the structure for classifying the comments. The
  5 +initial idea of this plugin is to support the management of public
  6 +consulting, but it can be used in different contexts.
  7 +For now, two kind of classification will be available:
  8 +
  9 + * Label: when creating a comment, the user identify the kind of it by
  10 +choosing the label of the comment. Example: "Suggestion",
  11 +"Disagreement"...
  12 + * Status: users with permission can include a Status for a comment.
  13 +Example: "Merged", "Unmerged"
  14 +
  15 +Dependency
  16 +==========
  17 +
  18 +This plugin was developed to help public consulting and needs the
  19 +CommentGroupPlugin enabled to be used.
... ...
plugins/comment_classification/controllers/admin/comment_classification_plugin_labels_controller.rb 0 → 100644
... ... @@ -0,0 +1,55 @@
  1 +class CommentClassificationPluginLabelsController < AdminController
  2 + append_view_path File.join(File.dirname(__FILE__) + '/../../views')
  3 +
  4 + def index
  5 +# @labels = @environment.labels
  6 + @labels = CommentClassificationPlugin::Label.all
  7 + end
  8 +
  9 + def create
  10 + @label = CommentClassificationPlugin::Label.new(params[:label])
  11 + @colors = CommentClassificationPlugin::Label::COLORS
  12 + if request.post?
  13 + begin
  14 + @label.owner = environment
  15 + @label.save!
  16 + session[:notice] = _('Label created')
  17 + redirect_to :action => 'index'
  18 + rescue
  19 + session[:notice] = _('Label could not be created')
  20 + end
  21 + end
  22 + end
  23 +
  24 + def edit
  25 +# @labels = @environment.labels.find(params[:id])
  26 + @label = CommentClassificationPlugin::Label.find(params[:id])
  27 + @colors = CommentClassificationPlugin::Label::COLORS
  28 + if request.post?
  29 + begin
  30 + @label.update_attributes!(params[:label])
  31 + session[:notice] = _('Label updated')
  32 + redirect_to :action => :index
  33 + rescue
  34 + session[:notice] = _('Failed to edit label')
  35 + end
  36 + end
  37 + end
  38 +
  39 + def remove
  40 +# @label = environment.labels.find(params[:label])
  41 + @label = CommentClassificationPlugin::Label.find(params[:id])
  42 + if request.post?
  43 + begin
  44 + @label.destroy
  45 + session[:notice] = _('Label removed')
  46 + rescue
  47 + session[:notice] = _('Label could not be removed')
  48 + end
  49 + else
  50 + session[:notice] = _('Label could not be removed')
  51 + end
  52 + redirect_to :action => 'index'
  53 + end
  54 +
  55 +end
... ...
plugins/comment_classification/controllers/admin/comment_classification_plugin_status_controller.rb 0 → 100644
... ... @@ -0,0 +1,53 @@
  1 +class CommentClassificationPluginStatusController < AdminController
  2 + append_view_path File.join(File.dirname(__FILE__) + '/../../views')
  3 +
  4 + def index
  5 +# @labels = @environment.labels
  6 + @status = CommentClassificationPlugin::Status.all
  7 + end
  8 +
  9 + def create
  10 + @status = CommentClassificationPlugin::Status.new(params[:status])
  11 + if request.post?
  12 + begin
  13 + @status.owner = environment
  14 + @status.save!
  15 + session[:notice] = _('Status created')
  16 + redirect_to :action => 'index'
  17 + rescue
  18 + session[:notice] = _('Status could not be created')
  19 + end
  20 + end
  21 + end
  22 +
  23 + def edit
  24 +# @labels = @environment.labels.find(params[:id])
  25 + @status = CommentClassificationPlugin::Status.find(params[:id])
  26 + if request.post?
  27 + begin
  28 + @status.update_attributes!(params[:status])
  29 + session[:notice] = _('Status updated')
  30 + redirect_to :action => :index
  31 + rescue
  32 + session[:notice] = _('Failed to edit status')
  33 + end
  34 + end
  35 + end
  36 +
  37 + def remove
  38 +# @label = environment.labels.find(params[:label])
  39 + @status = CommentClassificationPlugin::Status.find(params[:id])
  40 + if request.post?
  41 + begin
  42 + @status.destroy
  43 + session[:notice] = _('Status removed')
  44 + rescue
  45 + session[:notice] = _('Status could not be removed')
  46 + end
  47 + else
  48 + session[:notice] = _('Status could not be removed')
  49 + end
  50 + redirect_to :action => 'index'
  51 + end
  52 +
  53 +end
... ...
plugins/comment_classification/controllers/comment_classification_plugin_admin_controller.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +class CommentClassificationPluginAdminController < AdminController
  2 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  3 +
  4 + def index
  5 + end
  6 +
  7 +end
... ...
plugins/comment_classification/controllers/comment_classification_plugin_myprofile_controller.rb 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +class CommentClassificationPluginMyprofileController < MyProfileController
  2 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  3 +
  4 + before_filter :organizations_only
  5 + protect 'moderate_comments', :profile
  6 +
  7 + def index
  8 + @comments = Comment.all
  9 + end
  10 +
  11 + def add_status
  12 + @comment = Comment.find(params[:id])
  13 + @statuses = CommentClassificationPlugin::Status.enabled
  14 + @status = CommentClassificationPlugin::CommentStatusUser.new(:profile => user, :comment => @comment)
  15 + if request.post? && params[:status]
  16 + @status.update_attributes(params[:status])
  17 + @status.save
  18 + end
  19 + end
  20 +
  21 + private
  22 +
  23 + def organizations_only
  24 + render_not_found if !profile.organization?
  25 + end
  26 +end
... ...
plugins/comment_classification/db/migrate/20130822043033_create_comments_labels.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +class CreateCommentsLabels < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :comment_classification_plugin_labels do |t|
  4 + t.string :name
  5 + t.string :color
  6 + t.boolean :enabled, :default => true
  7 + t.references :owner, :polymorphic => true
  8 +
  9 + t.timestamps
  10 + end
  11 + end
  12 +
  13 + def self.down
  14 + drop_table :comment_classification_plugin_labels
  15 + end
  16 +end
... ...
plugins/comment_classification/db/migrate/20130822075623_create_comment_label_user.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +class CreateCommentLabelUser < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :comment_classification_plugin_comment_label_user do |t|
  4 + t.references :profile
  5 + t.references :comment
  6 + t.references :label
  7 +
  8 + t.timestamps
  9 + end
  10 +
  11 + end
  12 +
  13 + def self.down
  14 + drop_table :comment_classification_plugin_comment_label_user
  15 + end
  16 +end
... ...
plugins/comment_classification/db/migrate/20130829130226_create_comment_status.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +class CreateCommentStatus < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :comment_classification_plugin_statuses do |t|
  4 + t.string :name
  5 + t.boolean :enabled, :default => true
  6 + t.boolean :enable_reason, :default => true
  7 + t.references :owner, :polymorphic => true
  8 + t.timestamps
  9 + end
  10 +
  11 + end
  12 +
  13 + def self.down
  14 + drop_table :comment_classification_plugin_statuses
  15 + end
  16 +end
... ...
plugins/comment_classification/db/migrate/20130829144037_create_comment_status_user.rb 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +class CreateCommentStatusUser < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :comment_classification_plugin_comment_status_user do |t|
  4 + t.references :profile
  5 + t.references :comment
  6 + t.references :status
  7 + t.text :reason
  8 +
  9 + t.timestamps
  10 + end
  11 + end
  12 +
  13 + def self.down
  14 + drop_table :comment_classification_plugin_comment_status_user
  15 + end
  16 +end
... ...
plugins/comment_classification/features/labels.feature 0 → 100644
... ... @@ -0,0 +1,64 @@
  1 +Feature:
  2 + As a user
  3 + I want to add label for comments
  4 +
  5 +Background:
  6 + Given the following users
  7 + | login | name |
  8 + | joaosilva | Joao Silva |
  9 + | mariasilva | Maria Silva |
  10 + And the following communities
  11 + | identifier | name |
  12 + | sample-community | Sample Community |
  13 + And the following articles
  14 + | owner | name | body |
  15 + | sample-community | Article to comment | First post |
  16 + And CommentClassificationPlugin is enabled
  17 + And "Maria Silva" is a member of "Sample Community"
  18 + And "Joao Silva" is admin of "Sample Community"
  19 + And I am logged in as "joaosilva"
  20 +
  21 + @selenium
  22 + Scenario: dont display labels if admin did not configure status
  23 + Given I am on article "Article to comment"
  24 + And I follow "Post a comment"
  25 + Then I should not see "Label" within "#page-comment-form"
  26 +
  27 + Scenario: admin configure labels
  28 + Given I am logged in as "admin_user"
  29 + And I am on the environment control panel
  30 + And I follow "Plugins"
  31 + And I follow "Configuration"
  32 + And I follow "Manage Labels"
  33 + Then I should see "no label registered yet" within "#comment-classification-labels"
  34 + When I follow "Add a new label"
  35 + And I fill in "Name" with "Question"
  36 + And I check "Enable this label"
  37 + And I press "Save"
  38 + Then I should see "Question" within "#comment-classification-labels"
  39 +
  40 + @selenium
  41 + Scenario: save label for comment
  42 + Given the following labels
  43 + | owner | name | enabled |
  44 + | environment | Addition | true |
  45 + And I go to article "Article to comment"
  46 + And I follow "Post a comment"
  47 + And I fill in "Enter your comment" with "Hey ho, let's go!"
  48 + Then I select "Addition" from "comment_label_id"
  49 + And I press "Post comment"
  50 + Then I should see "Addition" within ".comment-details"
  51 +
  52 + @selenium
  53 + Scenario: users without permission should not edit the labels
  54 + Given the following labels
  55 + | owner | name | enabled |
  56 + | environment | Addition | true |
  57 + And I go to article "Article to comment"
  58 + And I follow "Post a comment"
  59 + Then I should see "Label" within "#page-comment-form"
  60 + And I should see "Addition" within "#comment_label_id"
  61 + When I am not logged in
  62 + And I am on article "Article to comment"
  63 + And I follow "Post a comment"
  64 + Then I should not see "Label" within "#page-comment-form"
... ...
plugins/comment_classification/features/status.feature 0 → 100644
... ... @@ -0,0 +1,67 @@
  1 +Feature:
  2 + As a user
  3 + I want to add status for comments
  4 +
  5 +Background:
  6 + Given the following users
  7 + | login | name |
  8 + | joaosilva | Joao Silva |
  9 + | mariasilva | Maria Silva |
  10 + And the following communities
  11 + | identifier | name |
  12 + | sample-community | Sample Community |
  13 + And the following articles
  14 + | owner | name | body |
  15 + | sample-community | Article to comment | First post |
  16 + And the following comments
  17 + | article | author | body |
  18 + | Article to comment | mariasilva | great post! |
  19 + And CommentClassificationPlugin is enabled
  20 + And "Maria Silva" is a member of "Sample Community"
  21 + And "Joao Silva" is admin of "Sample Community"
  22 + And I am logged in as "joaosilva"
  23 +
  24 + Scenario: dont display to add status if not an organization
  25 + Given the following articles
  26 + | owner | name | body |
  27 + | joaosilva | Article on a person profile | First post |
  28 + And the following comments
  29 + | article | author | body |
  30 + | Article on a person profile | mariasilva | great post! |
  31 + Given I am on article "Article on a person profile"
  32 + Then I should see "great post!" within ".comment-details"
  33 + And I should not see "Status" within ".comment-details"
  34 +
  35 + Scenario: dont display to add status if admin did not configure status
  36 + Given I am on article "Article to comment"
  37 + Then I should see "great post!" within ".comment-details"
  38 + And I should not see "Status" within ".comment-details"
  39 +
  40 + Scenario: admin configure status
  41 + Given I am logged in as "admin_user"
  42 + And I am on the environment control panel
  43 + And I follow "Plugins"
  44 + And I follow "Configuration"
  45 + And I follow "Manage Status"
  46 + Then I should see "no status registered yet" within "#comment-classification-status"
  47 + When I follow "Add a new status"
  48 + And I fill in "Name" with "Merged"
  49 + And I check "Enable this status"
  50 + And I press "Save"
  51 + Then I should see "Merged" within "#comment-classification-status"
  52 +
  53 + Scenario: save status for comment
  54 + Given the following status
  55 + | owner | name | enabled |
  56 + | environment | Merged | true |
  57 + And I go to article "Article to comment"
  58 + And I follow "Status"
  59 + Then I select "Merged" from "status_status_id"
  60 + And I press "Save"
  61 + Then I should see "added the status Merged" within "#comment-classification-status-list"
  62 +
  63 + Scenario: dont display to add status if user not allowed
  64 + Given I am logged in as "mariasilva"
  65 + When I go to article "Article to comment"
  66 + Then I should see "great post!" within ".comment-details"
  67 + And I should not see "Status" within ".comment-details"
... ...
plugins/comment_classification/features/step_definitions/plugin_steps.rb 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +Given /^CommentClassificationPlugin is enabled$/ do
  2 + Given %{I am logged in as admin}
  3 + And %{I am on the environment control panel}
  4 + And %{I follow "Plugins"}
  5 + And %{I check "Comment Classification"}
  6 + And %{I press "Save changes"}
  7 + Environment.default.enabled_plugins.should include("CommentClassificationPlugin")
  8 +end
  9 +
  10 +Given /^the following labels$/ do |table|
  11 + table.hashes.map{|item| item.dup}.each do |item|
  12 + owner_type = item.delete('owner')
  13 + owner = owner_type == 'environment' ? Environment.default : Profile[owner_type]
  14 + CommentClassificationPlugin::Label.create!(item)
  15 + end
  16 +end
  17 +
  18 +Given /^the following status$/ do |table|
  19 + table.hashes.map{|item| item.dup}.each do |item|
  20 + owner_type = item.delete('owner')
  21 + owner = owner_type == 'environment' ? Environment.default : Profile[owner_type]
  22 + CommentClassificationPlugin::Status.create!(item)
  23 + end
  24 +end
... ...
plugins/comment_classification/lib/comment_classification_plugin.rb 0 → 100644
... ... @@ -0,0 +1,57 @@
  1 +require 'ext/environment'
  2 +require 'ext/comment'
  3 +
  4 +class CommentClassificationPlugin < Noosfero::Plugin
  5 +
  6 + def self.plugin_name
  7 + "Comment Classification"
  8 + end
  9 +
  10 + def self.plugin_description
  11 + _("A plugin that allow classification of comments.")
  12 + end
  13 +
  14 +#TODO Each organization can add its own status and labels
  15 +# def control_panel_buttons
  16 +# if context.profile.organization?
  17 +# { :title => _('Manage comment classification'), :icon => 'comment_classification', :url => {:controller => 'comment_classification_plugin_myprofile'} }
  18 +# end
  19 +# end
  20 +
  21 + def comment_form_extra_contents(args)
  22 + comment = args[:comment]
  23 + lambda {
  24 + render :file => 'comment/comments_labels_select.rhtml', :locals => {:comment => comment }
  25 + }
  26 + end
  27 +
  28 + def comment_extra_contents(args)
  29 + comment = args[:comment]
  30 + lambda {
  31 + render :file => 'comment/comment_extra.rhtml', :locals => {:comment => comment}
  32 + }
  33 + end
  34 +
  35 + def process_extra_comment_params(args)
  36 + comment = Comment.find args[0]
  37 + label_id = args[1][:comment_label_id]
  38 + if label_id.blank?
  39 + if !CommentClassificationPlugin::CommentLabelUser.find_by_comment_id(comment.id).nil?
  40 + CommentClassificationPlugin::CommentLabelUser.find_by_comment_id(comment.id).destroy
  41 + end
  42 + else
  43 + label = CommentClassificationPlugin::Label.find label_id
  44 + relation = CommentClassificationPlugin::CommentLabelUser.new(:profile => comment.author, :comment => comment, :label => label )
  45 + relation.save
  46 + end
  47 + end
  48 +
  49 + def js_files
  50 + 'comment_classification.js'
  51 + end
  52 +
  53 + def stylesheet?
  54 + true
  55 + end
  56 +
  57 +end
... ...
plugins/comment_classification/lib/comment_classification_plugin/comment_label_user.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class CommentClassificationPlugin::CommentLabelUser < Noosfero::Plugin::ActiveRecord
  2 + set_table_name :comment_classification_plugin_comment_label_user
  3 +
  4 + belongs_to :profile
  5 + belongs_to :comment
  6 + belongs_to :label, :class_name => 'CommentClassificationPlugin::Label'
  7 +
  8 + validates_presence_of :profile
  9 + validates_presence_of :comment
  10 + validates_presence_of :label
  11 +end
... ...
plugins/comment_classification/lib/comment_classification_plugin/comment_status_user.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class CommentClassificationPlugin::CommentStatusUser < Noosfero::Plugin::ActiveRecord
  2 + set_table_name :comment_classification_plugin_comment_status_user
  3 +
  4 + belongs_to :profile
  5 + belongs_to :comment
  6 + belongs_to :status, :class_name => 'CommentClassificationPlugin::Status'
  7 +
  8 + validates_presence_of :profile
  9 + validates_presence_of :comment
  10 + validates_presence_of :status
  11 +end
... ...
plugins/comment_classification/lib/comment_classification_plugin/label.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class CommentClassificationPlugin::Label < Noosfero::Plugin::ActiveRecord
  2 +
  3 + belongs_to :owner, :polymorphic => true
  4 +
  5 + validates_presence_of :name
  6 +
  7 + named_scope :enabled, :conditions => { :enabled => true }
  8 +
  9 + COLORS = ['red', 'green', 'yellow', 'gray', 'blue']
  10 +end
... ...
plugins/comment_classification/lib/comment_classification_plugin/status.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +class CommentClassificationPlugin::Status < Noosfero::Plugin::ActiveRecord
  2 +
  3 + belongs_to :owner, :polymorphic => true
  4 +
  5 + validates_presence_of :name
  6 +
  7 + named_scope :enabled, :conditions => { :enabled => true }
  8 +end
... ...
plugins/comment_classification/lib/ext/comment.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +require_dependency 'comment'
  2 +require 'comment_classification_plugin.rb'
  3 +require 'comment_classification_plugin/label.rb'
  4 +
  5 +class Comment
  6 +
  7 + has_one :comment_classification_plugin_comment_label_user, :class_name => 'CommentClassificationPlugin::CommentLabelUser'
  8 + has_one :label, :through => :comment_classification_plugin_comment_label_user, :foreign_key => 'label_id'
  9 +
  10 + has_many :comment_classification_plugin_comment_status_users, :class_name => 'CommentClassificationPlugin::CommentStatusUser'
  11 + has_many :statuses, :through => :comment_classification_plugin_comment_status_users, :foreign_key => 'status_id'
  12 +
  13 +end
... ...
plugins/comment_classification/lib/ext/environment.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +require_dependency 'environment'
  2 +
  3 +class Environment
  4 +
  5 + has_many :labels, :as => :owner, :class_name => 'CommentClassificationPlugin::Label'
  6 +
  7 +end
  8 +
... ...
plugins/comment_classification/public/images/comment-classification.png 0 → 100644

4.15 KB

plugins/comment_classification/public/style.css 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +.controller-profile_editor .control-panel a.control-panel-comment_classification {
  2 + background-image: url(images/comment-classification.png);
  3 +}
  4 +
  5 +#content .comment-classification-options .label-name {
  6 + font-style: italic;
  7 +}
  8 +
  9 +#content .comment-classification-options a.button {
  10 + background-color: transparent;
  11 + border: none;
  12 +}
... ...
plugins/comment_classification/views/comment/comment_extra.rhtml 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +<div class='comment-classification-options'>
  2 +
  3 + <% unless comment.label.nil? %>
  4 + <p class='label-name' style='color:<%= comment.label.color %>'>
  5 + <%= comment.label.name %>
  6 + </p>
  7 + <% end %>
  8 +
  9 + <% statuses = CommentClassificationPlugin::Status.enabled %>
  10 + <% if profile.organization? && user && user.has_permission?(:moderate_comments, profile) && !statuses.empty? %>
  11 + <div class='comment-classification-status'>
  12 + <%= link_to(_('Status'), :profile => profile.identifier, :controller => :comment_classification_plugin_myprofile, :action => :add_status, :id => comment.id)
  13 + %>
  14 + </div>
  15 + <% end %>
  16 +
  17 +</div>
... ...
plugins/comment_classification/views/comment/comments_labels_select.rhtml 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +<% labels = CommentClassificationPlugin::Label.enabled %>
  2 +<% if logged_in? && user.has_permission?(:moderate_comments, profile) && !labels.empty? %>
  3 + <%= labelled_form_field(_('Label'), select_tag('comment_label_id', options_for_select( [[_('[Select ...]'), nil]] + labels.map{|l|[l.name,l.id]}, @comment.label.nil? ? '' : @comment.label.id))) %>
  4 +<% end %>
... ...
plugins/comment_classification/views/comment_classification_plugin_admin/index.rhtml 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<h1><%= _('Comments classification options')%></h1>
  2 +
  3 +<ul>
  4 + <li><%= link_to _('Manage Labels'), :controller => 'comment_classification_plugin_labels' %></li>
  5 + <li><%= link_to _('Manage Status'), :controller => 'comment_classification_plugin_status' %></li>
  6 +</ul>
... ...
plugins/comment_classification/views/comment_classification_plugin_labels/_form.rhtml 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +<%= error_messages_for :label %>
  2 +
  3 +<% form_for :label, @label do |f| %>
  4 + <%= required_fields_message %>
  5 +
  6 + <%= required labelled_form_field(_('Name'), f.text_field(:name)) %>
  7 + <%= labelled_form_field(_('Color'), f.select(:color, @colors.map{|s|[s.capitalize,s]})) %>
  8 + <%= labelled_form_field(f.check_box(:enabled) + _('Enable this label?'),'') %>
  9 +
  10 + <% button_bar do %>
  11 + <%= submit_button('save', _('Save'), :cancel => {:action => 'index'} ) %>
  12 + <% end %>
  13 +<% end %>
... ...
plugins/comment_classification/views/comment_classification_plugin_labels/create.rhtml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h2> <%= _("Add a new label") %> </h2>
  2 +
  3 +<%= render :partial => 'form' %>
... ...
plugins/comment_classification/views/comment_classification_plugin_labels/edit.rhtml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h2> <%= _("Editing label %s") % @label.name %> </h2>
  2 +
  3 +<%= render :partial => 'form' %>
... ...
plugins/comment_classification/views/comment_classification_plugin_labels/index.rhtml 0 → 100644
... ... @@ -0,0 +1,32 @@
  1 +<h1><%= _("Manage comments labels") %></h1>
  2 +
  3 +<div id='comment-classification-labels'>
  4 + <% if @labels.empty? %>
  5 + <%= _('(no label registered yet)') %>
  6 + <% else %>
  7 + <table>
  8 + <tr>
  9 + <th><%= _('Label') %></th>
  10 + <th><%= _('Color') %></th>
  11 + <th><%= _('Enabled') %></th>
  12 + <th><%= _('Actions') %></th>
  13 + </tr>
  14 + <% @labels.each do |label| %>
  15 + <tr>
  16 + <td><%= label.name %></td>
  17 + <td><%= label.color %></td>
  18 + <td><%= label.enabled %></td>
  19 + <td>
  20 + <%= button_without_text :edit, _('Edit'), {:action => 'edit', :id => label} %>
  21 + <%= button_without_text :delete, _('Remove'), {:action => 'destroy', :id => label}, :confirm => _('Are you sure you want to remove this label?') %>
  22 + </td>
  23 + </tr>
  24 + <% end %>
  25 + </table>
  26 + <% end %>
  27 +
  28 + <% button_bar do %>
  29 + <%= button(:add, _('Add a new label'), :action => 'create')%>
  30 + <%= button :back, _('Back to admin panel'), :controller => 'admin_panel' %>
  31 + <% end %>
  32 +</div>
... ...
plugins/comment_classification/views/comment_classification_plugin_myprofile/_status_form.html.erb 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +<%= error_messages_for :status %>
  2 +
  3 +<% form_for :status, @status do |f| %>
  4 + <%= required_fields_message %>
  5 +
  6 + <%= labelled_form_field(_('Status'), f.select(:status_id, @statuses.map{|s|[s.name,s.id]})) %>
  7 + <%= labelled_form_field(_('Reason:'), f.text_area(:reason, :rows => 5)) %>
  8 +
  9 + <% button_bar do %>
  10 + <%= submit_button('save', _('Save') ) %>
  11 + <% end %>
  12 +<% end %>
... ...
plugins/comment_classification/views/comment_classification_plugin_myprofile/add_status.html.erb 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +<h1><%= _('Status for comment') %></h1>
  2 +
  3 +<div id='comment-classification-status-list'>
  4 + <% unless @comment.title.blank? %>
  5 + <div class='comment-title'><%= _("Title: %s") % @comment.title %></div>
  6 + <% end %>
  7 +
  8 + <b><%= _('Body:') %></b>
  9 + <p><%= @comment.body %></p>
  10 +
  11 + <h2> <%= _("History") %> </h2>
  12 +
  13 + <ul>
  14 + <% @comment.comment_classification_plugin_comment_status_users.each do |relation| %>
  15 + <li>
  16 + <%= _("<i>%{user}</i> added the status <i>%{status_name}</i> at <i>%{created_at}</i>.") % { :user => relation.profile.name, :status_name => relation.status.name, :created_at => time_ago_as_sentence(relation.created_at)} %>
  17 + <% unless relation.reason.blank? %>
  18 + <p><%= _("<i>Reason:</i> %s") % relation.reason %></p>
  19 + <% end %>
  20 + </li>
  21 + <% end %>
  22 + </ul>
  23 +
  24 + <h2> <%= _("Add a new status") %> </h2>
  25 +
  26 + <%= render :partial => 'status_form' %>
  27 +</div>
... ...
plugins/comment_classification/views/comment_classification_plugin_myprofile/index.html.erb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h1><%= _('Manage comment classification') %></h1>
  2 +
  3 +List all classifications
... ...
plugins/comment_classification/views/comment_classification_plugin_status/_form.rhtml 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +<%= error_messages_for :status %>
  2 +
  3 +<% form_for :status, @status do |f| %>
  4 + <%= required_fields_message %>
  5 +
  6 + <%= required labelled_form_field(_('Name'), f.text_field(:name)) %>
  7 + <%= labelled_form_field(f.check_box(:enabled) + _('Enable this status?'),'') %>
  8 + <%#= labelled_form_field(f.check_box(:enable_reason) + _('This status allows reason?'),'') %>
  9 +
  10 + <% button_bar do %>
  11 + <%= submit_button('save', _('Save'), :cancel => {:action => 'index'} ) %>
  12 + <% end %>
  13 +<% end %>
... ...
plugins/comment_classification/views/comment_classification_plugin_status/create.rhtml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h2> <%= _("Add a new status") %> </h2>
  2 +
  3 +<%= render :partial => 'form' %>
... ...
plugins/comment_classification/views/comment_classification_plugin_status/edit.rhtml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<h2> <%= _("Editing status %s") % @status.name %> </h2>
  2 +
  3 +<%= render :partial => 'form' %>
... ...
plugins/comment_classification/views/comment_classification_plugin_status/index.rhtml 0 → 100644
... ... @@ -0,0 +1,32 @@
  1 +<h1> <%= _("Manage comments status") %></h1>
  2 +
  3 +<div id='comment-classification-status'>
  4 + <% if @status.empty? %>
  5 + <%= _('(no status registered yet)') %>
  6 + <% else %>
  7 + <table>
  8 + <tr>
  9 + <th><%= _('Status') %></th>
  10 + <th><%= _('Enabled') %></th>
  11 + <th><%= _('Reason enabled?') %></th>
  12 + <th><%= _('Actions') %></th>
  13 + </tr>
  14 + <% @status.each do |st| %>
  15 + <tr>
  16 + <td><%= st.name %></td>
  17 + <td><%= st.enabled %></td>
  18 + <td><%= st.enable_reason %></td>
  19 + <td>
  20 + <%= button_without_text :edit, _('Edit'), {:action => 'edit', :id => st} %>
  21 + <%= button_without_text :delete, _('Remove'), {:action => 'destroy', :id => st}, :confirm => _('Are you sure you want to remove this status?') %>
  22 + </td>
  23 + </tr>
  24 + <% end %>
  25 + </table>
  26 + <% end %>
  27 +
  28 + <% button_bar do %>
  29 + <%= button(:add, _('Add a new status'), :action => 'create')%>
  30 + <%= button :back, _('Back to admin panel'), :controller => 'admin_panel' %>
  31 + <% end %>
  32 +</div>
... ...
plugins/container_block/lib/container_block_plugin.rb
... ... @@ -16,4 +16,8 @@ class ContainerBlockPlugin &lt; Noosfero::Plugin
16 16 true
17 17 end
18 18  
  19 + def js_files
  20 + 'container_block.js'
  21 + end
  22 +
19 23 end
... ...
plugins/container_block/lib/container_block_plugin/container_block.rb
... ... @@ -7,6 +7,16 @@ class ContainerBlockPlugin::ContainerBlock &lt; Block
7 7 settings_items :container_box_id, :type => Integer, :default => nil
8 8 settings_items :children_settings, :type => Hash, :default => {}
9 9  
  10 + validate :no_cyclical_reference, :if => 'container_box_id.present?'
  11 +
  12 + def no_cyclical_reference
  13 + errors.add(:box_id, _('cyclical reference is not allowed.')) if box_id == container_box_id
  14 + end
  15 +
  16 + before_save do |b|
  17 + raise "cyclical reference is not allowed" if b.box_id == b.container_box_id && !b.container_box_id.blank?
  18 + end
  19 +
10 20 def self.description
11 21 _('Container')
12 22 end
... ... @@ -15,6 +25,10 @@ class ContainerBlockPlugin::ContainerBlock &lt; Block
15 25 _('This block acts as a container for another blocks')
16 26 end
17 27  
  28 + def cacheable?
  29 + false
  30 + end
  31 +
18 32 def layout_template
19 33 nil
20 34 end
... ... @@ -24,8 +38,9 @@ class ContainerBlockPlugin::ContainerBlock &lt; Block
24 38 end
25 39  
26 40 def create_box
27   - box = Box.create!(:owner => owner)
28   - settings[:container_box_id] = box.id
  41 + container_box = Box.create!(:owner => owner)
  42 + container_box.update_attribute(:position, nil)
  43 + settings[:container_box_id] = container_box.id
29 44 save!
30 45 end
31 46  
... ...
plugins/container_block/public/container_block.js 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +function enableMoveContainerChildren(container, box) {
  2 + var div = jQuery('#box-'+box+' > .block-outer > .block');
  3 + if(!div.is('.ui-resizable')) {
  4 + div.resizable({
  5 + handles: 'e, w',
  6 + containment: '#block-'+container+' .block-inner-2',
  7 + resize: function( event, ui ) {
  8 + ui.element.height('auto');
  9 + }
  10 + });
  11 + }
  12 +}
  13 +
  14 +function disableMoveContainerChildren(container, box) {
  15 + var div = jQuery('#box-'+box+' > .block-outer > .block');
  16 + if(div.is('.ui-resizable')) {
  17 + div.resizable('destroy');
  18 + }
  19 +}
  20 +
  21 +function containerChildrenWidth(container, box) {
  22 + widths = "";
  23 + jQuery('#box-'+box+' > .block-outer > .block').each(function(i) {
  24 + childId = jQuery(this).attr('id').match(/block-(\d+)/)[1];
  25 + widths+=childId+","+jQuery(this).width()+"|";
  26 + });
  27 + return "widths="+widths;
  28 +}
... ...
plugins/container_block/public/style.css
  1 +#box-organizer .container-block-plugin_container-block > .block-inner-1 > .block-inner-2 > .button-bar {
  2 + height: 22px;
  3 + padding-bottom: 0px;
  4 + width: auto;
  5 +}
  6 +
1 7 #content .boxes .container-block-plugin_container-block .container_block_child, .container-block-plugin_container-block .block-outer {
2 8 display: inline-block;
3 9 vertical-align: top;
... ... @@ -17,14 +23,10 @@
17 23 background-image: url(/designs/icons/default/Tango/16x16/actions/go-previous.png);
18 24 }
19 25  
20   -#content .boxes .container-block-plugin_container-block .block {
21   - outline-offset: -2px;
22   -}
23   -
24 26 #content .boxes .container-block-plugin_container-block .block .ui-resizable-handle {
25 27 width: 10px;
26 28 height: 28px;
27   - z-index: 0;
  29 + z-index: 1000;
28 30 }
29 31  
30 32 #content .boxes .container-block-plugin_container-block .block .ui-resizable-e {
... ... @@ -37,6 +39,21 @@
37 39 background-image: url(/plugins/container_block/images/handle_w.png);
38 40 }
39 41  
40   -.container-block-plugin_container-block .button-bar .icon-resize {
  42 +.container-block-plugin_container-block .container-block-button-bar .icon-resize {
41 43 background-image: url(/designs/icons/default/Tango/16x16/actions/view-fullscreen.png);
42 44 }
  45 +
  46 +#box-organizer .block .container-block-button-bar {
  47 + right: 0px;
  48 + bottom: 0px;
  49 + height: auto;
  50 +}
  51 +
  52 +#box-organizer .container-block-plugin_container-block:hover .block {
  53 + outline: 1px dashed black;
  54 + outline-offset: -1px;
  55 +}
  56 +
  57 +.container-block-plugin_container-block .block-target {
  58 + background: #afd;
  59 +}
... ...
plugins/container_block/test/functional/container_block_home_controller_test.rb
... ... @@ -31,6 +31,13 @@ class HomeControllerTest &lt; ActionController::TestCase
31 31 assert_tag :div, :attributes => { :class => 'block container-block-plugin_container-block' }
32 32 end
33 33  
  34 + should 'display block title' do
  35 + @block.title = "Block Title"
  36 + @block.save!
  37 + get :index
  38 + assert_tag :div, :attributes => { :class => 'block container-block-plugin_container-block' }, :descendant => {:tag => 'h3', :attributes => { :class => "block-title"}, :content => @block.title }
  39 + end
  40 +
34 41 should 'display container children' do
35 42 c1 = RawHTMLBlock.create!(:box_id => @block.container_box.id, :html => 'child1 content')
36 43 c2 = RawHTMLBlock.create!(:box_id => @block.container_box.id, :html => 'child2 content')
... ...
plugins/container_block/test/unit/block_test.rb
... ... @@ -1,31 +0,0 @@
1   -require 'test_helper'
2   -
3   -class BlockTest < ActiveSupport::TestCase
4   -
5   - def setup
6   - @environment = fast_create(Environment)
7   - @box = Box.create!(:owner => @environment)
8   - @container = ContainerBlockPlugin::ContainerBlock.create!(:box_id => @box.id)
9   - end
10   -
11   - should 'return environment box if block owner is not a ContainerBlock' do
12   - block = Block.create!(:box_id => @box.id)
13   - assert_equal @box, block.box
14   - end
15   -
16   - should 'return container box if block owner is a ContainerBlock' do
17   - block = Block.create!(:box_id => @container.container_box.id)
18   - assert_equal @container.container_box, block.box
19   - end
20   -
21   - should 'return block owner if block onwer is not a ContainerBlock' do
22   - block = Block.create!(:box_id => @box.id)
23   - assert_equal @environment, block.owner
24   - end
25   -
26   - should 'return environment as owner if block onwer is a ContainerBlock' do
27   - block = Block.create!(:box_id => @container.container_box.id)
28   - assert_equal @environment, block.owner
29   - end
30   -
31   -end
plugins/container_block/test/unit/container_block_plugin/container_block_test.rb
... ... @@ -20,6 +20,11 @@ class ContainerBlockPlugin::ContainerBlockTest &lt; ActiveSupport::TestCase
20 20 assert @block.container_box_id
21 21 end
22 22  
  23 + should 'created box should have nil as position' do
  24 + @block.save!
  25 + assert_equal nil, @block.container_box.position
  26 + end
  27 +
23 28 should 'return created box' do
24 29 @block.save!
25 30 assert @block.container_box
... ... @@ -89,4 +94,27 @@ class ContainerBlockPlugin::ContainerBlockTest &lt; ActiveSupport::TestCase
89 94 end
90 95 end
91 96  
  97 + should 'not mess up with boxes positions when destroyed' do
  98 + env = fast_create(Environment)
  99 + box1 = fast_create(Box, :owner_id => env.id, :owner_type => 'Environment', :position => 1)
  100 + box2 = fast_create(Box, :owner_id => env.id, :owner_type => 'Environment', :position => 2)
  101 + box3 = fast_create(Box, :owner_id => env.id, :owner_type => 'Environment', :position => 3)
  102 + block = ContainerBlockPlugin::ContainerBlock.create!(:box => box1)
  103 + block.destroy
  104 + assert_equal [1, 2, 3], [box1.reload.position, box2.reload.position, box3.reload.position]
  105 + end
  106 +
  107 + should 'be able to change box' do
  108 + @block.save!
  109 + @block.box = Box.new(:owner => Environment.default)
  110 + @block.save!
  111 + end
  112 +
  113 + should 'not able to change box to be the same as container_box' do
  114 + @block.save!
  115 + @block.box = @block.container_box
  116 + @block.save
  117 + assert @block.errors.invalid?(:box_id)
  118 + end
  119 +
92 120 end
... ...
plugins/container_block/test/unit/environment_test.rb
... ... @@ -1,32 +0,0 @@
1   -require 'test_helper'
2   -
3   -class EnvironmentTest < ActiveSupport::TestCase
4   -
5   - def setup
6   - @environment = fast_create(Environment)
7   -
8   - @box = create(Box, :owner => @environment)
9   - @block = create(Block, :box => @box)
10   -
11   - @container = create(ContainerBlockPlugin::ContainerBlock, :box => @box)
12   - end
13   -
14   - should 'return blocks as usual' do
15   - assert_equal [@block, @container], @environment.blocks
16   - end
17   -
18   - should 'return blocks with container children' do
19   - child = Block.create!(:box_id => @container.container_box.id)
20   - assert_equal [@block, @container, child], @environment.blocks
21   - end
22   -
23   - should 'return block with id at find method' do
24   - assert_equal @block, @environment.blocks.find(@block.id)
25   - end
26   -
27   - should 'return child block with id at find method' do
28   - child = Block.create!(:box_id => @container.container_box.id)
29   - assert_equal child, @environment.blocks.find(child.id)
30   - end
31   -
32   -end
plugins/container_block/test/unit/profile_test.rb
... ... @@ -1,32 +0,0 @@
1   -require 'test_helper'
2   -
3   -class ProfileTest < ActiveSupport::TestCase
4   -
5   - def setup
6   - @profile = fast_create(Profile)
7   -
8   - @box = create(Box, :owner => @profile)
9   - @block = create(Block, :box => @box)
10   -
11   - @container = create(ContainerBlockPlugin::ContainerBlock, :box => @box)
12   - end
13   -
14   - should 'return blocks as usual' do
15   - assert_equal [@block, @container], @profile.blocks
16   - end
17   -
18   - should 'return blocks with container children' do
19   - child = Block.create!(:box_id => @container.container_box.id)
20   - assert_equal [@block, @container, child], @profile.blocks
21   - end
22   -
23   - should 'return block with id at find method' do
24   - assert_equal @block, @profile.blocks.find(@block.id)
25   - end
26   -
27   - should 'return child block with id at find method' do
28   - child = Block.create!(:box_id => @container.container_box.id)
29   - assert_equal child, @profile.blocks.find(child.id)
30   - end
31   -
32   -end
plugins/container_block/views/blocks/container.html.erb
1 1 <% edit_mode = controller.send(:boxes_editor?) && controller.send(:uses_design_blocks?) %>
2 2 <% box_decorator = edit_mode ? self : BoxesHelper::DontMoveBlocks %>
3 3  
  4 +<%= block_title(block.title) %>
4 5  
5 6 <div class="box" id="box-<%= block.container_box.id %>">
6 7 <%= display_box_content(block.container_box, nil) %>
... ... @@ -15,46 +16,23 @@
15 16 </style>
16 17  
17 18 <% if edit_mode %>
18   - <div class="button-bar">
19   - <a href="#" onclick="toggleMoveContainerChildren(<%= block.id %>, <%= block.container_box.id %>); return false;" class="button icon-resize" title=<%= _('Resize blocks').to_json %>></a>
20   - <%= link_to_remote '', :url => { :controller => controller.boxes_holder.kind_of?(Environment) ? 'container_block_plugin_admin' : 'container_block_plugin_myprofile', :action => 'saveWidths', :id => block.id },
  19 +
  20 + <script>
  21 + jQuery("#block-<%= block.id %>").hover(
  22 + function() {
  23 + enableMoveContainerChildren(<%= block.id %>, <%= block.container_box.id %>);
  24 + }, function() {
  25 + disableMoveContainerChildren(<%= block.id %>, <%= block.container_box.id %>);
  26 + }
  27 + );
  28 + </script>
  29 +
  30 + <div class="container-block-button-bar button-bar">
  31 + <%= link_to_remote '', :url => { :controller => @controller.boxes_holder.kind_of?(Environment) ? 'container_block_plugin_admin' : 'container_block_plugin_myprofile', :action => 'saveWidths', :id => block.id },
21 32 :with => "containerChildrenWidth(#{block.id}, #{block.container_box.id})",
22 33 :html => {:class => "button icon-save container_block_save", :id => "container_block_save_#{block.id}", :title => _('Save') },
23 34 :loading => "open_loading(DEFAULT_LOADING_MESSAGE);",
24 35 :loaded => "close_loading();",
25 36 :complete => "display_notice(request.responseText);"%>
26 37 </div>
27   -
28   - <script>
29   - function toggleMoveContainerChildren(container, box) {
30   - var div = jQuery('#box-'+box+' > .block-outer > .block');
31   - var targetDiv = jQuery('#box-'+box+' .block-outer .block-target');
32   - if(div.is('.ui-resizable')) {
33   - targetDiv.show();
34   - div.find("a").die("click");
35   - div.resizable('destroy');
36   - } else {
37   - targetDiv.hide();
38   - div.find("a").live("click", function(e) {
39   - e.preventDefault();
40   - });
41   - div.resizable({
42   - handles: 'e, w',
43   - containment: '#block-'+container+' .block-inner-2',
44   - resize: function( event, ui ) {
45   - ui.element.height('auto');
46   - }
47   - });
48   - }
49   - }
50   -
51   - function containerChildrenWidth(container, box) {
52   - widths = "";
53   - jQuery('#box-'+box+' > .block-outer > .block').each(function(i) {
54   - childId = jQuery(this).attr('id').match(/block-(\d+)/)[1];
55   - widths+=childId+","+jQuery(this).width()+"|";
56   - });
57   - return "widths="+widths;
58   - }
59   - </script>
60 38 <% end %>
... ...
plugins/require_auth_to_comment/public/hide_comment_form.js
1 1 (function($) {
2 2 $(window).bind('userDataLoaded', function(event, data) {
3   - if (data.login || $('meta[name=profile.allow_unauthenticated_comments]').length > 0) {
  3 + if (data.login || $('meta[name="profile.allow_unauthenticated_comments"]').length > 0) {
4 4 $('.post-comment-button').livequery(function() {
5 5 $(this).show();
6 6 });
... ... @@ -8,7 +8,7 @@
8 8 $(this).show();
9 9 });
10 10 $('.comment-footer').livequery(function() {
11   - $(this).show();
  11 + $(this).show();
12 12 });
13 13 }
14 14 });
... ...
public/stylesheets/application.css
  1 +/* browser fixes */
  2 +
  3 +img:-moz-broken {
  4 + -moz-force-broken-image-icon:1;
  5 +}
  6 +
  7 +/* general styles */
  8 +
1 9 body {
2 10 padding: 0px;
3 11 margin: 0px;
... ... @@ -1727,8 +1735,8 @@ a.button.disabled, input.disabled {
1727 1735 display: none;
1728 1736 }
1729 1737  
1730   -#box-organizer .block:focus .button-bar,
1731   -#box-organizer .block:hover .button-bar {
  1738 +#box-organizer .block-outer:focus .button-bar,
  1739 +#box-organizer .block-outer:hover .button-bar {
1732 1740 display: block;
1733 1741 }
1734 1742  
... ... @@ -1834,8 +1842,13 @@ a.button.disabled, input.disabled {
1834 1842 }
1835 1843 /* ==> blocks/link-list-block.css <<= */
1836 1844  
  1845 +#edit-link-list-block {
  1846 + width: 820px;
  1847 +}
  1848 +
1837 1849 #edit-link-list-block table {
1838   - width: 100%;
  1850 + width: auto;
  1851 + margin-bottom: 10px;
1839 1852 }
1840 1853 #edit-link-list-block table .cel-address {
1841 1854 width: 220px;
... ...
script/install-dependencies/debian-squeeze.sh
... ... @@ -5,7 +5,7 @@ run sudo apt-get -y install $runtime_dependencies
5 5 sudo apt-get -y install iceweasel || sudo apt-get -y install firefox
6 6  
7 7 # needed for development
8   -run sudo apt-get -y install libtidy-ruby libhpricot-ruby libnokogiri-ruby libmocha-ruby imagemagick po4a xvfb libxml2-dev libxslt-dev postgresql openjdk-6-jre
  8 +run sudo apt-get -y install libtidy-ruby libhpricot-ruby libmocha-ruby imagemagick po4a xvfb libxml2-dev libxslt-dev postgresql openjdk-6-jre
9 9 gem which bundler >/dev/null 2>&1 || gem_install bundler
10 10 setup_rubygems_path
11 11 run bundle install
... ...
test/functional/profile_themes_controller_test.rb
... ... @@ -310,4 +310,23 @@ class ProfileThemesControllerTest &lt; ActionController::TestCase
310 310 assert_no_tag :content => "Select theme"
311 311 end
312 312  
  313 + should 'not duplicate themes that are included by the user and by the environment' do
  314 + t1 = Theme.create('theme1')
  315 + t2 = Theme.create('theme2')
  316 + Environment.any_instance.stubs('themes').returns([t1,t2])
  317 + Theme.stubs(:approved_themes).returns([t2])
  318 +
  319 + get :index, :profile => "testinguser"
  320 + assert_equivalent [t1, t2], assigns(:themes)
  321 + end
  322 +
  323 + should 'sort themes by name' do
  324 + t1 = Theme.create('bill-theme')
  325 + t2 = Theme.create('ana-theme')
  326 + Theme.stubs(:approved_themes).returns([t1,t2])
  327 +
  328 + get :index, :profile => "testinguser"
  329 + assert_equal [t2, t1], assigns(:themes)
  330 + end
  331 +
313 332 end
... ...
test/functional/users_controller_test.rb
... ... @@ -107,6 +107,21 @@ class UsersControllerTest &lt; ActionController::TestCase
107 107 assert_equal false, u.activated?
108 108 end
109 109  
  110 + should 'order users by name' do
  111 + create_user('jeremy')
  112 + create_user('bill')
  113 + create_user('ana')
  114 + create_user('creed')
  115 + get :index
  116 +
  117 + assert_order ['ana', 'bill', 'creed', 'jeremy'], assigns(:collection).map(&:name)
  118 + end
  119 +
  120 + should 'set filter to all_users by default' do
  121 + get :index
  122 + assert_equal 'all_users', assigns(:filter)
  123 + end
  124 +
110 125 should 'response as XML to export users' do
111 126 get :download, :format => 'xml'
112 127 assert_equal 'text/xml', @response.content_type
... ...
test/test_helper.rb
... ... @@ -178,6 +178,19 @@ class ActiveSupport::TestCase
178 178 assert !tag, "expected no tag #{options.inspect}, but tag found in #{text.inspect}"
179 179 end
180 180  
  181 + def assert_order(reference, original)
  182 + original.each do |value|
  183 + if reference.include?(value)
  184 + if reference.first == value
  185 + reference.shift
  186 + else
  187 + assert false, "'#{value}' was found before it should be on: #{original.inspect}"
  188 + end
  189 + end
  190 + end
  191 + assert reference.blank?, "The following elements are not in the collection: #{reference.inspect}"
  192 + end
  193 +
181 194 # For models that render views (blocks, articles, ...)
182 195 def render(*args)
183 196 view_paths = @explicit_view_paths || ActionController::Base.view_paths
... ...
test/unit/array_core_ext_test.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +# tests for Array core extension. See lib/noosfero/core_ext/array.rb
  4 +class StringCoreExtTest < ActiveSupport::TestCase
  5 +
  6 + should 'allow uniq by a block' do
  7 + array = [0,1,2,3,4,5,6]
  8 + assert_equal [0,1], array.uniq_by {|number| number%2 }
  9 + end
  10 +
  11 +end
... ...
test/unit/box_test.rb
... ... @@ -140,4 +140,11 @@ class BoxTest &lt; ActiveSupport::TestCase
140 140 assert !blocks.include?('box-test_plugin-block')
141 141 end
142 142  
  143 + should 'list only boxes with a postion greater than zero' do
  144 + profile = fast_create(Profile)
  145 + box = fast_create(Box, :owner_id => profile.id, :owner_type => 'Profile', :position => 0)
  146 + box2 = fast_create(Box, :owner_id => profile.id, :owner_type => 'Profile', :position => 1)
  147 + assert_equal [box2], profile.boxes.with_position
  148 + end
  149 +
143 150 end
... ...
test/unit/boxes_helper_test.rb
... ... @@ -12,7 +12,8 @@ class BoxesHelperTest &lt; ActionView::TestCase
12 12  
13 13 should 'include profile-specific header' do
14 14 holder = mock
15   - holder.stubs(:boxes).returns([])
  15 + holder.stubs(:boxes).returns(boxes = [])
  16 + boxes.stubs(:with_position).returns([])
16 17 holder.stubs(:boxes_limit).returns(0)
17 18 holder.stubs(:custom_header_expanded).returns('my custom header')
18 19 @controller.stubs(:boxes_holder).returns(holder)
... ... @@ -22,7 +23,8 @@ class BoxesHelperTest &lt; ActionView::TestCase
22 23  
23 24 should 'include profile-specific footer' do
24 25 holder = mock
25   - holder.stubs(:boxes).returns([])
  26 + holder.stubs(:boxes).returns(boxes = [])
  27 + boxes.stubs(:with_position).returns([])
26 28 holder.stubs(:boxes_limit).returns(0)
27 29 holder.stubs(:custom_footer_expanded).returns('my custom footer')
28 30 @controller.stubs(:boxes_holder).returns(holder)
... ...
test/unit/comment_test.rb
... ... @@ -285,6 +285,37 @@ class CommentTest &lt; ActiveSupport::TestCase
285 285 assert_equal [c1,c3], c.reload.children
286 286 end
287 287  
  288 + should "return activities comments as a thread" do
  289 + person = fast_create(Person)
  290 + a = TextileArticle.create!(:profile => person, :name => 'My article', :body => 'Article body')
  291 + c0 = Comment.create!(:source => a, :body => 'My comment', :author => person)
  292 + c1 = Comment.create!(:reply_of_id => c0.id, :source => a, :body => 'bla', :author => person)
  293 + c2 = Comment.create!(:reply_of_id => c1.id, :source => a, :body => 'bla', :author => person)
  294 + c3 = Comment.create!(:reply_of_id => c0.id, :source => a, :body => 'bla', :author => person)
  295 + c4 = Comment.create!(:source => a, :body => 'My comment', :author => person)
  296 + result = a.activity.comments
  297 + assert_equal c0, result[0]
  298 + assert_equal [c1, c3], result[0].replies
  299 + assert_equal [c2], result[0].replies[0].replies
  300 + assert_equal c4, result[1]
  301 + assert result[1].replies.empty?
  302 + end
  303 +
  304 + should "return activities comments when some comment on thread is spam and not display its replies" do
  305 + person = fast_create(Person)
  306 + a = TextileArticle.create!(:profile => person, :name => 'My article', :body => 'Article body')
  307 + c0 = Comment.create(:source => a, :body => 'Root comment', :author => person)
  308 + c1 = Comment.create(:reply_of_id => c0.id, :source => a, :body => 'c1', :author => person)
  309 + c2 = Comment.create(:source => a, :body => 'c2', :author => person)
  310 + spam = Comment.create(:spam => true, :reply_of_id => c2.id, :source => a, :body => 'spam', :author => person)
  311 + spam_reply = Comment.create(:reply_of_id => spam.id, :source => a, :body => 'spam reply', :author => person)
  312 + result = a.activity.comments
  313 + assert_equal c0, result[0]
  314 + assert_equal [c1], result[0].replies.without_spam
  315 + assert_equal c2, result[1]
  316 + assert_equal [], result[1].replies.without_spam
  317 + end
  318 +
288 319 should 'provide author url for authenticated user' do
289 320 author = Person.new
290 321 author.expects(:url).returns('http://blabla.net/author')
... ... @@ -389,6 +420,7 @@ class CommentTest &lt; ActiveSupport::TestCase
389 420 end
390 421  
391 422 should 'be able to select non-spam comments' do
  423 + Comment.destroy_all
392 424 c1 = fast_create(Comment)
393 425 c2 = fast_create(Comment, :spam => false)
394 426 c3 = fast_create(Comment, :spam => true)
... ... @@ -660,6 +692,7 @@ class CommentTest &lt; ActiveSupport::TestCase
660 692 end
661 693  
662 694 should 'be able to select non-reply comments' do
  695 + Comment.destroy_all
663 696 c1 = fast_create(Comment)
664 697 c2 = fast_create(Comment, :reply_of_id => c1.id)
665 698 c3 = fast_create(Comment, :reply_of_id => c2.id)
... ...
test/unit/environment_test.rb
... ... @@ -783,6 +783,18 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
783 783 assert role2.valid?
784 784 end
785 785  
  786 + should 'destroy roles when its environment is destroyed' do
  787 + e1 = fast_create(Environment)
  788 + role1 = Role.create!(:name => 'test_role', :environment => e1, :key => 'a_member')
  789 + e2 = fast_create(Environment)
  790 + role2 = Role.create!(:name => 'test_role', :environment => e2, :key => 'a_member')
  791 +
  792 + e2.destroy
  793 +
  794 + assert_nothing_raised {Role.find(role1.id)}
  795 + assert_raise(ActiveRecord::RecordNotFound) {Role.find(role2.id)}
  796 + end
  797 +
786 798 should 'have a help_message_to_add_enterprise attribute' do
787 799 env = Environment.new
788 800  
... ...
test/unit/link_list_block_test.rb
... ... @@ -88,4 +88,9 @@ class LinkListBlockTest &lt; ActiveSupport::TestCase
88 88 assert_equivalent LinkListBlock::TARGET_OPTIONS.map {|t|t[1]}, ['_self', '_blank', '_new']
89 89 end
90 90  
  91 + should 'link with title' do
  92 + l = LinkListBlock.new
  93 + assert_match /title="mytitle"/, l.link_html({:name => 'mylink', :address => '/myaddress', :title => 'mytitle'})
  94 + end
  95 +
91 96 end
... ...
test/unit/macro_test.rb
... ... @@ -15,7 +15,7 @@ class MacroTest &lt; ActiveSupport::TestCase
15 15  
16 16 def setup
17 17 @macro = Plugin1::Macro.new
18   - @macro_element = Nokogiri::HTML(MACRO).css('.macro').first
  18 + @macro_element = Hpricot(MACRO).search('.macro').first
19 19 end
20 20  
21 21 attr_reader :macro, :macro_element
... ...
test/unit/profile_test.rb
... ... @@ -1462,7 +1462,7 @@ class ProfileTest &lt; ActiveSupport::TestCase
1462 1462 should 'list events by month' do
1463 1463 profile = fast_create(Profile)
1464 1464  
1465   - today = Date.today
  1465 + today = Date.new(2014, 03, 2)
1466 1466 yesterday_event = Event.new(:name => 'Joao Birthday', :start_date => today - 1.day)
1467 1467 today_event = Event.new(:name => 'Ze Birthday', :start_date => today)
1468 1468 tomorrow_event = Event.new(:name => 'Mane Birthday', :start_date => today + 1.day)
... ... @@ -1693,6 +1693,28 @@ class ProfileTest &lt; ActiveSupport::TestCase
1693 1693 assert_equal 1, community.members_count
1694 1694 end
1695 1695  
  1696 + should 'order members by name alphabetically considering special characters' do
  1697 + community = fast_create(Community)
  1698 +
  1699 + community.add_member(create_user('José').person)
  1700 + community.add_member(create_user('João').person)
  1701 + community.add_member(create_user('Mariana').person)
  1702 + members = community.members_by_name
  1703 +
  1704 + assert_equal ["João", "José", "Mariana"], members.map(&:name)
  1705 + end
  1706 +
  1707 + should 'order members by name alphabetically considering upper and lower cases' do
  1708 + community = fast_create(Community)
  1709 +
  1710 + community.add_member(create_user('mariana').person)
  1711 + community.add_member(create_user('João').person)
  1712 + community.add_member(create_user('guest').person)
  1713 + members = community.members_by_name
  1714 +
  1715 + assert_equal ["guest", "João", "mariana"], members.map(&:name)
  1716 + end
  1717 +
1696 1718 should 'know if url is the profile homepage' do
1697 1719 profile = fast_create(Profile)
1698 1720  
... ...
vendor/plugins/action_tracker_has_comments/init.rb
... ... @@ -11,5 +11,6 @@ Rails.configuration.to_prepare do
11 11 type, id = (self.target_type == 'Article' ? ['Article', self.target_id] : [self.class.to_s, self.id])
12 12 "source_type = '#{type}' AND source_id = '#{id}' AND spam IS NOT TRUE AND reply_of_id IS NULL"
13 13 end
  14 +
14 15 end
15 16 end
... ...