Skip to content

Commit 1597fce

Browse files
authored
Feature/posts (#1050)
## Pull Request #1050: Feature/posts ### Overview - Base branch: main - Feature branch: feature/posts - Commits: 20 (all on August 19, 2025) - File changes: 28 files changed (+368 / −95) ### Key Highlights by File 1. .env.dev - Changed Elasticsearch port from 9201 to 9200. 2. app/builders/better_together/agreement_builder.rb - Removed logic that links agreements to existing Privacy Policy, Terms of Service, or Code of Conduct pages. 3. app/controllers/better_together/pages_controller.rb - Removed :author_ids from permitted parameters for page creation/updating. 4. app/models/better_together/page.rb - Added rich text support with `translates :content, backend: :action_text`. 5. app/models/better_together/post.rb - Added rich text content using Action Text. - Added cover image support with `attachable_cover_image`. - Automatically adds the post creator as an author on creation. - Enhanced search and indexing logic in `as_indexed_json`. - Retains slugging and validations. 6. app/models/concerns/better_together/attachments/images.rb - Cleaned up variants for image attachments (`optimized_jpeg`, `optimized_png`, etc.). 7. app/models/concerns/better_together/authorable.rb - Extended permitted parameters to include `author_ids` for posts. 8. app/models/concerns/better_together/publishable.rb - Added `published_at` to permitted attributes. - Updated `published?` and `scheduled?` methods to use `DateTime.current`. 9. app/views/better_together/posts/_empty.html.erb - New view partial: shows placeholder card when no posts exist. - Includes link to create a post if permitted. 10. app/views/better_together/posts/_form.html.erb - Enhanced form with: - Multiple authors (`author_ids`). - `published_at` datetime field. - Rich content editor (`translated_rich_text_field`). - Cover image attachment field. 11. app/views/better_together/posts/_post.html.erb - Post card updates: - Multi-author rendering via shared authors partial. - Displays publish date or “Unpublished” status. 12. app/views/better_together/posts/index.html.erb - Updated rendering to show either post cards or empty partial using `render(@posts) || render('empty')`. ### Summary This pull request introduces a posts system into the Community Engine: - Rich text content with Action Text - Cover image attachments with optimized variants - Multi-author support and automatic author attribution - Publish scheduling with `published_at` - Enhanced searchability and indexing - UI improvements for empty states, author display, and post listings ### Next Steps / Considerations - Confirm image variant definitions are correct and performant. - Review UX for post editor (rich text and cover upload). - Confirm permissions for setting `published_at` and managing authors. - Test search indexing to ensure updates propagate correctly.
2 parents 3af1e92 + 9651b05 commit 1597fce

File tree

29 files changed

+406
-95
lines changed

29 files changed

+406
-95
lines changed

.env.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ APP_DEFAULT_LOCALE='en'
99
APP_AVAILABLE_LOCALES='es,en,fr'
1010
APP_FALLBACK_LOCALES='es,en,fr'
1111

12-
ES_PORT='9201'
12+
ES_PORT='9200'
1313
ES_HOST='http://elasticsearch'
1414
# ELASTICSEARCH_URL='http://elasticsearch:9201'
1515

app/builders/better_together/agreement_builder.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def build_privacy_policy # rubocop:todo Metrics/MethodLength, Metrics/AbcSize
3636
# If a Page exists for the privacy policy, link it so the page content
3737
# is shown to users instead of the agreement terms.
3838
page = BetterTogether::Page.find_by(identifier: 'privacy_policy') ||
39-
BetterTogether::Page.find_by(slug: 'privacy-policy')
39+
BetterTogether::Page.friendly.find('privacy-policy')
4040
agreement.update!(page: page) if page.present?
4141
end
4242
# rubocop:enable Metrics/AbcSize
@@ -58,7 +58,7 @@ def build_terms_of_service # rubocop:todo Metrics/MethodLength, Metrics/AbcSize
5858

5959
# Link a Terms of Service Page if one exists
6060
page = BetterTogether::Page.find_by(identifier: 'terms_of_service') ||
61-
BetterTogether::Page.find_by(slug: 'terms-of-service')
61+
BetterTogether::Page.friendly.find('terms-of-service')
6262
agreement.update!(page: page) if page.present?
6363
end
6464
# rubocop:enable Metrics/AbcSize
@@ -80,7 +80,7 @@ def build_code_of_conduct # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
8080

8181
# Link a Code of Conduct Page if one exists
8282
page = BetterTogether::Page.find_by(identifier: 'code_of_conduct') ||
83-
BetterTogether::Page.find_by(slug: 'code-of-conduct')
83+
BetterTogether::Page.friendly.find('code-of-conduct')
8484
agreement.update!(page: page) if page.present?
8585
end
8686
# rubocop:enable Metrics/MethodLength

app/controllers/better_together/pages_controller.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ def page_params # rubocop:todo Metrics/MethodLength
147147
:meta_description, :keywords, :published_at, :sidebar_nav_id,
148148
:privacy, :layout, :template, *Page.localized_attribute_list,
149149
*Page.extra_permitted_attributes,
150-
author_ids: [],
151150
page_blocks_attributes: [
152151
:id, :position, :_destroy,
153152
{

app/models/better_together/page.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ class Page < ApplicationRecord
99
# to stamp newly-created authorships with the acting person.
1010
include Categorizable
1111
include Identifier
12+
include Metrics::Viewable
1213
include Protected
1314
include Privacy
15+
include Publishable
1416
include Searchable
1517
include TrackedActivity
16-
include Metrics::Viewable
1718

1819
categorizable
1920

@@ -35,6 +36,8 @@ class Page < ApplicationRecord
3536
accepts_nested_attributes_for :page_blocks, allow_destroy: true
3637

3738
translates :title, type: :string
39+
alias name title
40+
3841
translates :content, backend: :action_text
3942

4043
settings index: default_elasticsearch_index
@@ -78,11 +81,6 @@ def as_indexed_json(_options = {}) # rubocop:todo Metrics/MethodLength
7881
)
7982
end
8083

81-
# Needed for elasticsearch results to work properly (April 22, 2025)
82-
def name
83-
title
84-
end
85-
8684
def primary_image
8785
hero_block&.background_image_file
8886
end

app/models/better_together/post.rb

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,28 @@
33
module BetterTogether
44
# Represents a blog post
55
class Post < ApplicationRecord
6+
include Attachments::Images
67
include Authorable
78
include BlockFilterable
89
include FriendlySlug
910
include Categorizable
1011
include Creatable
1112
include Identifier
13+
include Metrics::Viewable
1214
include Privacy
1315
include Publishable
16+
include Searchable
17+
include TrackedActivity
18+
19+
attachable_cover_image
1420

1521
categorizable
1622

1723
translates :title
18-
translates :content, type: :text
19-
# translates :content_html, type: :action_text
24+
alias name title
25+
translates :content, backend: :action_text
26+
27+
settings index: default_elasticsearch_index
2028

2129
slugged :title
2230

@@ -26,8 +34,31 @@ class Post < ApplicationRecord
2634
validates :content,
2735
presence: true
2836

37+
# Automatically grant the post creator an authorship record
38+
after_create :add_creator_as_author
39+
2940
def to_s
3041
title
3142
end
43+
44+
configure_attachment_cleanup
45+
46+
# Customize the data sent to Elasticsearch for indexing
47+
def as_indexed_json(_options = {})
48+
as_json(
49+
only: [:id],
50+
methods: [:title, :name, :slug, *self.class.localized_attribute_list.keep_if do |a|
51+
a.starts_with?('title' || a.starts_with?('slug') || a.starts_with?('content'))
52+
end]
53+
)
54+
end
55+
56+
private
57+
58+
def add_creator_as_author
59+
return unless respond_to?(:creator_id) && creator_id.present?
60+
61+
authorships.find_or_create_by(author_id: creator_id)
62+
end
3263
end
3364
end

app/models/concerns/better_together/attachments/images.rb

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,11 @@ module Images
1313
included do
1414
def self.attachable_cover_image # rubocop:todo Metrics/MethodLength
1515
has_one_attached :cover_image do |attachable|
16-
attachable.variant :optimized_jpeg, resize_to_limit: [2400, 1200],
17-
# rubocop:todo Layout/LineLength
18-
saver: { strip: true, quality: 85, interlace: true, optimize_coding: true, trellis_quant: true, quant_table: 3 }, format: 'jpg'
19-
# rubocop:enable Layout/LineLength
20-
attachable.variant :optimized_png, resize_to_limit: [2400, 1200],
21-
saver: { strip: true, quality: 85, optimize_coding: true }, format: 'png'
16+
attachable.variant :optimized_jpeg, resize_to_limit: [2400, 1200]
17+
attachable.variant :optimized_png, resize_to_limit: [2400, 1200]
2218

23-
attachable.variant :optimized_card_jpeg, resize_to_limit: [1200, 300],
24-
# rubocop:todo Layout/LineLength
25-
saver: { strip: true, quality: 90, interlace: true, optimize_coding: true, trellis_quant: true, quant_table: 3 }, format: 'jpg'
26-
# rubocop:enable Layout/LineLength
27-
attachable.variant :optimized_card_png, resize_to_limit: [1200, 300],
28-
# rubocop:todo Layout/LineLength
29-
saver: { strip: true, quality: 90, optimize_coding: true }, format: 'png'
30-
# rubocop:enable Layout/LineLength
19+
attachable.variant :optimized_card_jpeg, resize_to_limit: [1200, 300]
20+
attachable.variant :optimized_card_png, resize_to_limit: [1200, 300]
3121
end
3222

3323
validates :cover_image,

app/models/concerns/better_together/authorable.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,15 @@ module Authorable
1212
has_many :authors,
1313
through: :authorships
1414
end
15+
16+
class_methods do
17+
def extra_permitted_attributes
18+
super + [
19+
{
20+
author_ids: []
21+
}
22+
]
23+
end
24+
end
1525
end
1626
end

app/models/concerns/better_together/publishable.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,24 @@ def draft?
2323
end
2424

2525
def published?
26+
return false if published_at.nil?
27+
2628
published_at <= DateTime.current
2729
end
2830

2931
def scheduled?
32+
return false if published_at.nil?
33+
3034
published_at >= DateTime.current
3135
end
3236
end
37+
38+
class_methods do
39+
def extra_permitted_attributes
40+
super + [
41+
:published_at
42+
]
43+
end
44+
end
3345
end
3446
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<div class="card mb-4 text-center w-100 py-4">
2+
<div class="card-body">
3+
<h3 class="card-title"> <%= t('better_together.posts.none_title', default: 'No posts yet') %> </h3>
4+
<p class="card-text text-muted mb-3"><%= t('better_together.posts.none_body', default: "There are no posts to show right now.") %></p>
5+
6+
<div>
7+
<% if policy(BetterTogether::Post).create? %>
8+
<%= link_to t('better_together.posts.create_post', default: 'Create the first post'), new_post_path, class: 'btn btn-primary' %>
9+
<% end %>
10+
</div>
11+
</div>
12+
</div>

app/views/better_together/posts/_form.html.erb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,24 @@
4848
<small class="form-text text-muted"><%= t('hints.categories.select_multiple') %></small>
4949
</div>
5050

51+
<div class="mb-3">
52+
<%= form.label :author_ids, t('better_together.posts.authors', default: 'Authors') %>
53+
<%= form.collection_select :author_ids,
54+
BetterTogether::Person.all,
55+
:id,
56+
:select_option_title,
57+
{},
58+
{ multiple: true,
59+
class: "form-select#{' is-invalid' if post.errors[:author_ids].any?}",
60+
data: { controller: 'better_together--slim-select' } } %>
61+
<% if post.errors[:author_ids].any? %>
62+
<div class="invalid-feedback">
63+
<%= post.errors[:author_ids].join(', ') %>
64+
</div>
65+
<% end %>
66+
<small class="form-text text-muted"><%= t('hints.authors.select_multiple', default: 'Select one or more authors') %></small>
67+
</div>
68+
5169
<div class="mb-3">
5270
<%= form.label :published_at %>
5371
<%= form.datetime_field :published_at, include_seconds: false, class: 'form-control' %>
@@ -57,5 +75,9 @@
5775
<%= render partial: 'better_together/shared/translated_rich_text_field', locals: { model: post, form: form, attribute: 'content' } %>
5876
</div>
5977

78+
<div class="mb-3">
79+
<%= render 'better_together/shared/fields/attachments/cover_image', form: form %>
80+
</div>
81+
6082
<%= yield :resource_toolbar %>
6183
<% end %>

0 commit comments

Comments
 (0)