biovision-comment 0.1.170914 → 0.7.190905.0

Sign up to get free protection for your applications and to get access to all the features.
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>