biovision-post 0.1.180127 → 0.2.180325

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