Commit 77d6fa0d4dbace32bc2ab87ac88e8bb657859c25
Committed by
Antonio Terceiro
1 parent
445b9833
Exists in
master
and in
27 other branches
Add Profile Members Headlines plugin
Showing
15 changed files
with
387 additions
and
2 deletions
Show diff stats
app/models/person.rb
@@ -21,6 +21,12 @@ class Person < Profile | @@ -21,6 +21,12 @@ class Person < Profile | ||
21 | { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => [conditions] } | 21 | { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => [conditions] } |
22 | } | 22 | } |
23 | 23 | ||
24 | + scope :by_role, lambda { |roles| | ||
25 | + roles = [roles] unless roles.kind_of?(Array) | ||
26 | + { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.role_id IN (?)', | ||
27 | +roles] } | ||
28 | + } | ||
29 | + | ||
24 | def has_permission_with_plugins?(permission, profile) | 30 | def has_permission_with_plugins?(permission, profile) |
25 | permissions = [has_permission_without_plugins?(permission, profile)] | 31 | permissions = [has_permission_without_plugins?(permission, profile)] |
26 | permissions += plugins.map do |plugin| | 32 | permissions += plugins.map do |plugin| |
app/models/profile.rb
@@ -108,8 +108,8 @@ class Profile < ActiveRecord::Base | @@ -108,8 +108,8 @@ class Profile < ActiveRecord::Base | ||
108 | alias_method_chain :count, :distinct | 108 | alias_method_chain :count, :distinct |
109 | end | 109 | end |
110 | 110 | ||
111 | - def members_by_role(role) | ||
112 | - Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', role.id]) | 111 | + def members_by_role(roles) |
112 | + Person.members_of(self).by_role(roles) | ||
113 | end | 113 | end |
114 | 114 | ||
115 | acts_as_having_boxes | 115 | acts_as_having_boxes |
@@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
1 | +README - Profile Members Headlines (ProfileMembersHeadlines Plugin) | ||
2 | +=================================================================== | ||
3 | + | ||
4 | +ProfileMembersHeadlines is a plugin that allow users to add a block that | ||
5 | +displays the most recent post from members with the defined roles. | ||
6 | + | ||
7 | +The user also can configure the limit of headlines. | ||
8 | + | ||
9 | +This block will be available for all layout columns of communities and enterprises. | ||
10 | + | ||
11 | +INSTALL | ||
12 | +======= | ||
13 | + | ||
14 | +Enable Plugin | ||
15 | +------------- | ||
16 | + | ||
17 | +Also, you need to enable ProfileMembersHeadlines Plugin at you Noosfero: | ||
18 | + | ||
19 | +cd <your_noosfero_dir> | ||
20 | +./script/noosfero-plugins enable profile_members_headlines | ||
21 | + | ||
22 | +Active Plugin | ||
23 | +------------- | ||
24 | + | ||
25 | +As a Noosfero administrator user, go to administrator panel: | ||
26 | + | ||
27 | +- Click on "Enable/disable plugins" option | ||
28 | +- Click on "Profile Members Headlines Plugin" checkbox | ||
29 | + | ||
30 | +Running DisplayContent tests | ||
31 | +-------------------- | ||
32 | + | ||
33 | +$ rake test:noosfero_plugins:profile_members_headlines_plugin | ||
34 | + | ||
35 | +LICENSE | ||
36 | +======= | ||
37 | + | ||
38 | +See Noosfero license. |
plugins/profile_members_headlines/lib/profile_members_headlines_block.rb
0 → 100644
@@ -0,0 +1,43 @@ | @@ -0,0 +1,43 @@ | ||
1 | +class ProfileMembersHeadlinesBlock < Block | ||
2 | + | ||
3 | + settings_items :interval, :type => 'integer', :default => 10 | ||
4 | + settings_items :limit, :type => 'integer', :default => 10 | ||
5 | + settings_items :navigation, :type => 'boolean', :default => true | ||
6 | + settings_items :filtered_roles, :type => Array, :default => [] | ||
7 | + | ||
8 | + attr_accessible :interval, :limit, :navigation, :filtered_roles | ||
9 | + | ||
10 | + def self.description | ||
11 | + _('Display headlines from members of a community') | ||
12 | + end | ||
13 | + | ||
14 | + def help | ||
15 | + _('This block displays one post from members of a community.') | ||
16 | + end | ||
17 | + | ||
18 | + include Noosfero::Plugin::HotSpot | ||
19 | + | ||
20 | + def default_title | ||
21 | + _('Profile members headlines') | ||
22 | + end | ||
23 | + | ||
24 | + def filtered_roles=(array) | ||
25 | + self.settings[:filtered_roles] = array.map(&:to_i).select { |r| !r.to_i.zero? } | ||
26 | + end | ||
27 | + | ||
28 | + def authors_list | ||
29 | + result = owner.members_by_role(filtered_roles).public.includes([:image,:domains,:preferred_domain,:environment]).order('updated_at DESC') | ||
30 | + | ||
31 | + result.all(:limit => limit * 5).select { |p| p.has_headline? | ||
32 | +}.slice(0..limit-1) | ||
33 | + end | ||
34 | + | ||
35 | + def content(args={}) | ||
36 | + block = self | ||
37 | + members = authors_list | ||
38 | + proc do | ||
39 | + render :file => 'blocks/headlines', :locals => { :block => block, :members => members } | ||
40 | + end | ||
41 | + end | ||
42 | + | ||
43 | +end |
plugins/profile_members_headlines/lib/profile_members_headlines_plugin.rb
0 → 100644
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +require_dependency File.dirname(__FILE__) + '/profile_members_headlines_block' | ||
2 | +require 'ext/person' | ||
3 | + | ||
4 | +class ProfileMembersHeadlinesPlugin < Noosfero::Plugin | ||
5 | + | ||
6 | + def self.plugin_name | ||
7 | + "Profile Members Headlines Plugin" | ||
8 | + end | ||
9 | + | ||
10 | + def self.plugin_description | ||
11 | + _("A plugin that adds a block where you can display posts from members.") | ||
12 | + end | ||
13 | + | ||
14 | + def self.extra_blocks | ||
15 | + { ProfileMembersHeadlinesBlock => { :type => [Community], :position => | ||
16 | +['1'] }} | ||
17 | + end | ||
18 | + | ||
19 | + def stylesheet? | ||
20 | + true | ||
21 | + end | ||
22 | + | ||
23 | +end |
379 Bytes
@@ -0,0 +1,59 @@ | @@ -0,0 +1,59 @@ | ||
1 | +.profile-members-headlines-block { | ||
2 | + width: 100%; | ||
3 | +} | ||
4 | + | ||
5 | +.profile-members-headlines-block .headlines-container { | ||
6 | + height: 180px; | ||
7 | +} | ||
8 | + | ||
9 | +.profile-members-headlines-block .headlines-container .author, | ||
10 | +.profile-members-headlines-block .headlines-container .post { | ||
11 | + vertical-align: top; | ||
12 | + min-width: 150px; | ||
13 | +} | ||
14 | + | ||
15 | +.profile-members-headlines-block .headlines-container .author { | ||
16 | + position: absolute; | ||
17 | +} | ||
18 | + | ||
19 | +.profile-members-headlines-block .headlines-container .author p { | ||
20 | + text-align: center; | ||
21 | +} | ||
22 | + | ||
23 | +.profile-members-headlines-block .headlines-container .post { | ||
24 | + margin-left: 160px; | ||
25 | +} | ||
26 | + | ||
27 | +.profile-members-headlines-block #content .headlines-container h4 { | ||
28 | + margin-top: 15px; | ||
29 | + font-weight: bold; | ||
30 | +} | ||
31 | + | ||
32 | +.profile-members-headlines-block .headlines-container a { | ||
33 | + text-decoration: none; | ||
34 | +} | ||
35 | + | ||
36 | +.profile-members-headlines-block .headlines-container a:hover { | ||
37 | + text-decoration: underline; | ||
38 | +} | ||
39 | + | ||
40 | +.profile-members-headlines-block .headlines-container .post .date { | ||
41 | + margin-top: 15px; | ||
42 | +} | ||
43 | + | ||
44 | +.profile-members-headlines-block .headlines-block-pager { | ||
45 | + text-align: center; | ||
46 | +} | ||
47 | + | ||
48 | +.profile-members-headlines-block .headlines-block-pager a { | ||
49 | + padding: 0px 5px; | ||
50 | +} | ||
51 | + | ||
52 | +.profile-members-headlines-block .headlines-block-pager a.activeSlide { | ||
53 | + text-decoration: none; | ||
54 | +} | ||
55 | + | ||
56 | +.profile-members-headlines-block .headlines-block-pager a.activeSlide:visited, | ||
57 | +.profile-members-headlines-block .headlines-block-pager a.activeSlide { | ||
58 | + color: #000; | ||
59 | +} |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +require File.dirname(__FILE__) + '/../../../test/test_helper' |
plugins/profile_members_headlines/test/unit/profile_members_headlines_block_test.rb
0 → 100644
@@ -0,0 +1,81 @@ | @@ -0,0 +1,81 @@ | ||
1 | +require 'test_helper' | ||
2 | + | ||
3 | +class ProfileMembersHeadlinesBlockTest < ActiveSupport::TestCase | ||
4 | + | ||
5 | + include Noosfero::Plugin::HotSpot | ||
6 | + | ||
7 | + def setup | ||
8 | + @environment = fast_create(Environment) | ||
9 | + @environment.enable_plugin(ProfileMembersHeadlinesPlugin) | ||
10 | + | ||
11 | + @member1 = fast_create(Person) | ||
12 | + @member2 = fast_create(Person) | ||
13 | + @community = fast_create(Community) | ||
14 | + community.add_member member1 | ||
15 | + community.add_member member2 | ||
16 | + end | ||
17 | + attr_accessor :environment, :community, :member1, :member2 | ||
18 | + | ||
19 | + should 'inherit from Block' do | ||
20 | + assert_kind_of Block, ProfileMembersHeadlinesBlock.new | ||
21 | + end | ||
22 | + | ||
23 | + should 'describe itself' do | ||
24 | + assert_not_equal Block.description, ProfileMembersHeadlinesBlock.description | ||
25 | + end | ||
26 | + | ||
27 | + should 'provide a default title' do | ||
28 | + assert_not_equal Block.new.default_title, ProfileMembersHeadlinesBlock.new.default_title | ||
29 | + end | ||
30 | + | ||
31 | + should 'not have authors if they have no blog' do | ||
32 | + block = ProfileMembersHeadlinesBlock.create | ||
33 | + block.stubs(:owner).returns(community) | ||
34 | + | ||
35 | + self.expects(:render).with(:file => 'blocks/headlines', :locals => { :block => block, :members => []}).returns('file-without-authors-and-headlines') | ||
36 | + assert_equal 'file-without-authors-and-headlines', instance_eval(&block.content) | ||
37 | + end | ||
38 | + | ||
39 | + should 'display headlines file' do | ||
40 | + block = ProfileMembersHeadlinesBlock.create | ||
41 | + block.stubs(:owner).returns(community) | ||
42 | + blog = fast_create(Blog, :profile_id => member1.id) | ||
43 | + post = fast_create(TinyMceArticle, :name => 'headlines', :profile_id => member1.id, :parent_id => blog.id) | ||
44 | + self.expects(:render).with(:file => 'blocks/headlines', :locals => { :block => block, :members => []}).returns('file-with-authors-and-headlines') | ||
45 | + assert_equal 'file-with-authors-and-headlines', instance_eval(&block.content) | ||
46 | + end | ||
47 | + | ||
48 | + should 'select only authors with articles and selected roles to display' do | ||
49 | + role = Role.create!(:name => 'role1') | ||
50 | + community.affiliate(member1, role) | ||
51 | + block = ProfileMembersHeadlinesBlock.new(:limit => 1, :filtered_roles => [role.id]) | ||
52 | + block.expects(:owner).returns(community) | ||
53 | + blog = fast_create(Blog, :profile_id => member1.id) | ||
54 | + post = fast_create(TinyMceArticle, :name => 'headlines', :profile_id => member1.id, :parent_id => blog.id) | ||
55 | + assert_equal [member1], block.authors_list | ||
56 | + end | ||
57 | + | ||
58 | + should 'not select private authors to display' do | ||
59 | + block = ProfileMembersHeadlinesBlock.new(:limit => 1) | ||
60 | + block.expects(:owner).returns(community) | ||
61 | + private_author = fast_create(Person, :public_profile => false) | ||
62 | + blog = fast_create(Blog, :profile_id => private_author.id) | ||
63 | + post = fast_create(TinyMceArticle, :name => 'headlines', :profile_id => private_author.id, :parent_id => blog.id) | ||
64 | + assert_equal [], block.authors_list | ||
65 | + end | ||
66 | + | ||
67 | + should 'filter authors by roles to display' do | ||
68 | + role = Role.create!(:name => 'role1') | ||
69 | + author = fast_create(Person) | ||
70 | + community.affiliate(author, role) | ||
71 | + | ||
72 | + block = ProfileMembersHeadlinesBlock.new(:limit => 3, :filtered_roles => | ||
73 | +[role.id]) | ||
74 | + block.stubs(:owner).returns(community) | ||
75 | + community.members.each do |member| | ||
76 | + blog = fast_create(Blog, :profile_id => member.id) | ||
77 | + post = fast_create(TinyMceArticle, :name => 'headlines', :profile_id => member.id, :parent_id => blog.id) | ||
78 | + end | ||
79 | + assert_equal [author], block.authors_list | ||
80 | + end | ||
81 | +end |
plugins/profile_members_headlines/test/unit/profile_members_headlines_plugin_test.rb
0 → 100644
@@ -0,0 +1,41 @@ | @@ -0,0 +1,41 @@ | ||
1 | +require 'test_helper' | ||
2 | + | ||
3 | +class ProfileMembersHeadlinesPluginTest < ActiveSupport::TestCase | ||
4 | + | ||
5 | + include Noosfero::Plugin::HotSpot | ||
6 | + | ||
7 | + def setup | ||
8 | + @environment = fast_create(Environment) | ||
9 | + @environment.enable_plugin(ProfileMembersHeadlinesPlugin) | ||
10 | + end | ||
11 | + attr_accessor :environment | ||
12 | + | ||
13 | + should 'has a name' do | ||
14 | + assert_not_equal Noosfero::Plugin.plugin_name, ProfileMembersHeadlinesPlugin.plugin_name | ||
15 | + end | ||
16 | + | ||
17 | + should 'describe itself' do | ||
18 | + assert_not_equal Noosfero::Plugin.plugin_description, ProfileMembersHeadlinesPlugin.plugin_description | ||
19 | + end | ||
20 | + | ||
21 | + should 'return ProfileMembersHeadlinesBlock in extra_blocks class method' do | ||
22 | + assert ProfileMembersHeadlinesPlugin.extra_blocks.keys.include?(ProfileMembersHeadlinesBlock) | ||
23 | + end | ||
24 | + | ||
25 | + should 'ProfileMembersHeadlinesBlock not available for environment' do | ||
26 | + assert_not_includes plugins.dispatch(:extra_blocks, :type => Environment), ProfileMembersHeadlinesBlock | ||
27 | + end | ||
28 | + | ||
29 | + should 'ProfileMembersHeadlinesBlock not available for people' do | ||
30 | + assert_not_includes plugins.dispatch(:extra_blocks, :type => Person), ProfileMembersHeadlinesBlock | ||
31 | + end | ||
32 | + | ||
33 | + should "ProfileMembersHeadlinesBlock be available for community" do | ||
34 | + assert_includes plugins.dispatch(:extra_blocks, :type => Community), ProfileMembersHeadlinesBlock | ||
35 | + end | ||
36 | + | ||
37 | + should 'has stylesheet' do | ||
38 | + assert ProfileMembersHeadlinesPlugin.new.stylesheet? | ||
39 | + end | ||
40 | + | ||
41 | +end |
plugins/profile_members_headlines/views/blocks/headlines.html.erb
0 → 100644
@@ -0,0 +1,39 @@ | @@ -0,0 +1,39 @@ | ||
1 | +<%= block_title(block.title) %> | ||
2 | + | ||
3 | +<% unless members.empty? %> | ||
4 | + <div class='headlines-container'> | ||
5 | + <% members.each do |member| %> | ||
6 | + <div> | ||
7 | + <% headline = member.headline %> | ||
8 | + <%= link_to_profile(profile_image(member, :big) + content_tag(:p, member.short_name), member.identifier, {:class => 'author'}) %> | ||
9 | + <div class='post'> | ||
10 | + <h4><%= link_to(headline.title, headline.url, :class => 'title') %></h4> | ||
11 | + <div class='lead'> | ||
12 | + <%= headline.short_lead %> | ||
13 | + </div> | ||
14 | + <div class='date'> | ||
15 | + <%= show_date(headline.published_at) %> | ||
16 | + </div> | ||
17 | + <div class='tags'> | ||
18 | + <%= headline.tags.map { |t| link_to(t, :controller => 'profile', :profile => member.identifier, :action => 'tags', :id => t.name ) }.join("\n") %> | ||
19 | + </div> | ||
20 | + </div> | ||
21 | + </div> | ||
22 | + <% end %> | ||
23 | + </div> | ||
24 | + <% if block.navigation %> | ||
25 | + <div class='headlines-block-pager'> | ||
26 | + </div> | ||
27 | + <% end %> | ||
28 | + | ||
29 | + <script> | ||
30 | + (function($) { | ||
31 | + var options = {fx: 'fade', pause: 1, fastOnEvent: 1, timeout: <%= block.interval * 1000 %>}; | ||
32 | + options.pager = '#block-<%= block.id %> .headlines-block-pager'; | ||
33 | + $('#block-<%= block.id %> .headlines-container').cycle(options); | ||
34 | + })(jQuery); | ||
35 | + </script> | ||
36 | +<% else %> | ||
37 | + <em><%= _('No headlines to be shown.') %></em> | ||
38 | +<% end %> | ||
39 | + |
plugins/profile_members_headlines/views/box_organizer/_profile_members_headlines_block.html.erb
0 → 100644
@@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
1 | +<div id="profile_members_headlines_plugin"> | ||
2 | + | ||
3 | + <%= labelled_form_field _('Headlines transition:'), select('block', 'interval', [[_('No automatic transition'), 0]] + [1, 2, 3, 4, 5, 10, 20, 30, 60].map {|item| [n_('Every 1 second', 'Every %d seconds', item) % item, item]}) %> | ||
4 | + | ||
5 | + <%= labelled_form_field check_box(:block, :navigation) + _('Display navigation buttons'), '' %> | ||
6 | + <%= labelled_form_field _('Limit of headlines'), text_field(:block, :limit, :size => 3) %> | ||
7 | + | ||
8 | + <%= label :block, :filtered_roles, _('Choose which roles should be displayed:'), :class => 'formlabel' %> | ||
9 | + | ||
10 | + <% Profile::Roles.organization_member_roles(environment).each do |role| %> | ||
11 | + <%= labelled_check_box(role.name, 'block[filtered_roles][]', role.id, @block.filtered_roles.include?(role.id)) %> | ||
12 | + <% end %> | ||
13 | +</div> |
test/unit/organization_test.rb
@@ -383,6 +383,33 @@ class OrganizationTest < ActiveSupport::TestCase | @@ -383,6 +383,33 @@ class OrganizationTest < ActiveSupport::TestCase | ||
383 | assert !organization.errors[:cnpj.to_s].present? | 383 | assert !organization.errors[:cnpj.to_s].present? |
384 | end | 384 | end |
385 | 385 | ||
386 | + should 'get members by role' do | ||
387 | + community = fast_create(Community) | ||
388 | + role1 = Role.create!(:name => 'role1') | ||
389 | + person1 = fast_create(Person) | ||
390 | + community.affiliate(person1, role1) | ||
391 | + role2 = Role.create!(:name => 'role2') | ||
392 | + person2 = fast_create(Person) | ||
393 | + community.affiliate(person2, role2) | ||
394 | + | ||
395 | + assert_equal [person1], community.members_by_role([role1]) | ||
396 | + end | ||
397 | + | ||
398 | + should 'get members by more than one role' do | ||
399 | + community = fast_create(Community) | ||
400 | + role1 = Role.create!(:name => 'role1') | ||
401 | + person1 = fast_create(Person) | ||
402 | + community.affiliate(person1, role1) | ||
403 | + role2 = Role.create!(:name => 'role2') | ||
404 | + person2 = fast_create(Person) | ||
405 | + community.affiliate(person2, role2) | ||
406 | + role3 = Role.create!(:name => 'role3') | ||
407 | + person3 = fast_create(Person) | ||
408 | + community.affiliate(person3, role3) | ||
409 | + | ||
410 | + assert_equal [person2, person3], community.members_by_role([role2, role3]) | ||
411 | + end | ||
412 | + | ||
386 | should 'return members by role in a json format' do | 413 | should 'return members by role in a json format' do |
387 | organization = fast_create(Organization) | 414 | organization = fast_create(Organization) |
388 | p1 = create_user('person-1').person | 415 | p1 = create_user('person-1').person |