biovision-post 0.1.180127 → 0.2.180325

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/articles_controller.rb +2 -2
  3. data/app/controllers/blog_posts_controller.rb +2 -2
  4. data/app/controllers/news_controller.rb +2 -2
  5. data/app/controllers/post_categories_controller.rb +4 -4
  6. data/app/controllers/posts_controller.rb +5 -15
  7. data/app/helpers/biovision_posts_helper.rb +4 -4
  8. data/app/models/concerns/post_child_with_priority.rb +49 -0
  9. data/app/models/post.rb +21 -15
  10. data/app/models/post_link.rb +15 -0
  11. data/app/models/post_note.rb +16 -0
  12. data/app/models/post_reference.rb +19 -0
  13. data/app/models/post_type.rb +3 -1
  14. data/app/services/post_manager.rb +9 -6
  15. data/app/services/post_manager/article_handler.rb +2 -2
  16. data/app/services/post_manager/blog_post_handler.rb +2 -2
  17. data/app/services/post_manager/news_handler.rb +2 -2
  18. data/app/views/admin/index/dashboard/_biovision_post.html.erb +1 -1
  19. data/app/views/admin/post_categories/entity/_in_list.html.erb +3 -3
  20. data/app/views/admin/post_categories/show.html.erb +3 -3
  21. data/app/views/admin/post_types/entity/_in_list.html.erb +1 -1
  22. data/app/views/admin/post_types/new_post.html.erb +1 -1
  23. data/app/views/admin/post_types/show.html.erb +2 -2
  24. data/app/views/admin/posts/entity/_in_list.html.erb +17 -3
  25. data/app/views/admin/posts/show.html.erb +4 -2
  26. data/app/views/articles/category.html.erb +1 -1
  27. data/app/views/blog_posts/category.html.erb +1 -1
  28. data/app/views/index/dashboard/_biovision_post.html.erb +5 -0
  29. data/app/views/news/category.html.erb +1 -1
  30. data/app/views/post_categories/edit.html.erb +1 -1
  31. data/app/views/posts/_form.html.erb +160 -22
  32. data/app/views/posts/_post.html.erb +1 -1
  33. data/app/views/posts/edit.html.erb +1 -1
  34. data/app/views/posts/entity/_metadata.html.erb +10 -6
  35. data/config/locales/posts-en.yml +4 -3
  36. data/config/locales/posts-ru.yml +3 -2
  37. data/config/routes.rb +39 -35
  38. data/db/migrate/20170930000001_create_post_types.rb +1 -0
  39. data/db/migrate/20170930000003_create_posts.rb +2 -0
  40. data/db/migrate/20171218111111_add_meta_fields_to_posts.rb +1 -1
  41. data/db/migrate/20171219111112_add_author_fields_to_posts.rb +1 -1
  42. data/db/migrate/20180124111113_add_language_to_posts.rb +1 -1
  43. data/db/migrate/20180318215555_add_active_to_post_types.rb +11 -0
  44. data/db/migrate/20180321100000_create_post_references.rb +21 -0
  45. data/db/migrate/20180321100001_create_post_notes.rb +18 -0
  46. data/db/migrate/20180321100002_create_post_links.rb +20 -0
  47. data/lib/biovision/post/engine.rb +2 -3
  48. data/lib/biovision/post/version.rb +1 -1
  49. metadata +11 -6
  50. data/app/views/index/index/_recent_posts.html.erb +0 -5
  51. data/app/views/posts/edit.js.erb +0 -1
  52. data/app/views/posts/form/_wysiwyg.html.erb +0 -14
  53. data/app/views/posts/new.js.erb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b889ba7a155e2072f97474ffa415f86b0aaeb387d5266de6f08715ca090dc016
4
- data.tar.gz: b20b223bca477ff0f077720a6102a69dbdd72e68e8b6bbeabb821d632c5296c8
3
+ metadata.gz: fbf3b6832851e48e1450b9c63d50ac2a197b03c0b2eb05349b4f9d6f7cd58dd5
4
+ data.tar.gz: f0171a57eb12a4642dffb3c115a46e5bcd63fa9287b67080427e695ce7243abb
5
5
  SHA512:
6
- metadata.gz: 9c4e4732cd224dfb38af8e72f881d306bed081a5382fcee710e493b685a87a7c7ba1b8b276660dde410adbfe6529b14a7ac1f2722d91f20e6fb0e52a9b792f81
7
- data.tar.gz: e5f81682ceb25780a39098d3197fe1120b5e70051de31ebffa7b89baba832b771833b4120e127b6d4d117cdc31b8d0ee21ee5d6664b85b2e06bef2e7f95b975d
6
+ metadata.gz: 7bea4d1a8c899d26f28949e979801fcd1754819c8e371769a8e6c4024c933f2becbadddaefa695ab88fbd1ca838ce39a2a51ae689ebda12e82bb2e786e8c605c
7
+ data.tar.gz: 3ae3e714da864096277d071035387e316a0ff89155d866f1491308c64dc89ecaccfce16584280db74072d0f746f2f2ec60f823335da2539cb9a3c6113b5aad79
@@ -5,12 +5,12 @@ class ArticlesController < ApplicationController
5
5
  # get /articles
6
6
  def index
7
7
  post_type = PostType.find_by(slug: 'article')
8
- @collection = post_type.posts.page_for_visitors(current_page)
8
+ @collection = post_type.posts.for_language(current_language).page_for_visitors(current_page)
9
9
  end
10
10
 
11
11
  # get /articles/:category_slug
12
12
  def category
13
- @collection = @category.posts.page_for_visitors(current_page)
13
+ @collection = @category.posts.for_language(current_language).page_for_visitors(current_page)
14
14
  end
15
15
 
16
16
  # get /articles/:post_id-:post_slug
@@ -5,12 +5,12 @@ class BlogPostsController < ApplicationController
5
5
  # get /blog_posts
6
6
  def index
7
7
  post_type = PostType.find_by(slug: 'blog_post')
8
- @collection = post_type.posts.page_for_visitors(current_page)
8
+ @collection = post_type.posts.for_language(current_language).page_for_visitors(current_page)
9
9
  end
10
10
 
11
11
  # get /blog_posts/:category_slug
12
12
  def category
13
- @collection = @category.posts.page_for_visitors(current_page)
13
+ @collection = @category.posts.for_language(current_language).page_for_visitors(current_page)
14
14
  end
15
15
 
16
16
  # get /blog_posts/:post_id-:post_slug
@@ -5,12 +5,12 @@ class NewsController < ApplicationController
5
5
  # get /news
6
6
  def index
7
7
  post_type = PostType.find_by(slug: 'news')
8
- @collection = post_type.posts.page_for_visitors(current_page)
8
+ @collection = post_type.posts.for_language(current_language).page_for_visitors(current_page)
9
9
  end
10
10
 
11
11
  # get /news/:category_slug
12
12
  def category
13
- @collection = @category.posts.page_for_visitors(current_page)
13
+ @collection = @category.posts.for_language(current_language).page_for_visitors(current_page)
14
14
  end
15
15
 
16
16
  # get /news/:post_id-:post_slug
@@ -11,7 +11,7 @@ class PostCategoriesController < AdminController
11
11
  def create
12
12
  @entity = PostCategory.new(creation_parameters)
13
13
  if @entity.save
14
- next_page = admin_post_category_path(@entity.id)
14
+ next_page = admin_post_category_path(id: @entity.id)
15
15
  respond_to do |format|
16
16
  format.html { redirect_to(next_page) }
17
17
  format.json { render json: { links: { self: next_page } } }
@@ -29,7 +29,7 @@ class PostCategoriesController < AdminController
29
29
  # patch /post_categories/:id
30
30
  def update
31
31
  if @entity.update entity_parameters
32
- next_page = admin_post_category_path(@entity)
32
+ next_page = admin_post_category_path(id: @entity.id)
33
33
  respond_to do |format|
34
34
  format.html { redirect_to(next_page, notice: t('post_categories.update.success')) }
35
35
  format.json { render json: { links: { self: next_page } } }
@@ -45,7 +45,7 @@ class PostCategoriesController < AdminController
45
45
  if @entity.destroy
46
46
  flash[:notice] = t('post_categories.destroy.success')
47
47
  end
48
- redirect_to(post_categories_admin_post_type_path(@entity.post_type_id))
48
+ redirect_to(post_categories_admin_post_type_path(id: @entity.post_type_id))
49
49
  end
50
50
 
51
51
  protected
@@ -56,7 +56,7 @@ class PostCategoriesController < AdminController
56
56
 
57
57
  def restrict_editing
58
58
  if @entity.locked?
59
- redirect_to admin_post_category_path(@entity.id), alert: t('post_categories.edit.forbidden')
59
+ redirect_to admin_post_category_path(id: @entity.id), alert: t('post_categories.edit.forbidden')
60
60
  end
61
61
  end
62
62
 
@@ -14,20 +14,15 @@ class PostsController < ApplicationController
14
14
  def create
15
15
  @entity = Post.new(creation_parameters)
16
16
  if @entity.save
17
- next_page = PostManager.handler(@entity).post_path
18
- respond_to do |format|
19
- format.html { redirect_to next_page }
20
- format.json { render json: { links: { self: next_page } } }
21
- format.js { render js: "document.location.href = '#{next_page}'" }
22
- end
17
+ form_processed_ok(PostManager.handler(@entity).post_path)
23
18
  else
24
- render :new, status: :bad_request
19
+ form_processed_with_error(:new)
25
20
  end
26
21
  end
27
22
 
28
23
  # get /posts/:id
29
24
  def show
30
- @entity = Post.find_by(id: params[:id], visible: true, deleted: false)
25
+ @entity = Post.list_for_visitors.find_by(id: params[:id])
31
26
  if @entity.nil?
32
27
  handle_http_404("Cannot find non-deleted post #{params[:id]}")
33
28
  end
@@ -40,14 +35,9 @@ class PostsController < ApplicationController
40
35
  # patch /posts/:id
41
36
  def update
42
37
  if @entity.update(entity_parameters)
43
- next_page = PostManager.handler(@entity).post_path
44
- respond_to do |format|
45
- format.html { redirect_to next_page }
46
- format.json { render json: { links: { self: next_page } } }
47
- format.js { render js: "document.location.href = '#{next_page}'" }
48
- end
38
+ form_processed_ok(PostManager.handler(@entity).post_path)
49
39
  else
50
- render :edit, status: :bad_request
40
+ form_processed_with_error(:edit)
51
41
  end
52
42
  end
53
43
 
@@ -2,19 +2,19 @@ module BiovisionPostsHelper
2
2
  # @param [PostType] entity
3
3
  # @param [String] text
4
4
  def admin_post_type_link(entity, text = entity.name)
5
- link_to(text, admin_post_type_path(entity.id))
5
+ link_to(text, admin_post_type_path(id: entity.id))
6
6
  end
7
7
 
8
8
  # @param [PostCategory] entity
9
9
  # @param [String] text
10
10
  def admin_post_category_link(entity, text = entity.name)
11
- link_to(text, admin_post_category_path(entity.id))
11
+ link_to(text, admin_post_category_path(id: entity.id))
12
12
  end
13
13
 
14
14
  # @param [Post] entity
15
15
  # @param [String] text
16
16
  def admin_post_link(entity, text = entity.title)
17
- link_to(text, admin_post_path(entity.id))
17
+ link_to(text, admin_post_path(id: entity.id))
18
18
  end
19
19
 
20
20
  # @param [Integer] post_type_id
@@ -40,7 +40,7 @@ module BiovisionPostsHelper
40
40
  # @param [String] text
41
41
  # @param [Hash] options
42
42
  def post_link(entity, text = entity.title, options = {})
43
- link_to(text, PostManager.handler(entity).post_path, options)
43
+ link_to(text, PostManager.handler(entity, locale).post_path, options)
44
44
  end
45
45
 
46
46
  # @param [Post] entity
@@ -0,0 +1,49 @@
1
+ module PostChildWithPriority
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ belongs_to :post
6
+
7
+ after_initialize :set_next_priority
8
+ before_validation :normalize_priority
9
+
10
+ scope :siblings, ->(entity) { where(post_id: entity.post_id) }
11
+ scope :ordered_by_priority, -> { order 'priority asc, id desc' }
12
+
13
+ def self.priority_range
14
+ (1..32767)
15
+ end
16
+
17
+ # @param [User] user
18
+ def editable_by?(user)
19
+ post.editable_by?(user)
20
+ end
21
+
22
+ # @param [Integer] delta
23
+ def change_priority(delta)
24
+ new_priority = priority + delta
25
+ criteria = { post_id: post_id, priority: new_priority }
26
+ adjacent = self.class.find_by(criteria)
27
+ if adjacent.is_a?(self.class) && (adjacent.id != id)
28
+ adjacent.update!(priority: priority)
29
+ end
30
+ self.update(priority: new_priority)
31
+
32
+ self.class.siblings(self).ordered_by_priority.map { |e| [e.id, e.priority] }.to_h
33
+ end
34
+
35
+ private
36
+
37
+ def set_next_priority
38
+ if id.nil? && priority == 1
39
+ self.priority = self.class.siblings(self).maximum(:priority).to_i + 1
40
+ end
41
+ end
42
+
43
+ def normalize_priority
44
+ range = self.class.priority_range
45
+ self.priority = range.first if priority < range.first
46
+ self.priority = range.last if priority > range.last
47
+ end
48
+ end
49
+ end
data/app/models/post.rb CHANGED
@@ -4,16 +4,17 @@ class Post < ApplicationRecord
4
4
  include VotableItem if Gem.loaded_specs.key?('biovision-vote')
5
5
  include Toggleable
6
6
 
7
- TITLE_LIMIT = 140
8
- SLUG_LIMIT = 200
9
- SLUG_PATTERN = /\A[a-z0-9]+[-_.a-z0-9]*[a-z0-9]+\z/
10
- LEAD_LIMIT = 350
11
- BODY_LIMIT = 50000
12
- META_LIMIT = 250
13
- ALT_LIMIT = 200
14
- PER_PAGE = 12
15
-
16
- toggleable :visible, :show_owner, :allow_comments
7
+ TITLE_LIMIT = 140
8
+ SLUG_LIMIT = 200
9
+ SLUG_PATTERN = /\A[a-z0-9]+[-_.a-z0-9]*[a-z0-9]+\z/
10
+ SLUG_PATTERN_HTML = '^[a-zA-Z0-9]+[-_.a-zA-Z0-9]*[a-zA-Z0-9]+$'
11
+ LEAD_LIMIT = 350
12
+ BODY_LIMIT = 50000
13
+ META_LIMIT = 250
14
+ ALT_LIMIT = 200
15
+ PER_PAGE = 12
16
+
17
+ toggleable :visible, :show_owner
17
18
 
18
19
  mount_uploader :image, PostImageUploader
19
20
 
@@ -23,6 +24,9 @@ class Post < ApplicationRecord
23
24
  belongs_to :post_category, counter_cache: true, optional: true
24
25
  belongs_to :language, optional: true
25
26
  belongs_to :agent, optional: true
27
+ has_many :post_references, dependent: :delete_all
28
+ has_many :post_notes, dependent: :delete_all
29
+ has_many :post_links, dependent: :delete_all
26
30
 
27
31
  after_initialize { self.uuid = SecureRandom.uuid if uuid.nil? }
28
32
  before_validation { self.slug = Canonizer.transliterate(title.to_s) if slug.blank? }
@@ -50,13 +54,15 @@ class Post < ApplicationRecord
50
54
  validates_format_of :slug, with: SLUG_PATTERN
51
55
  validate :category_consistency
52
56
 
53
- scope :recent, -> { order('id desc') }
57
+ scope :recent, -> { order('created_at desc') }
54
58
  scope :visible, -> { where(visible: true, deleted: false, approved: true) }
59
+ scope :for_language, -> (language) { where(language: language) }
55
60
  scope :list_for_visitors, -> { visible.recent }
61
+ scope :list_for_administration, -> { order('id desc') }
56
62
 
57
63
  # @param [Integer] page
58
64
  def self.page_for_administration(page = 1)
59
- recent.page(page).per(PER_PAGE)
65
+ list_for_administration.page(page).per(PER_PAGE)
60
66
  end
61
67
 
62
68
  # @param [Integer] page
@@ -65,10 +71,10 @@ class Post < ApplicationRecord
65
71
  end
66
72
 
67
73
  def self.entity_parameters
68
- main_data = %i(post_category_id title slug lead body visible translation region_id)
74
+ main_data = %i(post_category_id title slug lead body region_id language_id)
69
75
  image_data = %i(image image_alt_text image_name image_author_name image_author_link)
70
76
  meta_data = %i(source_name source_link meta_title meta_description meta_keywords)
71
- flags_data = %i(show_owner allow_comments)
77
+ flags_data = %i(show_owner allow_comments visible translation)
72
78
  author_data = %i(author_name author_title author_url)
73
79
 
74
80
  main_data + image_data + meta_data + author_data + flags_data
@@ -102,7 +108,7 @@ class Post < ApplicationRecord
102
108
 
103
109
  def prepare_source_names
104
110
  if image_author_name.blank? && !image_author_link.blank?
105
- self.image_author_name = URI.parse(image_author_name).host
111
+ self.image_author_name = URI.parse(image_author_link).host
106
112
  end
107
113
  if source_name.blank? && !source_link.blank?
108
114
  self.source_name = URI.parse(source_link).host
@@ -0,0 +1,15 @@
1
+ class PostLink < ApplicationRecord
2
+ include PostChildWithPriority
3
+
4
+ belongs_to :other_post, class_name: Post.to_s
5
+
6
+ validates_uniqueness_of :other_post_id, scope: [:post_id]
7
+
8
+ def self.entity_parameters
9
+ %i(priority)
10
+ end
11
+
12
+ def self.creation_parameters
13
+ %i(post_id other_post_id)
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ class PostNote < ApplicationRecord
2
+ include PostChildWithPriority
3
+
4
+ TEXT_LIMIT = 1000
5
+
6
+ validates_presence_of :text
7
+ validates_length_of :text, maximum: TEXT_LIMIT
8
+
9
+ def self.entity_parameters
10
+ %i(priority text)
11
+ end
12
+
13
+ def self.creation_parameters
14
+ entity_parameters + %i(post_id)
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ class PostReference < ApplicationRecord
2
+ include PostChildWithPriority
3
+
4
+ META_LIMIT = 255
5
+
6
+ validates_presence_of :title
7
+ validates_length_of :authors, maximum: META_LIMIT
8
+ validates_length_of :title, maximum: META_LIMIT
9
+ validates_length_of :url, maximum: META_LIMIT
10
+ validates_length_of :publishing_info, maximum: META_LIMIT
11
+
12
+ def self.entity_parameters
13
+ %i(priority authors title url publishing_info)
14
+ end
15
+
16
+ def self.creation_parameters
17
+ entity_parameters + %i(post_id)
18
+ end
19
+ end
@@ -15,7 +15,9 @@ class PostType < ApplicationRecord
15
15
  validates_format_of :slug, with: SLUG_PATTERN
16
16
  validates_inclusion_of :category_depth, in: DEPTH_RANGE
17
17
 
18
+ scope :active, -> { where(active: true) }
19
+
18
20
  def self.page_for_administration
19
- ordered_by_name
21
+ active.ordered_by_name
20
22
  end
21
23
  end
@@ -1,15 +1,18 @@
1
1
  class PostManager
2
2
  # @param [Post] entity
3
- def initialize(entity)
3
+ # @param [Symbol|String] locale
4
+ def initialize(entity, locale = I18n.locale)
4
5
  @entity = entity
5
6
  @body = entity.body.to_s
7
+ @prefix = locale.nil? ? '' : "/#{locale}"
6
8
  end
7
9
 
8
10
  # @param [Post] entity
9
- def self.handler(entity)
11
+ # @param [Symbol|String] locale
12
+ def self.handler(entity, locale = I18n.locale)
10
13
  handler_name = "post_manager/#{entity.post_type.slug}_handler".classify
11
14
  handler_class = handler_name.safe_constantize || PostManager
12
- handler_class.new(entity)
15
+ handler_class.new(entity, locale)
13
16
  end
14
17
 
15
18
  def parsed_body
@@ -17,15 +20,15 @@ class PostManager
17
20
  end
18
21
 
19
22
  def post_path
20
- "/posts/#{@entity.id}"
23
+ "#{@prefix}/posts/#{@entity.id}"
21
24
  end
22
25
 
23
26
  def edit_path
24
- "/posts/#{@entity.id}/edit"
27
+ "#{@prefix}/posts/#{@entity.id}/edit"
25
28
  end
26
29
 
27
30
  # @param [PostCategory] category
28
31
  def category_path(category)
29
- "posts/#{category.long_slug}"
32
+ "#{@prefix}/posts/#{category.long_slug}"
30
33
  end
31
34
  end
@@ -1,10 +1,10 @@
1
1
  class PostManager::ArticleHandler < PostManager
2
2
  def post_path
3
- "/articles/#{@entity.id}-#{@entity.slug}"
3
+ "#{@prefix}/articles/#{@entity.id}-#{@entity.slug}"
4
4
  end
5
5
 
6
6
  # @param [PostCategory] category
7
7
  def category_path(category)
8
- "/articles/#{category.long_slug}"
8
+ "#{@prefix}/articles/#{category.long_slug}"
9
9
  end
10
10
  end
@@ -1,10 +1,10 @@
1
1
  class PostManager::BlogPostHandler < PostManager
2
2
  def post_path
3
- "/blog_posts/#{@entity.id}-#{@entity.slug}"
3
+ "#{@prefix}/blog_posts/#{@entity.id}-#{@entity.slug}"
4
4
  end
5
5
 
6
6
  # @param [PostCategory] category
7
7
  def category_path(category)
8
- "/blog_posts/#{category.long_slug}"
8
+ "#{@prefix}/blog_posts/#{category.long_slug}"
9
9
  end
10
10
  end
@@ -1,10 +1,10 @@
1
1
  class PostManager::NewsHandler < PostManager
2
2
  def post_path
3
- "/news/#{@entity.id}-#{@entity.slug}"
3
+ "#{@prefix}/news/#{@entity.id}-#{@entity.slug}"
4
4
  end
5
5
 
6
6
  # @param [PostCategory] category
7
7
  def category_path(category)
8
- "/news/#{category.long_slug}"
8
+ "#{@prefix}/news/#{category.long_slug}"
9
9
  end
10
10
  end