decidim-blogs 0.29.4 → 0.30.0.rc1

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/blogs/content_blocks/highlighted_posts_cell.rb +0 -3
  3. data/app/commands/decidim/blogs/admin/create_post.rb +5 -5
  4. data/app/commands/decidim/blogs/create_post.rb +35 -0
  5. data/app/commands/decidim/blogs/update_post.rb +21 -0
  6. data/app/controllers/decidim/blogs/admin/posts_controller.rb +12 -9
  7. data/app/controllers/decidim/blogs/posts_controller.rb +61 -1
  8. data/app/forms/decidim/blogs/post_form.rb +52 -0
  9. data/app/helpers/decidim/blogs/posts_helper.rb +5 -0
  10. data/app/helpers/decidim/blogs/posts_select_helper.rb +32 -0
  11. data/app/models/decidim/blogs/post.rb +7 -2
  12. data/app/packs/stylesheets/blogs.scss +0 -20
  13. data/app/permissions/decidim/blog/permissions.rb +85 -2
  14. data/app/presenters/decidim/blogs/admin_log/post_presenter.rb +1 -1
  15. data/app/presenters/decidim/blogs/post_presenter.rb +25 -1
  16. data/app/views/decidim/blogs/admin/posts/_actions.html.erb +21 -0
  17. data/app/views/decidim/blogs/admin/posts/_post-tr.html.erb +25 -0
  18. data/app/views/decidim/blogs/admin/posts/_posts-thead.html.erb +9 -0
  19. data/app/views/decidim/blogs/admin/posts/index.html.erb +16 -55
  20. data/app/views/decidim/blogs/admin/posts/manage_trash.html.erb +20 -0
  21. data/app/views/decidim/blogs/posts/_actions.html.erb +7 -14
  22. data/app/views/decidim/blogs/posts/_form.html.erb +11 -0
  23. data/app/views/decidim/blogs/posts/_menu_actions.html.erb +21 -0
  24. data/app/views/decidim/blogs/posts/_schema_org_blog_posting_post.html.erb +3 -0
  25. data/app/views/decidim/blogs/posts/edit.html.erb +21 -0
  26. data/app/views/decidim/blogs/posts/index.html.erb +8 -1
  27. data/app/views/decidim/blogs/posts/new.html.erb +21 -0
  28. data/app/views/decidim/blogs/posts/show.html.erb +15 -6
  29. data/config/locales/ar.yml +0 -1
  30. data/config/locales/bg.yml +0 -3
  31. data/config/locales/ca.yml +58 -3
  32. data/config/locales/cs.yml +39 -4
  33. data/config/locales/de.yml +58 -3
  34. data/config/locales/el.yml +0 -3
  35. data/config/locales/en.yml +58 -3
  36. data/config/locales/es-MX.yml +59 -4
  37. data/config/locales/es-PY.yml +59 -4
  38. data/config/locales/es.yml +59 -4
  39. data/config/locales/eu.yml +60 -5
  40. data/config/locales/fi-plain.yml +39 -4
  41. data/config/locales/fi.yml +39 -4
  42. data/config/locales/fr-CA.yml +32 -2
  43. data/config/locales/fr.yml +32 -2
  44. data/config/locales/gl.yml +0 -1
  45. data/config/locales/hu.yml +0 -3
  46. data/config/locales/ja.yml +57 -2
  47. data/config/locales/kaa.yml +0 -1
  48. data/config/locales/ko.yml +0 -2
  49. data/config/locales/lt.yml +0 -3
  50. data/config/locales/nl.yml +0 -1
  51. data/config/locales/no.yml +0 -1
  52. data/config/locales/pl.yml +0 -3
  53. data/config/locales/pt-BR.yml +0 -3
  54. data/config/locales/pt.yml +0 -37
  55. data/config/locales/ro-RO.yml +41 -35
  56. data/config/locales/ru.yml +0 -3
  57. data/config/locales/sq-AL.yml +0 -3
  58. data/config/locales/sv.yml +58 -3
  59. data/config/locales/zh-TW.yml +0 -3
  60. data/db/migrate/20200827153709_add_commentable_counter_cache_to_posts.rb +1 -1
  61. data/db/migrate/20210310120514_add_followable_counter_cache_to_blogs.rb +1 -1
  62. data/db/migrate/20240828103324_add_deleted_at_to_decidim_blogs_posts.rb +8 -0
  63. data/decidim-blogs.gemspec +1 -1
  64. data/lib/decidim/api/blogs_type.rb +4 -5
  65. data/lib/decidim/api/post_type.rb +2 -2
  66. data/lib/decidim/blogs/admin_engine.rb +7 -1
  67. data/lib/decidim/blogs/component.rb +27 -0
  68. data/lib/decidim/blogs/engine.rb +1 -1
  69. data/lib/decidim/blogs/post_serializer.rb +70 -0
  70. data/lib/decidim/blogs/schema_org_blog_posting_post_serializer.rb +81 -0
  71. data/lib/decidim/blogs/test/factories.rb +9 -4
  72. data/lib/decidim/blogs/version.rb +1 -1
  73. data/lib/decidim/blogs.rb +2 -0
  74. metadata +34 -21
  75. data/app/jobs/decidim/blogs/application_job.rb +0 -8
  76. data/app/jobs/decidim/blogs/publish_post_job.rb +0 -27
  77. data/config/locales/ca-IT.yml +0 -98
@@ -18,7 +18,6 @@ sq:
18
18
  blogs:
19
19
  actions:
20
20
  author_id: Krijo postimin si
21
- destroy: Fshij
22
21
  edit: Përpuno
23
22
  new: Postim i ri
24
23
  title: Veprime
@@ -27,8 +26,6 @@ sq:
27
26
  create:
28
27
  invalid: Ndodhi një gabim gjatë krijimit të këtij postimi.
29
28
  success: Postimi u krijua me sukses.
30
- destroy:
31
- success: Postimi u fshi me sukses.
32
29
  edit:
33
30
  save: Përditëso
34
31
  title: Përpuno postimin
@@ -15,27 +15,34 @@ sv:
15
15
  one: Inlägg
16
16
  other: Inlägg
17
17
  decidim:
18
+ admin:
19
+ admin_log:
20
+ changeset:
21
+ posts: Inlägg
18
22
  blogs:
19
23
  actions:
20
24
  author_id: Skapa inlägg som
21
- confirm_destroy: Är du säker på att du vill radera inlägget?
22
- destroy: Radera
25
+ confirm_delete_post: Är du säker på att du vill radera inlägget?
26
+ deleted_posts_info: Raderade inlägg kan återställas från papperskorgen.
23
27
  edit: Redigera
24
28
  new: Nytt inlägg
25
29
  title: Åtgärder
30
+ view_deleted_posts: Visa raderade inlägg
26
31
  admin:
27
32
  posts:
28
33
  create:
29
34
  invalid: Det gick inte att skapa inlägget.
30
35
  success: Inlägget har skapats.
31
36
  destroy:
32
- success: Inlägget har raderats.
37
+ success: Inlägget raderades.
33
38
  edit:
34
39
  save: Uppdatera
35
40
  title: Redigera inlägg
36
41
  index:
37
42
  not_published_yet: Ej publicerat.
38
43
  title: Inlägg
44
+ manage_trash:
45
+ title: Raderade inlägg
39
46
  new:
40
47
  create: Skapa
41
48
  title: Skapa inlägg
@@ -46,6 +53,8 @@ sv:
46
53
  post:
47
54
  create: "%{user_name} skapade blogginlägget %{resource_name} i %{space_name}"
48
55
  delete: "%{user_name} raderade blogginlägget %{resource_name} från %{space_name}"
56
+ restore: "%{user_name} återställde %{resource_name} blogginlägg i %{space_name}"
57
+ soft_delete: "%{user_name} flyttade blogginlägget %{resource_name} i %{space_name} till papperskorgen"
49
58
  update: "%{user_name} uppdaterade blogginlägget %{resource_name} i %{space_name}"
50
59
  content_blocks:
51
60
  highlighted_posts:
@@ -63,11 +72,28 @@ sv:
63
72
  published_at: Publicerad
64
73
  title: Titel
65
74
  posts:
75
+ edit:
76
+ back: Tillbaka till inlägget
77
+ button: Uppdatera
78
+ title: Redigera inlägg
79
+ form:
80
+ author_id: Författare
81
+ body: Text
82
+ title: Titel
66
83
  index:
67
84
  count:
68
85
  one: "%{count} inlägg"
69
86
  other: "%{count} inlägg"
70
87
  empty: Det finns inga inlägg ännu.
88
+ new_post: Nytt inlägg
89
+ menu_actions:
90
+ button_destroy: Ta bort inlägg
91
+ button_destroy_confirm: Är du säker på att du vill radera inlägget?
92
+ button_edit: Redigera inlägg
93
+ new:
94
+ back: Tillbaka till inlägg
95
+ button: Skapa
96
+ title: Skapa nytt inlägg
71
97
  components:
72
98
  blogs:
73
99
  actions:
@@ -82,6 +108,7 @@ sv:
82
108
  announcement: Meddelande
83
109
  comments_enabled: Aktivera kommentarer
84
110
  comments_max_length: Maximal kommentarslängd (ange 0 för att använda standardvärdet)
111
+ creation_enabled_for_participants: Deltagarna kan skapa inlägg
85
112
  step:
86
113
  announcement: Meddelande
87
114
  comments_blocked: Stäng av kommentarer
@@ -94,5 +121,33 @@ sv:
94
121
  email_outro: Du har fått det här meddelandet eftersom du följer "%{participatory_space_title}". Du kan sluta att följa det från den föregående länken.
95
122
  email_subject: Ett nytt inlägg har publicerats i %{participatory_space_title}
96
123
  notification_title: Inlägget <a href="%{resource_path}">%{resource_title}</a> har publicerats i %{participatory_space_title}
124
+ open_data:
125
+ help:
126
+ post_comments:
127
+ alignment: Positiva, negativa eller neutrala kommentarer
128
+ author: Namn på den som kommenterat
129
+ body: Kommentaren
130
+ commentable_id: Kommentarens unika id
131
+ commentable_type: Vad som kommenterades (resultat, förslag etc)
132
+ created_at: Datum för kommentaren
133
+ depth: Kommentarens plats i en hierarki av kommentarer (om den är ett svar på en annan kommentar)
134
+ id: Id för kommentaren
135
+ locale: Kommentarförfattarens språk
136
+ root_commentable_url: URL till innehållet som är relaterat till kommentaren
137
+ user_group: Namn på gruppen som kommenterat (om det var en grupp)
138
+ posts:
139
+ author: Uppgifter om författaren
140
+ body: Inläggets text
141
+ comments_count: Antalet kommentarer till inlägget
142
+ component: Komponenten som inlägget tillhör
143
+ created_at: Datum för inlägget
144
+ endorsements_count: Antal gilla-markeringar för inlägget
145
+ follows_count: Antal följare för inlägget
146
+ id: Unikt id för inlägget
147
+ participatory_space: Vilket utrymme (deltagandeprocess eller samråd) inlägget tillhör
148
+ published_at: Publiceringsdatum för inlägget
149
+ title: Titeln på inlägget
150
+ updated_at: Senaste datum när inlägget uppdaterades
151
+ url: URL för inlägget
97
152
  statistics:
98
153
  posts_count: Inlägg
@@ -17,7 +17,6 @@ zh-TW:
17
17
  blogs:
18
18
  actions:
19
19
  author_id: 發佈文章
20
- destroy: 刪除
21
20
  edit: 編輯
22
21
  new: 新貼文
23
22
  title: 操作
@@ -26,8 +25,6 @@ zh-TW:
26
25
  create:
27
26
  invalid: 建立此文章時出現問題
28
27
  success: 文章已成功建立
29
- destroy:
30
- success: 文章已成功刪除
31
28
  edit:
32
29
  save: 更新
33
30
  title: 编辑文章
@@ -4,6 +4,6 @@ class AddCommentableCounterCacheToPosts < ActiveRecord::Migration[5.2]
4
4
  def change
5
5
  add_column :decidim_blogs_posts, :comments_count, :integer, null: false, default: 0, index: true
6
6
  Decidim::Blogs::Post.reset_column_information
7
- Decidim::Blogs::Post.find_each(&:update_comments_count)
7
+ Decidim::Blogs::Post.unscoped.find_each(&:update_comments_count)
8
8
  end
9
9
  end
@@ -7,7 +7,7 @@ class AddFollowableCounterCacheToBlogs < ActiveRecord::Migration[5.2]
7
7
  reversible do |dir|
8
8
  dir.up do
9
9
  Decidim::Blogs::Post.reset_column_information
10
- Decidim::Blogs::Post.find_each do |record|
10
+ Decidim::Blogs::Post.unscoped.find_each do |record|
11
11
  record.class.reset_counters(record.id, :follows)
12
12
  end
13
13
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddDeletedAtToDecidimBlogsPosts < ActiveRecord::Migration[7.0]
4
+ def change
5
+ add_column :decidim_blogs_posts, :deleted_at, :datetime
6
+ add_index :decidim_blogs_posts, :deleted_at
7
+ end
8
+ end
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  "homepage_uri" => "https://decidim.org",
20
20
  "source_code_uri" => "https://github.com/decidim/decidim"
21
21
  }
22
- s.required_ruby_version = "~> 3.2.0"
22
+ s.required_ruby_version = "~> 3.3.0"
23
23
 
24
24
  s.name = "decidim-blogs"
25
25
  s.summary = "Decidim blogs module"
@@ -6,14 +6,13 @@ module Decidim
6
6
  graphql_name "Blogs"
7
7
  description "A blogs component of a participatory space."
8
8
 
9
- field :posts, type: Decidim::Blogs::PostType.connection_type, description: "List all posts", connection: true, null: false do
10
- argument :order, Decidim::Blogs::PostInputSort, "Provides several methods to order the results", required: false
11
- argument :filter, Decidim::Blogs::PostInputFilter, "Provides several methods to filter the results", required: false
12
- end
13
-
14
9
  field :post, type: Decidim::Blogs::PostType, description: "Finds one post", null: true do
15
10
  argument :id, GraphQL::Types::ID, "The ID of the post", required: true
16
11
  end
12
+ field :posts, type: Decidim::Blogs::PostType.connection_type, description: "List all posts", connection: true, null: false do
13
+ argument :filter, Decidim::Blogs::PostInputFilter, "Provides several methods to filter the results", required: false
14
+ argument :order, Decidim::Blogs::PostInputSort, "Provides several methods to order the results", required: false
15
+ end
17
16
 
18
17
  def posts(filter: {}, order: {})
19
18
  base_query = Decidim::Core::ComponentListBase.new(model_class: Post).call(object, { filter:, order: }, context)
@@ -13,10 +13,10 @@ module Decidim
13
13
 
14
14
  description "A post"
15
15
 
16
- field :id, GraphQL::Types::ID, "The internal ID of this post", null: false
17
- field :title, Decidim::Core::TranslatedFieldType, "The title for this post", null: true
18
16
  field :body, Decidim::Core::TranslatedFieldType, "The body of this post", null: true
17
+ field :id, GraphQL::Types::ID, "The internal ID of this post", null: false
19
18
  field :published_at, Decidim::Core::DateTimeType, "The time this page was published", null: false
19
+ field :title, Decidim::Core::TranslatedFieldType, "The title for this post", null: true
20
20
 
21
21
  def self.authorized?(object, context)
22
22
  context[:post] = object
@@ -11,9 +11,15 @@ module Decidim
11
11
  paths["lib/tasks"] = nil
12
12
 
13
13
  routes do
14
- resources :posts do
14
+ resources :posts, except: [:destroy] do
15
15
  resources :attachment_collections, except: [:show]
16
16
  resources :attachments, except: [:show]
17
+ get :manage_trash, on: :collection
18
+
19
+ member do
20
+ patch :soft_delete
21
+ patch :restore
22
+ end
17
23
  end
18
24
  root to: "posts#index"
19
25
  end
@@ -23,6 +23,7 @@ Decidim.register_component(:blogs) do |component|
23
23
  settings.attribute :announcement, type: :text, translated: true, editor: true
24
24
  settings.attribute :comments_enabled, type: :boolean, default: true
25
25
  settings.attribute :comments_max_length, type: :integer, required: true
26
+ settings.attribute :creation_enabled_for_participants, type: :boolean, default: false
26
27
  end
27
28
 
28
29
  component.settings(:step) do |settings|
@@ -39,6 +40,32 @@ Decidim.register_component(:blogs) do |component|
39
40
  resource.searchable = true
40
41
  end
41
42
 
43
+ component.exports :posts do |exports|
44
+ exports.collection do |component_instance|
45
+ Decidim::Blogs::Post
46
+ .not_hidden
47
+ .published
48
+ .where(component: component_instance)
49
+ .includes(component: { participatory_space: :organization })
50
+ end
51
+
52
+ exports.include_in_open_data = true
53
+
54
+ exports.serializer Decidim::Blogs::PostSerializer
55
+ end
56
+
57
+ component.exports :post_comments do |exports|
58
+ exports.collection do |component_instance|
59
+ Decidim::Comments::Export.comments_for_resource(
60
+ Decidim::Blogs::Post, component_instance
61
+ ).includes(:author, :user_group, root_commentable: { component: { participatory_space: :organization } })
62
+ end
63
+
64
+ exports.include_in_open_data = true
65
+
66
+ exports.serializer Decidim::Comments::CommentSerializer
67
+ end
68
+
42
69
  component.seeds do |participatory_space|
43
70
  require "decidim/blogs/seeds"
44
71
 
@@ -14,7 +14,7 @@ module Decidim
14
14
  isolate_namespace Decidim::Blogs
15
15
 
16
16
  routes do
17
- resources :posts, only: [:index, :show]
17
+ resources :posts
18
18
  scope "/posts" do
19
19
  root to: "posts#index"
20
20
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Blogs
5
+ # This class serializes a Post so can be exported to CSV, JSON or other
6
+ # formats.
7
+ class PostSerializer < Decidim::Exporters::Serializer
8
+ include Decidim::ApplicationHelper
9
+ include Decidim::ResourceHelper
10
+
11
+ # Public: Initializes the serializer with a post.
12
+ def initialize(post)
13
+ @post = post
14
+ end
15
+
16
+ # Public: Exports a hash with the serialized data for this post.
17
+ def serialize
18
+ {
19
+ id: post.id,
20
+ author: {
21
+ **author_fields
22
+ },
23
+ title: post.title,
24
+ body: post.body,
25
+ created_at: post.created_at,
26
+ updated_at: post.updated_at,
27
+ published_at: post.published_at,
28
+ endorsements_count: post.endorsements_count,
29
+ comments_count: post.comments_count,
30
+ follows_count: post.follows_count,
31
+ participatory_space: {
32
+ id: post.participatory_space.id,
33
+ url: Decidim::ResourceLocatorPresenter.new(post.participatory_space).url
34
+ },
35
+ component: { id: component.id },
36
+ url:
37
+ }
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :post
43
+ alias resource post
44
+
45
+ def url
46
+ Decidim::ResourceLocatorPresenter.new(post).url
47
+ end
48
+
49
+ def author_fields
50
+ {
51
+ id: resource.author.id,
52
+ name: author_name(resource.author),
53
+ url: author_url(resource.author)
54
+ }
55
+ end
56
+
57
+ def author_name(author)
58
+ translated_attribute(author.name)
59
+ end
60
+
61
+ def author_url(author)
62
+ if author.respond_to?(:nickname)
63
+ profile_url(author) # is a Decidim::User or Decidim::UserGroup
64
+ else
65
+ root_url # is a Decidim::Organization
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Blogs
5
+ class SchemaOrgBlogPostingPostSerializer < Decidim::Exporters::Serializer
6
+ include Decidim::TranslationsHelper
7
+ include Decidim::SanitizeHelper
8
+ include ActionView::Helpers::UrlHelper
9
+
10
+ # Public: Initializes the serializer with a post.
11
+ def initialize(post)
12
+ @post = post
13
+ end
14
+
15
+ # Serializes a post for the Schema.org Event type
16
+ #
17
+ # @see https://schema.org/BlogPosting
18
+ # @see https://developers.google.com/search/docs/appearance/structured-data/article?hl=en
19
+ def serialize
20
+ attributes = {
21
+ "@context": "https://schema.org",
22
+ "@type": "BlogPosting",
23
+ headline: decidim_escape_translated(post.title),
24
+ author:
25
+ }
26
+
27
+ attributes = attributes.merge(image:) if post.photos.any?
28
+ attributes = attributes.merge(datePublished: published) if post.published?
29
+ attributes
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :post
35
+ alias resource post
36
+
37
+ def author
38
+ case post.author.class.name
39
+ when "Decidim::Organization"
40
+ author_organization
41
+ when "Decidim::UserGroup"
42
+ author_user_group
43
+ when "Decidim::User"
44
+ author_user
45
+ end
46
+ end
47
+
48
+ def author_user_group
49
+ {
50
+ "@type": "Organization",
51
+ name: post.author.name,
52
+ url: profile_url(post.author)
53
+ }
54
+ end
55
+
56
+ def author_organization
57
+ {
58
+ "@type": "Organization",
59
+ name: decidim_escape_translated(post.author.name),
60
+ url: EngineRouter.new("decidim", router_options).root_url
61
+ }
62
+ end
63
+
64
+ def author_user
65
+ {
66
+ "@type": "Person",
67
+ name: decidim_escape_translated(post.author.name),
68
+ url: profile_url(post.author)
69
+ }
70
+ end
71
+
72
+ def router_options = { host: post.organization.host }
73
+
74
+ def image = post.photos.map(&:thumbnail_url)
75
+
76
+ def published
77
+ post.published_at.iso8601
78
+ end
79
+ end
80
+ end
81
+ end
@@ -30,6 +30,14 @@ FactoryBot.define do
30
30
  }
31
31
  end
32
32
  end
33
+
34
+ trait :with_creation_enabled do
35
+ settings do
36
+ {
37
+ creation_enabled_for_participants: true
38
+ }
39
+ end
40
+ end
33
41
  end
34
42
 
35
43
  factory :post, class: "Decidim::Blogs::Post" do
@@ -41,10 +49,7 @@ FactoryBot.define do
41
49
  body { generate_localized_description(:blog_body, skip_injection:) }
42
50
  component { build(:post_component, skip_injection:) }
43
51
  author { build(:user, :confirmed, skip_injection:, organization: component.organization) }
44
-
45
- trait :published do
46
- published_at { 2.minutes.ago }
47
- end
52
+ deleted_at { nil }
48
53
 
49
54
  trait :with_endorsements do
50
55
  after :create do |post, evaluator|
@@ -4,7 +4,7 @@ module Decidim
4
4
  # This holds the decidim-pages version.
5
5
  module Blogs
6
6
  def self.version
7
- "0.29.4"
7
+ "0.30.0.rc1"
8
8
  end
9
9
  end
10
10
  end
data/lib/decidim/blogs.rb CHANGED
@@ -10,5 +10,7 @@ module Decidim
10
10
  # This namespace holds the logic of the `Blogs` component. This component
11
11
  # allows the admins to create a custom blog for a participatory process.
12
12
  module Blogs
13
+ autoload :PostSerializer, "decidim/blogs/post_serializer"
14
+ autoload :SchemaOrgBlogPostingPostSerializer, "decidim/blogs/schema_org_blog_posting_post_serializer"
13
15
  end
14
16
  end