biovision-comment 0.1.170914 → 0.7.190905.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +68 -14
  3. data/app/assets/javascripts/biovision/comment/biovision-comments.js +118 -0
  4. data/app/assets/stylesheets/biovision/comment/comments.scss +111 -38
  5. data/app/controllers/admin/comments_controller.rb +18 -5
  6. data/app/controllers/comments_controller.rb +45 -32
  7. data/app/helpers/comments_helper.rb +20 -0
  8. data/app/models/comment.rb +88 -19
  9. data/app/models/concerns/commentable_item.rb +15 -0
  10. data/app/services/biovision/components/comments_component.rb +39 -0
  11. data/app/services/comment_handler.rb +49 -0
  12. data/app/services/comments_manager.rb +24 -0
  13. data/app/views/admin/comments/entity/_in_list.html.erb +8 -5
  14. data/app/views/admin/comments/index.html.erb +2 -1
  15. data/app/views/admin/comments/show.html.erb +25 -10
  16. data/app/views/admin/components/links/_comments.html.erb +3 -0
  17. data/app/views/comment_mailer/entry_reply.text.erb +2 -6
  18. data/app/views/comments/_comment.html.erb +66 -16
  19. data/app/views/comments/_form.html.erb +92 -19
  20. data/app/views/comments/_list.html.erb +17 -19
  21. data/app/views/comments/_reply_container.html.erb +15 -0
  22. data/app/views/comments/_section.html.erb +34 -0
  23. data/app/views/comments/edit.html.erb +6 -5
  24. data/app/views/comments/new.html.erb +2 -2
  25. data/config/locales/comments-ru.yml +32 -2
  26. data/config/routes.rb +18 -10
  27. data/db/migrate/20170914000001_create_comments.rb +48 -23
  28. data/db/migrate/20190203090909_add_fields_to_comments.rb +14 -0
  29. data/db/migrate/20190428212121_add_approved_to_comments.rb +14 -0
  30. data/db/migrate/20190428212122_create_comments_component.rb +21 -0
  31. data/db/migrate/20190628101010_add_data_to_comments.rb +15 -0
  32. data/lib/biovision/comment/engine.rb +7 -2
  33. data/lib/biovision/comment/version.rb +3 -1
  34. metadata +19 -8
  35. data/app/views/comments/show.html.erb +0 -27
@@ -1,25 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Comments
1
4
  class CommentsController < ApplicationController
2
- before_action :restrict_anonymous_access
3
- before_action :restrict_access, except: [:create]
4
- before_action :set_entity, only: [:show, :edit, :update, :destroy]
5
+ before_action :set_handler
6
+ before_action :restrict_access, except: %i[check create]
7
+ before_action :set_entity, only: %i[edit update destroy]
8
+
9
+ layout 'admin', except: :create
5
10
 
6
- layout 'admin'
11
+ # post /comments/check
12
+ def check
13
+ @entity = Comment.instance_for_check(params[:entity_id], entity_parameters)
14
+
15
+ render 'shared/forms/check'
16
+ end
7
17
 
8
18
  # post /comments
9
19
  def create
10
- @entity = Comment.new creation_parameters
11
- if @entity.save
12
- notify_participants
13
- redirect_to(@entity.commentable || admin_comment_path(@entity.id), notice: t('comments.create.success'))
20
+ if params.key?(:agree)
21
+ emulate_creation
14
22
  else
15
- render :new, layout: 'application', status: :bad_request
23
+ create_comment
16
24
  end
17
25
  end
18
26
 
19
- # get /comments/:id
20
- def show
21
- end
22
-
23
27
  # get /comments/:id/edit
24
28
  def edit
25
29
  end
@@ -27,9 +31,9 @@ class CommentsController < ApplicationController
27
31
  # patch /comments/:id
28
32
  def update
29
33
  if @entity.update entity_parameters
30
- redirect_to admin_comment_path(@entity.id), notice: t('comments.update.success')
34
+ form_processed_ok(admin_comment_path(id: @entity.id))
31
35
  else
32
- render :edit, status: :bad_request
36
+ form_processed_with_error(:edit)
33
37
  end
34
38
  end
35
39
 
@@ -43,36 +47,45 @@ class CommentsController < ApplicationController
43
47
 
44
48
  private
45
49
 
50
+ def component_slug
51
+ Biovision::Components::CommentsComponent::SLUG
52
+ end
53
+
46
54
  def restrict_access
47
- require_privilege :moderator
55
+ error = 'Managing comments is not allowed'
56
+ handle_http_401(error) unless component_handler.allow?('moderator')
57
+ end
58
+
59
+ def emulate_creation
60
+ form_processed_ok(root_path)
61
+ end
62
+
63
+ def create_comment
64
+ @entity = component_handler.create_comment(creation_parameters)
65
+ if @entity.valid?
66
+ notify_participants
67
+ next_page = param_from_request(:return_url)
68
+ form_processed_ok(next_page.match?(%r{\A/[^/]}) ? next_page : root_path)
69
+ else
70
+ form_processed_with_error(:new)
71
+ end
48
72
  end
49
73
 
50
74
  def set_entity
51
75
  @entity = Comment.find_by(id: params[:id])
52
- if @entity.nil?
53
- handle_http_404('Cannot find comment')
54
- end
76
+ handle_http_404('Cannot find comment') if @entity.nil?
55
77
  end
56
78
 
57
79
  def entity_parameters
58
- permitted = current_user_has_privilege?(:moderator, nil) ? Comment.administrative_parameters : Comment.entity_parameters
59
- params.require(:comment).permit(permitted)
80
+ params.require(:comment).permit(Comment.entity_parameters)
60
81
  end
61
82
 
62
83
  def creation_parameters
63
- params.require(:comment).permit(Comment.creation_parameters).merge(owner_for_entity(true))
84
+ permitted = Comment.creation_parameters
85
+ params.require(:comment).permit(permitted).merge(owner_for_entity(true))
64
86
  end
65
87
 
66
88
  def notify_participants
67
- commentable = @entity.commentable
68
- unless commentable.owned_by?(current_user)
69
- category = Notification.category_from_object(commentable)
70
- Notification.notify(commentable.user, category, commentable.id)
71
- # begin
72
- # Comments.entry_reply(@entity).deliver_now if @entity.notify_entry_owner?
73
- # rescue Net::SMTPAuthenticationError => error
74
- # logger.warn error.message
75
- # end
76
- end
89
+ # to be implemented...
77
90
  end
78
91
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Helper methods for handling comments
4
+ module CommentsHelper
5
+ # @param [Comment] entity
6
+ # @param [String] text
7
+ # @param [Hash] options
8
+ def admin_comment_link(entity, text = entity.text_for_link, options = {})
9
+ link_to(text, admin_comment_path(id: entity.id), options)
10
+ end
11
+
12
+ # @param [Comment] entity
13
+ # @param [String] text
14
+ # @param [Hash] options
15
+ def comment_link(entity, text = entity.commentable_name, options = {})
16
+ anchor = options.key?(:anchor)
17
+ options.delete(:anchor)
18
+ link_to(text, CommentsManager.commentable_path(entity, anchor), options)
19
+ end
20
+ end
@@ -1,53 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Comment
4
+ #
5
+ # Attributes:
6
+ # agent_id [Agent], optional
7
+ # approved [Boolean]
8
+ # author_email [String], optional
9
+ # author_name [String], optional
10
+ # body [Text]
11
+ # commentable_id [Integer]
12
+ # commentable_type [String]
13
+ # created_at [DateTime]
14
+ # deleted [Boolean]
15
+ # downvote_count [Integer]
16
+ # ip [Inet], optional
17
+ # locked [Boolean]
18
+ # parent_id [Comment], optional
19
+ # spam [Boolean]
20
+ # updated_at [DateTime]
21
+ # upvote_count [Integer]
22
+ # visible [Boolean]
23
+ # vote_result [Integer]
1
24
  class Comment < ApplicationRecord
25
+ include Checkable
2
26
  include HasOwner
3
27
  include Toggleable
4
- include VotableItem
28
+ include VotableItem if Gem.loaded_specs.key?('biovision-vote')
5
29
 
6
- PER_PAGE = 20
30
+ AUTHOR_LIMIT = 100
31
+ BODY_LIMIT = 1_048_576
7
32
 
8
33
  toggleable :visible
9
34
 
10
- belongs_to :user, optional: true, counter_cache: true, touch: false
35
+ belongs_to :user, optional: true
11
36
  belongs_to :agent, optional: true
12
37
  belongs_to :commentable, polymorphic: true, counter_cache: true, touch: false
38
+ belongs_to :parent, class_name: Comment.to_s, optional: true
39
+ has_many :child_comments, class_name: Comment.to_s, foreign_key: :parent_id, dependent: :destroy
13
40
 
14
41
  validates_presence_of :body
42
+ validates_length_of :body, maximum: BODY_LIMIT
43
+ validates_length_of :author_name, maximum: AUTHOR_LIMIT
44
+ validates_length_of :author_email, maximum: AUTHOR_LIMIT
15
45
  validate :commentable_is_commentable
16
46
 
47
+ after_create { commentable.comment_impact(self) if commentable.respond_to?(:comment_impact) }
48
+
17
49
  scope :recent, -> { order 'id desc' }
18
- scope :visible, -> { where(deleted: false, visible: true) }
50
+ scope :chronological, -> { order 'id asc' }
51
+ scope :visible, -> { where(deleted: false, visible: true, spam: false) } #, approved: true) }
52
+ scope :list_for_administration, -> { recent }
53
+ scope :list_for_visitors, -> { visible.chronological }
54
+ scope :list_for_visitors_recent, -> { visible.recent }
55
+ scope :list_for_owner, ->(v) { owned_by(v).where(deleted: false).recent }
19
56
 
20
57
  # @param [Integer] page
21
- def self.page_for_administration(page)
22
- recent.page(page).per(PER_PAGE)
58
+ def self.page_for_administration(page = 1)
59
+ list_for_administration.page(page)
23
60
  end
24
61
 
25
62
  # @param [Integer] page
26
- def self.page_for_visitor(page)
27
- recent.visible.page(page).per(PER_PAGE)
63
+ def self.page_for_visitor(page = 1)
64
+ list_for_visitors.page(page)
28
65
  end
29
66
 
30
67
  # @param [User] user
31
68
  # @param [Integer] page
32
- def self.page_for_owner(user, page)
33
- owned_by(user).where(deleted: false).recent.page(page).per(PER_PAGE)
69
+ def self.page_for_owner(user, page = 1)
70
+ list_for_owner(user).page(page)
34
71
  end
35
72
 
36
73
  def self.entity_parameters
37
- %i(body)
74
+ %i[author_name author_email body]
38
75
  end
39
76
 
40
77
  def self.creation_parameters
41
- entity_parameters + %i(commentable_id commentable_type)
78
+ entity_parameters + %i[commentable_id commentable_type parent_id]
42
79
  end
43
80
 
44
81
  def self.administrative_parameters
45
- entity_parameters + %i(deleted)
82
+ entity_parameters + %i[deleted visible spam]
83
+ end
84
+
85
+ def self.tree(collection)
86
+ result = {}
87
+
88
+ collection.each do |entity|
89
+ result[entity.id] = {
90
+ parent_id: entity.parent_id,
91
+ comment: entity,
92
+ }
93
+ end
94
+
95
+ result
46
96
  end
47
97
 
48
98
  # @param [User] user
49
99
  def visible_to?(user)
50
- if self.commentable.respond_to? :visible_to?
100
+ if commentable.respond_to? :visible_to?
51
101
  if deleted?
52
102
  UserPrivilege.user_has_privilege?(user, :administrator) && commentable.visible_to?(user)
53
103
  else
@@ -67,13 +117,32 @@ class Comment < ApplicationRecord
67
117
  end
68
118
  end
69
119
 
120
+ def text_for_link
121
+ "#{self.class.model_name.human} #{id}"
122
+ end
123
+
124
+ def commentable_name
125
+ "#{commentable.class.model_name.human} #{commentable_id}"
126
+ end
127
+
128
+ def commentable_title
129
+ if commentable.respond_to?(:title)
130
+ commentable.title
131
+ else
132
+ commentable_name
133
+ end
134
+ end
135
+
136
+ def profile_name
137
+ user.nil? ? author_name : user.profile_name
138
+ end
139
+
70
140
  private
71
141
 
72
142
  def commentable_is_commentable
73
- if self.commentable.respond_to? :commentable_by?
74
- unless self.commentable.commentable_by? self.user
75
- errors.add(:commentable, I18n.t('activerecord.errors.models.comment.attributes.commentable.not_commentable'))
76
- end
77
- end
143
+ return unless commentable.respond_to?(:commentable_by?)
144
+ return if commentable.commentable_by?(user)
145
+
146
+ errors.add(:commentable, I18n.t('activerecord.errors.models.comment.attributes.commentable.not_commentable'))
78
147
  end
79
148
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Adds "commentable" behavior to entity
4
+ module CommentableItem
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ has_many :comments, as: :commentable, dependent: :destroy
9
+ end
10
+
11
+ # @param [User] user
12
+ def commentable_by?(user)
13
+ !user.nil?
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Biovision
4
+ module Components
5
+ # Handler for Biovision Comments
6
+ class CommentsComponent < BaseComponent
7
+ SLUG = 'comments'
8
+
9
+ def self.privilege_names
10
+ %w[moderator]
11
+ end
12
+
13
+ def use_parameters?
14
+ false
15
+ end
16
+
17
+ # @param [Hash] parameters
18
+ def create_comment(parameters)
19
+ @comment = ::Comment.new(parameters)
20
+ @comment.save
21
+ @comment
22
+ end
23
+
24
+ protected
25
+
26
+ # @param [Hash] data
27
+ # @return [Hash]
28
+ def normalize_settings(data)
29
+ result = {}
30
+ flags = %w[premoderation]
31
+ flags.each { |f| result[f] = data[f].to_i == 1 }
32
+ numbers = %w[auto_approve_threshold]
33
+ numbers.each { |f| result[f] = data[f].to_i }
34
+
35
+ result
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Handler for working with comments
4
+ class CommentHandler
5
+ attr_accessor :user
6
+
7
+ # @param [User] user
8
+ def initialize(user = nil)
9
+ slug = Biovision::Components::CommentsComponent::SLUG
10
+ @user = user
11
+ @handler = Biovision::Components::BaseComponent.handler(slug)
12
+ end
13
+
14
+ # Get list of comments for entity
15
+ #
16
+ # Depending on privileges, receives list for visitors (only visible
17
+ # and approved) or all comments (including deleted).
18
+ #
19
+ # @param [ApplicationRecord] entity
20
+ def list(entity)
21
+ if @handler.class.allow?(@user)
22
+ entity.comments.list_for_administration
23
+ else
24
+ entity.comments.list_for_visitors
25
+ end
26
+ end
27
+
28
+ # Auto-approve comment from current user?
29
+ #
30
+ # If premoderation flag is not set, every comment is automatically approved.
31
+ # If premoderation flag is set, difference between approved and non-approved
32
+ # comments must be more than threshold; anonymous comments are always
33
+ # non-approved.
34
+ def approve?
35
+ return true unless @component.settings['premoderate']
36
+ return false if @user.nil?
37
+ return true if @component.class.allow?(@user)
38
+
39
+ gate = @component.settings['auto_approve_threshold'].to_i
40
+ positive = Comment.where(user: @user, approved: true).count
41
+ negative = Comment.where(user: @user, approved: false).count
42
+ positive - negative >= gate
43
+ end
44
+
45
+ # @param [ApplicationRecord] entity
46
+ def allow_reply?(entity)
47
+ entity.respond_to?(:commentable_by?) && entity.commentable_by?(@user)
48
+ end
49
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tool for handling comments
4
+ class CommentsManager
5
+ # @param [Comment] comment
6
+ # @param [TrueClass|FalseClass] anchor
7
+ def self.commentable_path(comment, anchor = false)
8
+ method_name = "#{comment.commentable_type}_path".downcase.to_sym
9
+ if respond_to?(method_name)
10
+ result = send(method_name, comment.commentable)
11
+ anchor ? "#{result}#comment-#{comment.id}" : result
12
+ else
13
+ "##{method_name}"
14
+ end
15
+ end
16
+
17
+ # @param [Post] entity
18
+ def self.post_path(entity)
19
+ return '#post' unless Gem.loaded_specs.key?('biovision-post')
20
+
21
+ handler = PostManager.new(entity)
22
+ handler.post_path
23
+ end
24
+ end
@@ -6,23 +6,26 @@
6
6
  <%= admin_comment_link(entity) %>
7
7
 
8
8
  <% if entity.deleted? %>
9
- (<%= t('activerecord.attributes.comment.deleted') %>)
9
+ (<%= t('activerecord.attributes.comment.deleted') %>)
10
10
  <% end %>
11
11
  </div>
12
12
 
13
13
  <div class="info">
14
+ <%= entity.commentable_name %>
15
+ (<%= entity.commentable_title %>)<br/>
14
16
  <%= admin_user_link(entity.user) %>,
15
17
  <%= time_tag entity.created_at %>
16
18
  </div>
17
19
 
18
- <div class="info">
19
- <%= prepare_comment_text(entity) %>
20
+ <div class="secondary info">
21
+ <%= simple_format(entity.body) %>
20
22
  </div>
21
23
 
22
24
  <ul class="actions">
23
- <li><%= edit_icon(edit_comment_path(entity.id)) %></li>
25
+ <li><%= edit_icon(edit_comment_path(id: entity.id)) %></li>
24
26
  <% unless entity.deleted? %>
25
- <li class="danger"><%= destroy_icon(entity) %></li>
27
+ <li><%= world_icon(CommentsManager.commentable_path(entity, true)) %></li>
28
+ <li class="danger"><%= destroy_icon(entity) %></li>
26
29
  <% end %>
27
30
  </ul>
28
31
  </div>