biovision-post 0.21.190513.0 → 0.26.191013.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/biovision_post_manifest.js +1 -0
  3. data/app/assets/images/biovision/post/icons/comment.svg +9 -0
  4. data/app/assets/javascripts/biovision/post/biovision-posts.js +21 -28
  5. data/app/assets/stylesheets/biovision/post/posts.scss +6 -0
  6. data/app/controllers/admin/editorial_members_controller.rb +9 -4
  7. data/app/controllers/admin/featured_posts_controller.rb +9 -4
  8. data/app/controllers/admin/post_categories_controller.rb +12 -6
  9. data/app/controllers/admin/post_group_categories_controller.rb +9 -4
  10. data/app/controllers/admin/post_group_tags_controller.rb +9 -4
  11. data/app/controllers/admin/post_groups_controller.rb +9 -4
  12. data/app/controllers/admin/post_illustrations_controller.rb +9 -4
  13. data/app/controllers/admin/post_images_controller.rb +9 -4
  14. data/app/controllers/admin/post_links_controller.rb +9 -4
  15. data/app/controllers/admin/post_tags_controller.rb +9 -4
  16. data/app/controllers/admin/post_types_controller.rb +13 -4
  17. data/app/controllers/admin/posts_controller.rb +6 -8
  18. data/app/controllers/articles_controller.rb +16 -6
  19. data/app/controllers/authors_controller.rb +9 -4
  20. data/app/controllers/blog_posts_controller.rb +16 -6
  21. data/app/controllers/editorial_members_controller.rb +9 -4
  22. data/app/controllers/featured_posts_controller.rb +6 -1
  23. data/app/controllers/my/posts_controller.rb +35 -38
  24. data/app/controllers/news_controller.rb +16 -6
  25. data/app/controllers/post_attachments_controller.rb +36 -0
  26. data/app/controllers/post_categories_controller.rb +6 -1
  27. data/app/controllers/post_groups_controller.rb +9 -4
  28. data/app/controllers/post_illustrations_controller.rb +6 -1
  29. data/app/controllers/post_images_controller.rb +9 -8
  30. data/app/controllers/post_links_controller.rb +5 -2
  31. data/app/controllers/post_tags_controller.rb +9 -4
  32. data/app/controllers/posts_controller.rb +39 -17
  33. data/app/helpers/biovision_posts_helper.rb +28 -27
  34. data/app/jobs/post_body_parser_job.rb +1 -1
  35. data/app/models/concerns/post_child_with_priority.rb +2 -1
  36. data/app/models/editorial_member.rb +10 -1
  37. data/app/models/post.rb +39 -11
  38. data/app/models/post_attachment.rb +49 -0
  39. data/app/models/post_category.rb +6 -0
  40. data/app/models/post_group_category.rb +1 -1
  41. data/app/models/post_illustration.rb +2 -1
  42. data/app/models/post_image.rb +2 -1
  43. data/app/models/post_type.rb +15 -0
  44. data/app/services/biovision/components/posts_component.rb +65 -0
  45. data/app/views/admin/components/links/_posts.html.erb +33 -0
  46. data/app/views/admin/editorial_members/index.html.erb +2 -1
  47. data/app/views/admin/editorial_members/show.html.erb +1 -0
  48. data/app/views/admin/featured_posts/index.html.erb +1 -0
  49. data/app/views/admin/post_categories/entity/_in_list.html.erb +10 -0
  50. data/app/views/admin/post_categories/show.html.erb +11 -0
  51. data/app/views/admin/post_groups/index.html.erb +2 -1
  52. data/app/views/admin/post_groups/show.html.erb +1 -0
  53. data/app/views/admin/post_illustrations/index.html.erb +1 -0
  54. data/app/views/admin/post_illustrations/show.html.erb +1 -0
  55. data/app/views/admin/post_images/index.html.erb +2 -1
  56. data/app/views/admin/post_images/show.html.erb +1 -0
  57. data/app/views/admin/post_tags/index.html.erb +2 -1
  58. data/app/views/admin/post_tags/posts.html.erb +4 -3
  59. data/app/views/admin/post_tags/show.html.erb +3 -2
  60. data/app/views/admin/post_types/authors.html.erb +1 -0
  61. data/app/views/admin/post_types/index.html.erb +2 -1
  62. data/app/views/admin/post_types/new_post.html.erb +13 -4
  63. data/app/views/admin/post_types/post_categories.html.erb +1 -0
  64. data/app/views/admin/post_types/post_tags.html.erb +4 -3
  65. data/app/views/admin/post_types/show.html.erb +3 -2
  66. data/app/views/admin/posts/entity/_in_list.html.erb +3 -3
  67. data/app/views/admin/posts/images.html.erb +1 -0
  68. data/app/views/admin/posts/index.html.erb +2 -1
  69. data/app/views/admin/posts/search.html.erb +10 -1
  70. data/app/views/admin/posts/show.html.erb +6 -7
  71. data/app/views/authors/show.html.erb +1 -1
  72. data/app/views/blog_posts/index.html.erb +4 -2
  73. data/app/views/editorial_members/edit.html.erb +1 -0
  74. data/app/views/editorial_members/new.html.erb +3 -2
  75. data/app/views/my/index/dashboard/_biovision_post.html.erb +5 -4
  76. data/app/views/my/posts/_form.html.erb +40 -194
  77. data/app/views/my/posts/_post.html.erb +2 -3
  78. data/app/views/my/posts/articles.html.erb +1 -1
  79. data/app/views/my/posts/blog_posts.html.erb +1 -1
  80. data/app/views/my/posts/index/_navigation.html.erb +3 -3
  81. data/app/views/my/posts/new.html.erb +9 -1
  82. data/app/views/my/posts/news_index.html.erb +1 -1
  83. data/app/views/posts/_breadcrumbs.html.erb +1 -6
  84. data/app/views/posts/_form.html.erb +9 -2
  85. data/app/views/posts/_post.html.erb +7 -11
  86. data/app/views/posts/_preview.html.erb +7 -1
  87. data/app/views/posts/edit.html.erb +9 -1
  88. data/app/views/posts/post_type/_category.html.erb +4 -2
  89. data/app/views/posts/post_type/_post_page.html.erb +7 -1
  90. data/app/views/posts/post_type/_posts.html.erb +11 -2
  91. data/app/views/posts/post_type/_tagged.html.erb +4 -4
  92. data/app/views/posts/rss.xml.erb +1 -1
  93. data/app/views/posts/show.html.erb +0 -5
  94. data/app/views/posts/zen.xml.erb +3 -3
  95. data/config/locales/posts-ru.yml +11 -1
  96. data/config/routes.rb +3 -0
  97. data/db/{migrate → amends}/20190202232323_add_spam_to_posts.rb +0 -0
  98. data/db/{migrate → amends}/20190224212121_add_data_to_posts.rb +0 -0
  99. data/db/{migrate → amends}/20190224212122_rename_post_image_source.rb +0 -0
  100. data/db/{migrate → amends}/20190224212123_add_uuid_to_post_images.rb +0 -0
  101. data/db/{migrate → amends}/20190313141414_convert_json_post_columns.rb +0 -0
  102. data/db/amends/20190925181818_add_search_index_to_posts.rb +24 -0
  103. data/db/migrate/20170930000001_create_post_types.rb +4 -13
  104. data/db/migrate/20170930000010_create_posts.rb +26 -3
  105. data/db/migrate/20190715141414_add_data_to_post_categories.rb +14 -0
  106. data/db/migrate/20190801161616_add_posts_component.rb +14 -0
  107. data/db/migrate/20190822121212_create_post_attachments.rb +20 -0
  108. data/db/migrate/20190914212121_convert_post_privileges.rb +56 -0
  109. data/lib/biovision/post/version.rb +1 -1
  110. metadata +18 -13
  111. data/app/services/post_manager.rb +0 -71
  112. data/app/views/admin/index/dashboard/_biovision_post.html.erb +0 -36
  113. data/app/views/articles/show.html.erb +0 -6
  114. data/app/views/blog_posts/show.html.erb +0 -6
  115. data/app/views/news/show.html.erb +0 -6
@@ -26,13 +26,18 @@ class PostTagsController < AdminController
26
26
 
27
27
  private
28
28
 
29
- def set_entity
30
- @entity = PostTag.find_by(id: params[:id])
31
- handle_http_404('Cannot find post_tag') if @entity.nil?
29
+ def component_slug
30
+ Biovision::Components::PostsComponent::SLUG
32
31
  end
33
32
 
34
33
  def restrict_access
35
- require_privilege_group :editors
34
+ error = 'Managing post tags is not allowed'
35
+ handle_http_401(error) unless component_handler.allow?('chief_editor')
36
+ end
37
+
38
+ def set_entity
39
+ @entity = PostTag.find_by(id: params[:id])
40
+ handle_http_404('Cannot find post_tag') if @entity.nil?
36
41
  end
37
42
 
38
43
  def entity_parameters
@@ -20,8 +20,9 @@ class PostsController < ApplicationController
20
20
  if @entity.save
21
21
  apply_post_tags
22
22
  apply_post_categories
23
- PostBodyParserJob.perform_later(@entity.id)
24
- form_processed_ok(PostManager.new(@entity).post_path)
23
+ add_attachments if params.key?(:post_attachment)
24
+ mark_as_featured if params[:featured]
25
+ form_processed_ok(@entity.url)
25
26
  else
26
27
  form_processed_with_error(:new)
27
28
  end
@@ -48,8 +49,8 @@ class PostsController < ApplicationController
48
49
  if @entity.update(entity_parameters)
49
50
  apply_post_tags
50
51
  apply_post_categories
51
- PostBodyParserJob.perform_later(@entity.id)
52
- form_processed_ok(PostManager.new(@entity).post_path)
52
+ add_attachments if params.key?(:post_attachment)
53
+ form_processed_ok(@entity.url)
53
54
  else
54
55
  form_processed_with_error(:edit)
55
56
  end
@@ -57,9 +58,8 @@ class PostsController < ApplicationController
57
58
 
58
59
  # delete /posts/:id
59
60
  def destroy
60
- if @entity.destroy #@entity.update(deleted: true)
61
- flash[:notice] = t('posts.destroy.success')
62
- end
61
+ flash[:notice] = t('posts.destroy.success') if @entity.destroy #@entity.update(deleted: true)
62
+
63
63
  redirect_to admin_posts_path
64
64
  end
65
65
 
@@ -70,7 +70,8 @@ class PostsController < ApplicationController
70
70
 
71
71
  # get /posts/:category_slug
72
72
  def category
73
- @collection = Post.in_category(params[:category_slug]).page_for_visitors(current_page)
73
+ posts = Post.in_category(params[:category_slug])
74
+ @collection = posts.page_for_visitors(current_page)
74
75
  @category = @collection.first&.post_category
75
76
 
76
77
  handle_http_404('Cannot find post category in collection') if @category.nil?
@@ -88,12 +89,14 @@ class PostsController < ApplicationController
88
89
 
89
90
  # get /posts/rss/zen.xml
90
91
  def zen
91
- @collection = Post.for_language(current_language).list_for_visitors.posted_after(3.days.ago)
92
+ posts = Post.for_language(current_language).list_for_visitors
93
+ @collection = posts.posted_after(3.days.ago)
92
94
  end
93
95
 
94
96
  # get /posts/rss.xml
95
97
  def rss
96
- @collection = Post.for_language(current_language).list_for_visitors.first(20)
98
+ posts = Post.for_language(current_language).list_for_visitors
99
+ @collection = posts.first(20)
97
100
  end
98
101
 
99
102
  # get /posts/archive/(:year)(-:month)(-:day)
@@ -108,17 +111,22 @@ class PostsController < ApplicationController
108
111
 
109
112
  private
110
113
 
111
- def set_entity
112
- @entity = Post.find_by(id: params[:id])
113
- handle_http_404('Cannot find post') if @entity.nil?
114
+ def component_slug
115
+ Biovision::Components::PostsComponent::SLUG
114
116
  end
115
117
 
116
118
  def restrict_access
117
- require_privilege_group :editors
119
+ error = 'Managing posts is not allowed'
120
+ handle_http_401(error) unless component_handler.allow?
121
+ end
122
+
123
+ def set_entity
124
+ @entity = Post.find_by(id: params[:id])
125
+ handle_http_404('Cannot find post') if @entity.nil?
118
126
  end
119
127
 
120
128
  def restrict_editing
121
- if @entity.locked? || !@entity.editable_by?(current_user)
129
+ if @entity.locked? || !component_handler.editable?(@entity)
122
130
  handle_http_403('Post is locked or not editable by current user')
123
131
  end
124
132
  end
@@ -147,7 +155,7 @@ class PostsController < ApplicationController
147
155
  def owner_for_post
148
156
  key = :user_for_entity
149
157
  result = {}
150
- if current_user_has_privilege?(:chief_editor) && params.key?(key)
158
+ if component_handler.group?(:chief) && params.key?(key)
151
159
  result[:user_id] = param_from_request(key)
152
160
  end
153
161
  result
@@ -178,7 +186,21 @@ class PostsController < ApplicationController
178
186
  if Post.respond_to?(:search)
179
187
  Post.search(q).records.first(50).select(&:visible_to_visitors?)
180
188
  else
181
- Post.where('title ilike ?', "%#{q}%").list_for_visitors.first(50)
189
+ Post.pg_search(q).list_for_visitors.first(50)
182
190
  end
183
191
  end
192
+
193
+ def add_attachments
194
+ permitted = PostAttachment.entity_parameters
195
+ parameters = params.require(:post_attachment).permit(permitted)
196
+
197
+ @entity.post_attachments.create(parameters)
198
+ end
199
+
200
+ def mark_as_featured
201
+ FeaturedPost.where(language: @entity.language).update_all('priority = priority + 1')
202
+ link = FeaturedPost.new(language: @entity.language, post: @entity)
203
+ link.priority = 1
204
+ link.save
205
+ end
184
206
  end
@@ -70,26 +70,6 @@ module BiovisionPostsHelper
70
70
  end
71
71
  end
72
72
 
73
- # @param [Integer] post_type_id
74
- # @param [String] first_item
75
- def post_categories_for_select(post_type_id, first_item = t(:not_set))
76
- options = [[first_item, '']]
77
- PostCategory.for_tree(post_type_id).each do |category|
78
- options << [category.name, category.id]
79
- if category.child_categories.any?
80
- PostCategory.for_tree(post_type_id, category.id).each do |subcategory|
81
- options << ["-#{subcategory.name}", subcategory.id]
82
- if subcategory.child_categories.any?
83
- PostCategory.for_tree(post_type_id, subcategory.id).each do |deep_category|
84
- options << ["--#{deep_category.name}", deep_category.id]
85
- end
86
- end
87
- end
88
- end
89
- end
90
- options
91
- end
92
-
93
73
  def post_layouts_for_select
94
74
  options = PostLayout.ordered_by_name.map { |i| [i.name, i.id] }
95
75
  options + [[t(:not_set), '']]
@@ -99,13 +79,22 @@ module BiovisionPostsHelper
99
79
  # @param [String] text
100
80
  # @param [Hash] options
101
81
  def post_link(entity, text = entity.title, options = {})
102
- link_to(text, PostManager.new(entity).post_path, options)
82
+ link_to(text, entity.url, options)
103
83
  end
104
84
 
105
- # @param [Post|PostCategory] entity
106
- def post_category_link(entity)
107
- handler = PostManager.new(entity)
108
- link_to(handler.category_name, handler.category_path)
85
+ # @param [PostCategory] entity
86
+ # @param [String] text
87
+ # @param [Hash] options
88
+ def post_category_link(entity, text = nil, options = {})
89
+ text ||= entity.respond_to?(:name) ? entity.name : entity.title
90
+ link_to(text, entity.url, options)
91
+ end
92
+
93
+ # @param [PostType] entity
94
+ # @param [String] text
95
+ # @param [Hash] options
96
+ def post_type_link(entity, text = entity.category_name!, options = {})
97
+ link_to(text, entity.url, options)
109
98
  end
110
99
 
111
100
  # @param [PostGroup] entity
@@ -134,11 +123,23 @@ module BiovisionPostsHelper
134
123
  if entity.nil?
135
124
  link_to(tag_name, tagged_posts_path(tag_name: tag_name), rel: 'tag')
136
125
  else
137
- handler = PostManager.new(entity)
138
- link_to(tag_name, handler.tagged_path(tag_name), rel: 'tag')
126
+ link_to(tag_name, entity.tagged_path(tag_name), rel: 'tag')
139
127
  end
140
128
  end
141
129
 
130
+ # @param [PostAttachment] entity
131
+ # @param [String] text
132
+ # @param [Hash] options
133
+ def post_attachment_link(entity, text = entity.name!, options = {})
134
+ return '' if entity.file.blank?
135
+
136
+ default_options = {
137
+ target: '_blank'
138
+ }
139
+
140
+ link_to(text, entity.file.url, default_options.merge(options))
141
+ end
142
+
142
143
  # Post image preview for displaying in "administrative" lists
143
144
  #
144
145
  # @param [Post] entity
@@ -10,6 +10,6 @@ class PostBodyParserJob < ApplicationJob
10
10
 
11
11
  return if post.nil?
12
12
 
13
- post.update(parsed_body: PostManager.new(post).parsed_body)
13
+ post.update(parsed_body: PostParser.new(post).parsed_body)
14
14
  end
15
15
  end
@@ -15,8 +15,9 @@ module PostChildWithPriority
15
15
  end
16
16
 
17
17
  # @param [User] user
18
+ # @deprecated use component handler
18
19
  def editable_by?(user)
19
- post.editable_by?(user)
20
+ Biovision::Components::BaseComponent.handler('posts', user).editable?(post)
20
21
  end
21
22
 
22
23
  # @param [Integer] delta
@@ -50,6 +50,14 @@ class EditorialMember < ApplicationRecord
50
50
  user.is_a?(self) || owned_by(user).visible.exists?
51
51
  end
52
52
 
53
+ # @param [String|User] user
54
+ def self.[](user)
55
+ criteria = {
56
+ user: user.is_a?(User) ? user : User.find_by(slug: user)
57
+ }
58
+ find_by(criteria)
59
+ end
60
+
53
61
  def name
54
62
  user.profile_name
55
63
  end
@@ -71,8 +79,9 @@ class EditorialMember < ApplicationRecord
71
79
  end
72
80
 
73
81
  # @param [User] user
82
+ # @deprecated use component handler
74
83
  def editable_by?(user)
75
- UserPrivilege.user_has_privilege?(user, :chief_editor)
84
+ Biovision::Components::BaseComponent.handler('posts', user).editable?(self)
76
85
  end
77
86
 
78
87
  # @param [PostType] post_type
@@ -45,21 +45,19 @@ class Post < ApplicationRecord
45
45
  has_many :post_post_tags, dependent: :destroy
46
46
  has_many :post_tags, through: :post_post_tags
47
47
  has_many :post_images, dependent: :destroy
48
+ has_many :post_attachments, dependent: :destroy
48
49
  has_many :post_translations, dependent: :delete_all
49
50
  has_many :post_zen_categories, dependent: :destroy
50
51
  has_many :zen_categories, through: :post_zen_categories
51
52
 
52
53
  after_initialize { self.uuid = SecureRandom.uuid if uuid.nil? }
53
54
  after_initialize { self.publication_time = Time.now if publication_time.nil? }
54
- before_validation { self.slug = Canonizer.transliterate(title.to_s) if slug.blank? }
55
- before_validation { self.slug = slug.downcase }
55
+ before_validation :prepare_slug
56
56
  before_validation :prepare_source_names
57
57
 
58
58
  validates_presence_of :uuid, :title, :slug, :body
59
59
  validates_length_of :title, maximum: TITLE_LIMIT
60
- validates_length_of :slug, maximum: SLUG_LIMIT
61
60
  validates_length_of :lead, maximum: LEAD_LIMIT
62
- validates_length_of :body, maximum: BODY_LIMIT
63
61
  validates_length_of :image_name, maximum: IMAGE_NAME_LIMIT
64
62
  validates_length_of :image_alt_text, maximum: ALT_LIMIT
65
63
  validates_length_of :image_source_name, maximum: META_LIMIT
@@ -74,14 +72,15 @@ class Post < ApplicationRecord
74
72
  validates_length_of :author_title, maximum: META_LIMIT
75
73
  validates_length_of :author_url, maximum: META_LIMIT
76
74
  validates_length_of :translator_name, maximum: META_LIMIT
77
- validates_format_of :slug, with: SLUG_PATTERN
75
+ # validates_format_of :slug, with: SLUG_PATTERN
78
76
  validates_numericality_of :time_required, in: TIME_RANGE, allow_nil: true
79
77
 
80
78
  scope :recent, -> { order('publication_time desc') }
81
79
  scope :popular, -> { order('rating desc') }
82
80
  scope :visible, -> { where(visible: true, deleted: false, approved: true) }
83
81
  scope :published, -> { where('publication_time <= current_timestamp') }
84
- scope :for_language, ->(language) { where(language: language) }
82
+ scope :for_language, ->(language) { where(language: language).or(where(language: nil)) }
83
+ scope :pg_search, ->(v) { where("posts_tsvector(title, lead, body) @@ phraseto_tsquery('russian', ?)", v) }
85
84
  scope :exclude_ids, ->(v) { where('posts.id not in (?)', Array(v)) unless v.blank? }
86
85
  scope :list_for_visitors, -> { visible.published.recent }
87
86
  scope :list_for_administration, -> { order('id desc') }
@@ -89,6 +88,7 @@ class Post < ApplicationRecord
89
88
  scope :tagged, ->(tag) { joins(:post_post_tags).where(post_post_tags: { post_tag_id: PostTag.ids_for_name(tag) }).distinct unless tag.blank? }
90
89
  scope :in_category, ->(slug) { joins(:post_post_categories).where(post_post_categories: { post_category_id: PostCategory.ids_for_slug(slug) }).distinct unless slug.blank? }
91
90
  scope :in_category_branch, ->(category) { joins(:post_post_categories).where(post_post_categories: { post_category_id: category.subbranch_ids }).distinct }
91
+ scope :with_category_ids, ->(v) { joins(:post_post_categories).where(post_post_categories: { post_category_id: Array(v) }) }
92
92
  scope :authors, -> { User.where(id: Post.author_ids).order('screen_name asc') }
93
93
  scope :of_type, ->(slug) { where(post_type: PostType.find_by(slug: slug)) unless slug.blank? }
94
94
  scope :archive, -> { f = Arel.sql('date(publication_time)'); distinct.order(f).pluck(f) }
@@ -116,7 +116,7 @@ class Post < ApplicationRecord
116
116
  end
117
117
 
118
118
  def self.entity_parameters
119
- main_data = %i[body language_id lead original_title post_layout_id publication_time slug title]
119
+ main_data = %i[body language_id lead original_title post_layout_id publication_time region_id slug title]
120
120
  image_data = %i[image image_alt_text image_source_link image_source_name image_name]
121
121
  meta_data = %i[rating source_name source_link meta_title meta_description meta_keywords time_required]
122
122
  flags_data = %i[allow_comments allow_votes explicit show_owner visible translation]
@@ -148,9 +148,8 @@ class Post < ApplicationRecord
148
148
  def lead!
149
149
  if lead.blank?
150
150
  pattern = %r{<p>(.+?)</p>}
151
- chunk = parsed_body.blank? ? body : parsed_body
152
- passage = chunk.match(pattern)
153
- (passage.nil? ? chunk.gsub(/<[^>]+>/, '') : passage[1]).to_s[0..499]
151
+ passage = body.match(pattern)
152
+ (passage.nil? ? body.gsub(/<[^>]+>/, '') : passage[1]).to_s[0..499]
154
153
  else
155
154
  lead
156
155
  end
@@ -167,6 +166,25 @@ class Post < ApplicationRecord
167
166
  end
168
167
  end
169
168
 
169
+ # @param [Symbol] locale
170
+ def url(locale = I18n.default_locale)
171
+ prefix = locale.nil? || locale == I18n.default_locale ? '' : "/#{locale}"
172
+ "#{prefix}/#{post_type.url_part}/#{id}-#{slug}"
173
+ end
174
+
175
+ # @param [String] tag_name
176
+ # @param [Symbol] locale
177
+ def tagged_path(tag_name, locale = I18n.default_locale)
178
+ prefix = locale.nil? || locale == I18n.default_locale ? '' : "/#{locale}"
179
+ "#{prefix}/#{post_type.url_part}/tagged/#{CGI.escape(tag_name)}"
180
+ end
181
+
182
+ def enclosures
183
+ body.scan(/<img[^>]+>/).map do |image|
184
+ image.scan(/src="([^"]+)"/)[0][0]
185
+ end
186
+ end
187
+
170
188
  # Get editorial member instance for this post
171
189
  #
172
190
  # If user can be shown and is member of editorial, this method returns
@@ -223,8 +241,9 @@ class Post < ApplicationRecord
223
241
  end
224
242
 
225
243
  # @param [User] user
244
+ # @deprecated use component handler
226
245
  def editable_by?(user)
227
- owned_by?(user) || UserPrivilege.user_has_privilege?(user, :chief_editor)
246
+ Biovision::Components::BaseComponent.handler('posts', user).editable?(self)
228
247
  end
229
248
 
230
249
  def has_image_data?
@@ -253,8 +272,17 @@ class Post < ApplicationRecord
253
272
  update! tags_cache: post_tags.order('slug asc').map(&:name)
254
273
  end
255
274
 
275
+ def commentable_by?(user)
276
+ allow_comments? && !user.nil?
277
+ end
278
+
256
279
  private
257
280
 
281
+ def prepare_slug
282
+ self.slug = Canonizer.transliterate(title.to_s) if slug.blank?
283
+ self.slug = slug.downcase
284
+ end
285
+
258
286
  def prepare_source_names
259
287
  prepare_image_source
260
288
  prepare_source
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Model for post attachment
4
+ #
5
+ # Attributes
6
+ # created_at [DateTime]
7
+ # file [SimpleFileUploader]
8
+ # name [String]
9
+ # post_id [Post]
10
+ # updated_at [DateTime]
11
+ # uuid [UUID]
12
+ class PostAttachment < ApplicationRecord
13
+ include Checkable
14
+
15
+ NAME_LIMIT = 120
16
+
17
+ mount_uploader :file, SimpleFileUploader
18
+
19
+ belongs_to :post
20
+
21
+ after_initialize { self.uuid = SecureRandom.uuid if uuid.nil? }
22
+
23
+ validates_length_of :name, maximum: NAME_LIMIT
24
+ validates_presence_of :file
25
+
26
+ scope :ordered_for_list, -> { order('name asc, file asc') }
27
+
28
+ def self.entity_parameters
29
+ %i[file name]
30
+ end
31
+
32
+ def name!
33
+ return '' if file.blank?
34
+
35
+ name.blank? ? CGI.unescape(File.basename(file.path)) : name
36
+ end
37
+
38
+ def size
39
+ return 0 if file.blank?
40
+
41
+ File.size(file.path)
42
+ end
43
+
44
+ # @param [User] user
45
+ # @deprecated use component handler
46
+ def editable_by?(user)
47
+ Biovision::Components::BaseComponent.handler('posts', user).editable?(post)
48
+ end
49
+ end
@@ -141,6 +141,12 @@ class PostCategory < ApplicationRecord
141
141
  post_post_categories.where(post: post).delete_all
142
142
  end
143
143
 
144
+ # @param [Symbol] locale
145
+ def url(locale = I18n.default_locale)
146
+ prefix = locale.nil? || locale == I18n.default_locale ? '' : "/#{locale}"
147
+ "#{prefix}/#{post_type.url_part}/#{long_slug}"
148
+ end
149
+
144
150
  def text_for_link
145
151
  nav_text.blank? ? name : nav_text
146
152
  end