Commit 12a28da7f0beea6a5e66993ef52a12a72e1a448d
Committed by
Joenio Costa
1 parent
50f90482
Exists in
master
and in
22 other branches
Added plugin for mark comment as read
Signed-off-by: Leandro Nunes dos Santos <leandro.santos@serpro.gov.br> (ActionItem2670)
Showing
14 changed files
with
334 additions
and
0 deletions
Show diff stats
app/helpers/comment_helper.rb
| @@ -22,6 +22,12 @@ module CommentHelper | @@ -22,6 +22,12 @@ module CommentHelper | ||
| 22 | title | 22 | title |
| 23 | end | 23 | end |
| 24 | 24 | ||
| 25 | + def comment_extra_contents(comment) | ||
| 26 | + @plugins.dispatch(:comment_extra_contents, comment).collect do |extra_content| | ||
| 27 | + extra_content.kind_of?(Proc) ? self.instance_eval(&extra_content) : extra_content | ||
| 28 | + end.join('\n') | ||
| 29 | + end | ||
| 30 | + | ||
| 25 | def comment_actions(comment) | 31 | def comment_actions(comment) |
| 26 | url = url_for(:profile => profile.identifier, :controller => :comment, :action => :check_actions, :id => comment.id) | 32 | url = url_for(:profile => profile.identifier, :controller => :comment, :action => :check_actions, :id => comment.id) |
| 27 | links = links_for_comment_actions(comment) | 33 | links = links_for_comment_actions(comment) |
app/views/content_viewer/view_page.rhtml
| @@ -84,6 +84,8 @@ | @@ -84,6 +84,8 @@ | ||
| 84 | 84 | ||
| 85 | <%= display_source_info(@page) %> | 85 | <%= display_source_info(@page) %> |
| 86 | 86 | ||
| 87 | +<%= @plugins.dispatch(:article_extra_contents, @page).collect { |content| instance_eval(&content) }.join("") %> | ||
| 88 | + | ||
| 87 | <div class="comments" id="comments_list"> | 89 | <div class="comments" id="comments_list"> |
| 88 | 90 | ||
| 89 | <% if @page.accept_comments? || @comments_count > 0 %> | 91 | <% if @page.accept_comments? || @comments_count > 0 %> |
lib/noosfero/plugin.rb
| @@ -349,6 +349,12 @@ class Noosfero::Plugin | @@ -349,6 +349,12 @@ class Noosfero::Plugin | ||
| 349 | [] | 349 | [] |
| 350 | end | 350 | end |
| 351 | 351 | ||
| 352 | + # -> Adds adicional content to article | ||
| 353 | + # returns = lambda block that creates html code | ||
| 354 | + def article_extra_contents(article) | ||
| 355 | + nil | ||
| 356 | + end | ||
| 357 | + | ||
| 352 | # -> Adds fields to the signup form | 358 | # -> Adds fields to the signup form |
| 353 | # returns = lambda block that creates html code | 359 | # returns = lambda block that creates html code |
| 354 | def signup_extra_contents | 360 | def signup_extra_contents |
plugins/mark_comment_as_read/controllers/mark_comment_as_read_plugin_profile_controller.rb
0 → 100644
| @@ -0,0 +1,17 @@ | @@ -0,0 +1,17 @@ | ||
| 1 | +class MarkCommentAsReadPluginProfileController < ProfileController | ||
| 2 | + | ||
| 3 | + append_view_path File.join(File.dirname(__FILE__) + '/../views') | ||
| 4 | + | ||
| 5 | + def mark_as_read | ||
| 6 | + comment = Comment.find(params[:id]) | ||
| 7 | + comment.mark_as_read(user) | ||
| 8 | + render :text => {'ok' => true}.to_json, :content_type => 'application/json' | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + def mark_as_not_read | ||
| 12 | + comment = Comment.find(params[:id]) | ||
| 13 | + comment.mark_as_not_read(user) | ||
| 14 | + render :text => {'ok' => true}.to_json, :content_type => 'application/json' | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | +end |
plugins/mark_comment_as_read/db/migrate/20130509184338_create_mark_comment_as_read_plugin.rb
0 → 100644
| @@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
| 1 | +class CreateMarkCommentAsReadPlugin < ActiveRecord::Migration | ||
| 2 | + def self.up | ||
| 3 | + create_table :mark_comment_as_read_plugin do |t| | ||
| 4 | + t.integer :comment_id | ||
| 5 | + t.integer :person_id | ||
| 6 | + end | ||
| 7 | + add_index :mark_comment_as_read_plugin, [:comment_id, :person_id], :unique => true | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + def self.down | ||
| 11 | + drop_table :mark_comment_as_read_plugin | ||
| 12 | + end | ||
| 13 | +end |
plugins/mark_comment_as_read/lib/mark_comment_as_read_plugin.rb
0 → 100644
| @@ -0,0 +1,45 @@ | @@ -0,0 +1,45 @@ | ||
| 1 | +require_dependency 'mark_comment_as_read_plugin/ext/comment' | ||
| 2 | + | ||
| 3 | +class MarkCommentAsReadPlugin < Noosfero::Plugin | ||
| 4 | + | ||
| 5 | + def self.plugin_name | ||
| 6 | + "MarkCommentAsReadPlugin" | ||
| 7 | + end | ||
| 8 | + | ||
| 9 | + def self.plugin_description | ||
| 10 | + _("Provide a button to mark a comment as read.") | ||
| 11 | + end | ||
| 12 | + | ||
| 13 | + def js_files | ||
| 14 | + 'mark_comment_as_read.js' | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + def stylesheet? | ||
| 18 | + true | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + def comment_actions(comment) | ||
| 22 | + lambda do | ||
| 23 | + [{:link => link_to_function(_('Mark as not read'), 'toggle_comment_read(this, %s, false);' % url_for(:controller => 'mark_comment_as_read_plugin_profile', :profile => profile.identifier, :action => 'mark_as_not_read', :id => comment.id).to_json, :class => 'comment-footer comment-footer-link comment-footer-hide comment-action-extra', :style => 'display: none', :id => "comment-action-mark-as-not-read-#{comment.id}")}, | ||
| 24 | + {:link => link_to_function(_('Mark as read'), 'toggle_comment_read(this, %s, true);' % url_for(:controller => 'mark_comment_as_read_plugin_profile', :profile => profile.identifier, :action => 'mark_as_read', :id => comment.id).to_json, :class => 'comment-footer comment-footer-link comment-footer-hide comment-action-extra', :style => 'display: none', :id => "comment-action-mark-as-read-#{comment.id}")}] if user | ||
| 25 | + end | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | + def check_comment_actions(comment) | ||
| 29 | + lambda do | ||
| 30 | + if user | ||
| 31 | + comment.marked_as_read?(user) ? "#comment-action-mark-as-not-read-#{comment.id}" : "#comment-action-mark-as-read-#{comment.id}" | ||
| 32 | + end | ||
| 33 | + end | ||
| 34 | + end | ||
| 35 | + | ||
| 36 | + def article_extra_contents(article) | ||
| 37 | + lambda do | ||
| 38 | + if user | ||
| 39 | + ids = article.comments.marked_as_read(user).collect { |comment| comment.id} | ||
| 40 | + "<script type=\"text/javascript\">mark_comments_as_read(#{ids.to_json});</script>" if !ids.empty? | ||
| 41 | + end | ||
| 42 | + end | ||
| 43 | + end | ||
| 44 | + | ||
| 45 | +end |
plugins/mark_comment_as_read/lib/mark_comment_as_read_plugin/ext/comment.rb
0 → 100644
| @@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
| 1 | +require_dependency 'comment' | ||
| 2 | + | ||
| 3 | +class Comment | ||
| 4 | + | ||
| 5 | + has_many :read_comments, :class_name => 'MarkCommentAsReadPlugin::ReadComments' | ||
| 6 | + has_many :people, :through => :read_comments | ||
| 7 | + | ||
| 8 | + def mark_as_read(person) | ||
| 9 | + people << person | ||
| 10 | + end | ||
| 11 | + | ||
| 12 | + def mark_as_not_read(person) | ||
| 13 | + people.delete(person) | ||
| 14 | + end | ||
| 15 | + | ||
| 16 | + def marked_as_read?(person) | ||
| 17 | + person && people.find(:first, :conditions => {:id => person.id}) | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + def self.marked_as_read(person) | ||
| 21 | + find(:all, :joins => [:read_comments], :conditions => {:author_id => person.id}) | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | +end |
plugins/mark_comment_as_read/lib/mark_comment_as_read_plugin/read_comments.rb
0 → 100644
plugins/mark_comment_as_read/public/mark_comment_as_read.js
0 → 100644
| @@ -0,0 +1,28 @@ | @@ -0,0 +1,28 @@ | ||
| 1 | +function mark_comments_as_read(comments) { | ||
| 2 | + jQuery(document).ready(function($) { | ||
| 3 | + for(var i=0; i<comments.length; i++) { | ||
| 4 | + $comment = jQuery('#comment-'+comments[i]); | ||
| 5 | + $comment.find('.comment-content').first().addClass('comment-mark-read'); | ||
| 6 | + } | ||
| 7 | + }); | ||
| 8 | +} | ||
| 9 | + | ||
| 10 | +function toggle_comment_read(button, url, mark) { | ||
| 11 | + var $ = jQuery; | ||
| 12 | + var $button = $(button); | ||
| 13 | + $button.addClass('comment-button-loading'); | ||
| 14 | + $.post(url, function(data) { | ||
| 15 | + if (data.ok) { | ||
| 16 | + var $comment = $button.closest('.article-comment'); | ||
| 17 | + var $content = $comment.find('.comment-content').first(); | ||
| 18 | + if(mark) | ||
| 19 | + $content.addClass('comment-mark-read'); | ||
| 20 | + else | ||
| 21 | + $content.removeClass('comment-mark-read'); | ||
| 22 | + $button.hide(); | ||
| 23 | + $button.removeClass('comment-button-loading'); | ||
| 24 | + return; | ||
| 25 | + } | ||
| 26 | + }); | ||
| 27 | +} | ||
| 28 | + |
plugins/mark_comment_as_read/test/functional/mark_comment_as_read_plugin_profile_controller_test.rb
0 → 100644
| @@ -0,0 +1,33 @@ | @@ -0,0 +1,33 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
| 2 | +require File.dirname(__FILE__) + '/../../controllers/mark_comment_as_read_plugin_profile_controller' | ||
| 3 | + | ||
| 4 | +# Re-raise errors caught by the controller. | ||
| 5 | +class MarkCommentAsReadPluginProfileController; def rescue_action(e) raise e end; end | ||
| 6 | + | ||
| 7 | +class MarkCommentAsReadPluginProfileControllerTest < ActionController::TestCase | ||
| 8 | + def setup | ||
| 9 | + @controller = MarkCommentAsReadPluginProfileController.new | ||
| 10 | + @request = ActionController::TestRequest.new | ||
| 11 | + @response = ActionController::TestResponse.new | ||
| 12 | + @profile = create_user('profile').person | ||
| 13 | + @article = TinyMceArticle.create!(:profile => @profile, :name => 'An article') | ||
| 14 | + @comment = Comment.new(:source => @article, :author => @profile, :body => 'test') | ||
| 15 | + @comment.save! | ||
| 16 | + login_as(@profile.identifier) | ||
| 17 | + environment = Environment.default | ||
| 18 | + environment.enable_plugin(MarkCommentAsReadPlugin) | ||
| 19 | + self.stubs(:user).returns(@profile) | ||
| 20 | + end | ||
| 21 | + | ||
| 22 | + attr_reader :profile, :comment | ||
| 23 | + | ||
| 24 | + should 'mark comment as read' do | ||
| 25 | + xhr :post, :mark_as_read, :profile => profile.identifier, :id => comment.id | ||
| 26 | + assert_match /\{\"ok\":true\}/, @response.body | ||
| 27 | + end | ||
| 28 | + | ||
| 29 | + should 'mark comment as not read' do | ||
| 30 | + xhr :post, :mark_as_not_read, :profile => profile.identifier, :id => comment.id | ||
| 31 | + assert_match /\{\"ok\":true\}/, @response.body | ||
| 32 | + end | ||
| 33 | +end |
plugins/mark_comment_as_read/test/unit/mark_comment_as_read_plugin/comment_test.rb
0 → 100644
| @@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../../../../../test/test_helper' | ||
| 2 | + | ||
| 3 | +class MarkCommentAsReadPlugin::CommentTest < ActiveSupport::TestCase | ||
| 4 | + | ||
| 5 | + def setup | ||
| 6 | + @person = create_user('user').person | ||
| 7 | + @article = TinyMceArticle.create!(:profile => @person, :name => 'An article') | ||
| 8 | + @comment = Comment.create!(:title => 'title', :body => 'body', :author_id => @person.id, :source => @article) | ||
| 9 | + end | ||
| 10 | + | ||
| 11 | + should 'mark comment as read' do | ||
| 12 | + assert !@comment.marked_as_read?(@person) | ||
| 13 | + @comment.mark_as_read(@person) | ||
| 14 | + assert @comment.marked_as_read?(@person) | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + should 'do not mark a comment as read again' do | ||
| 18 | + @comment.mark_as_read(@person) | ||
| 19 | + assert_raise ActiveRecord::StatementInvalid do | ||
| 20 | + @comment.mark_as_read(@person) | ||
| 21 | + end | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | + should 'mark comment as not read' do | ||
| 25 | + @comment.mark_as_read(@person) | ||
| 26 | + assert @comment.marked_as_read?(@person) | ||
| 27 | + @comment.mark_as_not_read(@person) | ||
| 28 | + assert !@comment.marked_as_read?(@person) | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + should 'return comments marked as read for a user' do | ||
| 32 | + person2 = create_user('user2').person | ||
| 33 | + @comment.mark_as_read(@person) | ||
| 34 | + assert_equal [], @article.comments.marked_as_read(@person) - [@comment] | ||
| 35 | + assert_equal [], @article.comments.marked_as_read(person2) | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | +end |
plugins/mark_comment_as_read/test/unit/mark_comment_as_read_test.rb
0 → 100644
| @@ -0,0 +1,87 @@ | @@ -0,0 +1,87 @@ | ||
| 1 | +require File.dirname(__FILE__) + '/../../../../test/test_helper' | ||
| 2 | + | ||
| 3 | +class MarkCommentAsReadPluginTest < ActiveSupport::TestCase | ||
| 4 | + | ||
| 5 | + include ActionView::Helpers::TagHelper | ||
| 6 | + include NoosferoTestHelper | ||
| 7 | + | ||
| 8 | + def setup | ||
| 9 | + @plugin = MarkCommentAsReadPlugin.new | ||
| 10 | + @person = create_user('user').person | ||
| 11 | + @article = TinyMceArticle.create!(:profile => @person, :name => 'An article') | ||
| 12 | + @comment = Comment.create!(:source => @article, :author => @person, :body => 'test') | ||
| 13 | + self.stubs(:user).returns(@person) | ||
| 14 | + self.stubs(:profile).returns(@person) | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + attr_reader :plugin, :comment | ||
| 18 | + | ||
| 19 | + should 'show link when person is logged in' do | ||
| 20 | + action = @plugin.comment_actions(@comment) | ||
| 21 | + link = self.instance_eval(&action) | ||
| 22 | + assert link | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + should 'do not show link when person is not logged in' do | ||
| 26 | + self.stubs(:user).returns(nil) | ||
| 27 | + action = @plugin.comment_actions(@comment) | ||
| 28 | + link = self.instance_eval(&action) | ||
| 29 | + assert !link | ||
| 30 | + end | ||
| 31 | + | ||
| 32 | + should 'return actions when comment is not read' do | ||
| 33 | + action = @plugin.comment_actions(@comment) | ||
| 34 | + links = self.instance_eval(&action) | ||
| 35 | + assert_equal 2, links.size | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | + should 'return actions when comment is read' do | ||
| 39 | + @comment.mark_as_read(@person) | ||
| 40 | + action = @plugin.comment_actions(@comment) | ||
| 41 | + links = self.instance_eval(&action) | ||
| 42 | + assert_equal 2, links.size | ||
| 43 | + end | ||
| 44 | + | ||
| 45 | + should 'do not return any id when user is not logged in' do | ||
| 46 | + self.stubs(:user).returns(nil) | ||
| 47 | + action = @plugin.check_comment_actions(@comment) | ||
| 48 | + id = self.instance_eval(&action) | ||
| 49 | + assert !id | ||
| 50 | + end | ||
| 51 | + | ||
| 52 | + should 'return id of mark as not read link when comment is read' do | ||
| 53 | + @comment.mark_as_read(@person) | ||
| 54 | + action = @plugin.check_comment_actions(@comment) | ||
| 55 | + id = self.instance_eval(&action) | ||
| 56 | + assert_equal "#comment-action-mark-as-not-read-#{@comment.id}", id | ||
| 57 | + end | ||
| 58 | + | ||
| 59 | + should 'return id of mark as read link when comment is not read' do | ||
| 60 | + action = @plugin.check_comment_actions(@comment) | ||
| 61 | + id = self.instance_eval(&action) | ||
| 62 | + assert_equal "#comment-action-mark-as-read-#{@comment.id}", id | ||
| 63 | + end | ||
| 64 | + | ||
| 65 | + should 'return javascript to mark comment as read' do | ||
| 66 | + @comment.mark_as_read(@person) | ||
| 67 | + content = @plugin.article_extra_contents(@article) | ||
| 68 | + assert self.instance_eval(&content) | ||
| 69 | + end | ||
| 70 | + | ||
| 71 | + should 'do not return extra content if comment is not marked as read' do | ||
| 72 | + content = @plugin.article_extra_contents(@article) | ||
| 73 | + assert !self.instance_eval(&content) | ||
| 74 | + end | ||
| 75 | + | ||
| 76 | + should 'do not return extra content if user is not logged in' do | ||
| 77 | + @comment.mark_as_read(@person) | ||
| 78 | + self.stubs(:user).returns(nil) | ||
| 79 | + content = @plugin.article_extra_contents(@article) | ||
| 80 | + assert !self.instance_eval(&content) | ||
| 81 | + end | ||
| 82 | + | ||
| 83 | + def link_to_function(content, url, options = {}) | ||
| 84 | + link_to(content, url, options) | ||
| 85 | + end | ||
| 86 | + | ||
| 87 | +end |
test/unit/plugin_test.rb
| @@ -496,4 +496,29 @@ class PluginTest < ActiveSupport::TestCase | @@ -496,4 +496,29 @@ class PluginTest < ActiveSupport::TestCase | ||
| 496 | end | 496 | end |
| 497 | end | 497 | end |
| 498 | 498 | ||
| 499 | + should 'comment_actions be nil if the comment is nil' do | ||
| 500 | + class SomePlugin < Noosfero::Plugin; end | ||
| 501 | + plugin = SomePlugin.new | ||
| 502 | + assert_nil plugin.comment_actions(nil) | ||
| 503 | + end | ||
| 504 | + | ||
| 505 | + should 'comment_actions be nil by default' do | ||
| 506 | + class SomePlugin < Noosfero::Plugin; end | ||
| 507 | + plugin = SomePlugin.new | ||
| 508 | + assert_nil plugin.comment_actions(Comment.new) | ||
| 509 | + end | ||
| 510 | + | ||
| 511 | + should 'check_comment_actions be an empty array if the comment is nil' do | ||
| 512 | + class SomePlugin < Noosfero::Plugin; end | ||
| 513 | + plugin = SomePlugin.new | ||
| 514 | + assert_equal [], plugin.check_comment_actions(nil) | ||
| 515 | + end | ||
| 516 | + | ||
| 517 | + | ||
| 518 | + should 'check_comment_actions be an empty array by default' do | ||
| 519 | + class SomePlugin < Noosfero::Plugin; end | ||
| 520 | + plugin = SomePlugin.new | ||
| 521 | + assert_equal [], plugin.check_comment_actions(Comment.new) | ||
| 522 | + end | ||
| 523 | + | ||
| 499 | end | 524 | end |