Commit 77d6fa0d4dbace32bc2ab87ac88e8bb657859c25
Committed by
Antonio Terceiro
1 parent
445b9833
Exists in
master
and in
22 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 | 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 | 30 | def has_permission_with_plugins?(permission, profile) |
| 25 | 31 | permissions = [has_permission_without_plugins?(permission, profile)] |
| 26 | 32 | permissions += plugins.map do |plugin| | ... | ... |
app/models/profile.rb
| ... | ... | @@ -108,8 +108,8 @@ class Profile < ActiveRecord::Base |
| 108 | 108 | alias_method_chain :count, :distinct |
| 109 | 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 | 113 | end |
| 114 | 114 | |
| 115 | 115 | acts_as_having_boxes | ... | ... |
| ... | ... | @@ -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 @@ |
| 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 @@ |
| 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 @@ |
| 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 @@ |
| 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 @@ |
| 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 @@ |
| 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 @@ |
| 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 @@ |
| 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 | 383 | assert !organization.errors[:cnpj.to_s].present? |
| 384 | 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 | 413 | should 'return members by role in a json format' do |
| 387 | 414 | organization = fast_create(Organization) |
| 388 | 415 | p1 = create_user('person-1').person | ... | ... |