helios-press 0.4 → 0.5
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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +49 -1
- data/app/controllers/helios/press/admin/posts_controller.rb +1 -1
- data/app/models/helios/press/post.rb +33 -0
- data/app/views/helios/press/admin/posts/_form.html.erb +7 -1
- data/app/views/helios/press/posts/show.html.erb +3 -20
- data/app/views/layouts/helios/press/application.html.erb +1 -1
- data/lib/helios/press/configuration.rb +7 -1
- data/lib/helios/press/engine.rb +18 -0
- data/lib/helios/press/version.rb +1 -1
- metadata +18 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 81300e27ac6528a2ed98720628689300cb0be7257061154bbecc6ecfb8734ca6
|
|
4
|
+
data.tar.gz: 145396036b9ce29b1ea7eb700e3a8af95f0d33329852c8f79ac375b913a0df50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 469105b200430a999c911d10e90744e2936824e5224079cb91fe97fb4753599f398ba197a6bbfe232fcf7e52784b519904b62e9b71799d0d48770950100a77e8
|
|
7
|
+
data.tar.gz: ed7b189d44a41125b0fa0fe3d44c59c4912ffbf48b20cee7a0f6b1e49c6fd924e191d0ad38020737435982d9721e7a8fed239e16b80ad468dab983dffd13f707
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -38,6 +38,11 @@ Helios::Press.configure do |config|
|
|
|
38
38
|
# Optional slug prefix for posts
|
|
39
39
|
config.post_slug_prefix = nil # e.g., "blog/" for /blog/my-post
|
|
40
40
|
|
|
41
|
+
# SEO / social metadata
|
|
42
|
+
config.site_name = "My Blog" # og:site_name and <title> fallback
|
|
43
|
+
config.twitter_site = "@myhandle" # twitter:site card handle
|
|
44
|
+
config.default_image_url = nil # fallback og:image when a post has no image
|
|
45
|
+
|
|
41
46
|
# API authentication (for external post ingestion)
|
|
42
47
|
# Default: checks X-API-Key header against BLOG_INGEST_API_KEY env var
|
|
43
48
|
config.api_authentication = ->(controller) {
|
|
@@ -50,6 +55,34 @@ Helios::Press.configure do |config|
|
|
|
50
55
|
end
|
|
51
56
|
```
|
|
52
57
|
|
|
58
|
+
## SEO (helios-seo)
|
|
59
|
+
|
|
60
|
+
helios-press delegates the entire page `<head>` — meta description, canonical,
|
|
61
|
+
robots, Open Graph, Twitter cards, and the JSON-LD graph — to
|
|
62
|
+
[helios-seo](../helios-seo), which it depends on. Press only supplies the
|
|
63
|
+
per-post share image; the post `show` view calls
|
|
64
|
+
`helios_seo_tags(@post, title: false)` (the layout keeps ownership of `<title>`).
|
|
65
|
+
|
|
66
|
+
Integration checklist:
|
|
67
|
+
|
|
68
|
+
1. **Configure helios-seo** in `config/initializers/helios_seo.rb` — set
|
|
69
|
+
`site_name`, `site_url`, `author`, `twitter_site` / `twitter_creator`, and
|
|
70
|
+
`default_og_image`. These now live in helios-seo; the old
|
|
71
|
+
`Helios::Press` SEO settings (`twitter_site`, `default_image_url`) are
|
|
72
|
+
superseded. (`config.site_name` on Press is still used as the layout
|
|
73
|
+
`<title>` fallback.)
|
|
74
|
+
2. **Layout** — make sure your layout yields `:head` inside `<head>`. The
|
|
75
|
+
bundled Press layout already does.
|
|
76
|
+
3. **Per-post share image** — authors can upload a "Social share image" in the
|
|
77
|
+
post form to override; otherwise the post's banner (its first image block) is
|
|
78
|
+
used. Falls back to helios-seo's site-wide `default_og_image`. Requires
|
|
79
|
+
ActiveStorage (already used by Press image blocks).
|
|
80
|
+
4. **Canonical URLs** default to `"{site_url}/{slug}"`, which matches the public
|
|
81
|
+
`get ":slug"` route when the Public engine is mounted at root. On a subpath
|
|
82
|
+
mount, set `config.canonical_url` in the helios-seo initializer.
|
|
83
|
+
5. **`/llms.txt`** (optional) — mount `Helios::Seo::Engine` at root to serve it;
|
|
84
|
+
set `config.llms_posts = -> { Helios::Press::Post.published.reverse_sorted }`.
|
|
85
|
+
|
|
53
86
|
## Routes
|
|
54
87
|
|
|
55
88
|
Helios::Press provides three independent engines that you can mount wherever you want:
|
|
@@ -185,6 +218,21 @@ Two modes are supported:
|
|
|
185
218
|
|
|
186
219
|
Images are stored as `BlockImage` records with ActiveStorage attachments and served via `rails_storage_proxy_path`. Failed downloads are logged and skipped (soft failure — the original src is preserved).
|
|
187
220
|
|
|
221
|
+
## Releases
|
|
222
|
+
|
|
223
|
+
### 0.5
|
|
224
|
+
|
|
225
|
+
- SEO delegated to [helios-seo](../helios-seo) (new dependency). The post `show`
|
|
226
|
+
view now renders `helios_seo_tags(@post, title: false)` instead of the bundled
|
|
227
|
+
`_meta_tags` partial, adding a meta description, `robots`, and a JSON-LD graph
|
|
228
|
+
on top of the previous Open Graph / Twitter tags.
|
|
229
|
+
- Removed `app/views/helios/press/posts/_meta_tags.html.erb` and
|
|
230
|
+
`app/helpers/helios/press/seo_helper.rb` (superseded by helios-seo).
|
|
231
|
+
- `Post` gains an optional `og_image` attachment (a "Social share image" field in
|
|
232
|
+
the admin form) plus `Post#og_image_url`, which builds a stable, absolute
|
|
233
|
+
ActiveStorage proxy URL for the post's share image, falling back to the post's
|
|
234
|
+
banner (first image block). See the [SEO](#seo-helios-seo) section.
|
|
235
|
+
|
|
188
236
|
## License
|
|
189
237
|
|
|
190
|
-
|
|
238
|
+
MIT License. See [MIT-LICENSE](MIT-LICENSE) for details.
|
|
@@ -3,6 +3,10 @@ module Helios
|
|
|
3
3
|
class Post < ActiveRecord::Base
|
|
4
4
|
self.table_name = "helios_press_posts"
|
|
5
5
|
|
|
6
|
+
# Optional explicit social-share image. When set it overrides the banner;
|
|
7
|
+
# otherwise the post's banner (first image block) is used. See #og_image_url.
|
|
8
|
+
has_one_attached :og_image
|
|
9
|
+
|
|
6
10
|
has_many :blocks, -> { order(position: :asc) },
|
|
7
11
|
class_name: "Helios::Press::Block",
|
|
8
12
|
foreign_key: :post_id,
|
|
@@ -18,8 +22,37 @@ module Helios
|
|
|
18
22
|
scope :published, -> { where(published: true) }
|
|
19
23
|
scope :reverse_sorted, -> { order(updated_at: :desc) }
|
|
20
24
|
|
|
25
|
+
# Absolute URL for the post's social-share image, sized to the OG-
|
|
26
|
+
# recommended 1200x630. Prefers the explicit `og_image` override, then
|
|
27
|
+
# falls back to the post's banner (the first attached image block). Returns
|
|
28
|
+
# nil when neither exists, so helios-seo can fall back to its site-wide
|
|
29
|
+
# default.
|
|
30
|
+
#
|
|
31
|
+
# `view` is the ActionView context helios-seo passes into the resolver. We
|
|
32
|
+
# use `view.polymorphic_url` so the result is the stable ActiveStorage
|
|
33
|
+
# proxy/redirect URL (per the app's resolve_model_to_route) — absolute,
|
|
34
|
+
# using the request host — NOT the short-lived signed service URL that
|
|
35
|
+
# `.url` returns. Degrades to nil on any failure (e.g. no view context).
|
|
36
|
+
def og_image_url(view: nil)
|
|
37
|
+
attachment = og_image.attached? ? og_image : banner_image
|
|
38
|
+
return nil unless attachment && view
|
|
39
|
+
|
|
40
|
+
variant = attachment.variant(resize_to_limit: [ 1200, 630 ])
|
|
41
|
+
view.polymorphic_url(variant)
|
|
42
|
+
rescue StandardError
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
|
|
21
46
|
private
|
|
22
47
|
|
|
48
|
+
# The post's banner: the first attached image in the first image block.
|
|
49
|
+
def banner_image
|
|
50
|
+
image_block = blocks.detect do |block|
|
|
51
|
+
block.block_type == "image_container" && block.block_images.any?
|
|
52
|
+
end
|
|
53
|
+
image_block&.block_images&.detect { |bi| bi.file.attached? }&.file
|
|
54
|
+
end
|
|
55
|
+
|
|
23
56
|
def generate_slug
|
|
24
57
|
prefix = Helios::Press.configuration.post_slug_prefix
|
|
25
58
|
base_slug = name.parameterize
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<%= form_with(model: post, url: post.persisted? ? helios_press_admin.post_path(post) : helios_press_admin.posts_path, local: true) do |form| %>
|
|
1
|
+
<%= form_with(model: post, url: post.persisted? ? helios_press_admin.post_path(post) : helios_press_admin.posts_path, local: true, multipart: true) do |form| %>
|
|
2
2
|
<% if post.errors.any? %>
|
|
3
3
|
<div class="alert alert-danger">
|
|
4
4
|
<h4><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h4>
|
|
@@ -31,6 +31,12 @@
|
|
|
31
31
|
<%= form.text_field :keywords, class: "form-control", placeholder: "Separate keywords with commas" %>
|
|
32
32
|
</div>
|
|
33
33
|
|
|
34
|
+
<div class="mb-3">
|
|
35
|
+
<%= form.label :og_image, "Social share image", class: "form-label" %>
|
|
36
|
+
<%= form.file_field :og_image, class: "form-control", accept: "image/*" %>
|
|
37
|
+
<div class="form-text">Optional. Overrides the post's banner image for social/link previews.</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
34
40
|
<div class="mb-3">
|
|
35
41
|
<div class="form-check">
|
|
36
42
|
<%= form.check_box :published, class: "form-check-input" %>
|
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
<% content_for :title, @post.name %>
|
|
2
2
|
|
|
3
|
+
<%# helios-seo renders the full head layer: description, canonical, robots, OG,
|
|
4
|
+
Twitter, and JSON-LD. title: false because the layout owns the <title>. %>
|
|
3
5
|
<% content_for :head do %>
|
|
4
|
-
|
|
5
|
-
first_image_block = @post.blocks.find { |b| b.block_type == "image_container" && b.block_images.any? }
|
|
6
|
-
%>
|
|
7
|
-
<% if @post.keywords.present? %>
|
|
8
|
-
<meta name="keywords" content="<%= @post.keywords %>">
|
|
9
|
-
<% end %>
|
|
10
|
-
|
|
11
|
-
<meta property="og:title" content="<%= @post.name %>">
|
|
12
|
-
<meta property="og:type" content="article">
|
|
13
|
-
<meta property="og:url" content="<%= request.base_url %>/<%= @post.slug %>">
|
|
14
|
-
<% if @post.description.present? %>
|
|
15
|
-
<meta property="og:description" content="<%= @post.description %>">
|
|
16
|
-
<% end %>
|
|
17
|
-
<% if first_image_block&.block_images&.first&.file&.attached? %>
|
|
18
|
-
<meta property="og:image" content="<%= url_for(first_image_block.block_images.first.file.variant(resize_to_limit: [1200, 630])) %>">
|
|
19
|
-
<% end %>
|
|
20
|
-
<% if @post.published? %>
|
|
21
|
-
<meta property="article:published_time" content="<%= @post.created_at.iso8601 %>">
|
|
22
|
-
<meta property="article:modified_time" content="<%= @post.updated_at.iso8601 %>">
|
|
23
|
-
<% end %>
|
|
6
|
+
<%= helios_seo_tags(@post, title: false) %>
|
|
24
7
|
<% end %>
|
|
25
8
|
|
|
26
9
|
<div class="container my-5">
|
|
@@ -5,7 +5,10 @@ module Helios
|
|
|
5
5
|
:public_parent_controller,
|
|
6
6
|
:api_parent_controller,
|
|
7
7
|
:api_authentication,
|
|
8
|
-
:post_slug_prefix
|
|
8
|
+
:post_slug_prefix,
|
|
9
|
+
:site_name,
|
|
10
|
+
:twitter_site,
|
|
11
|
+
:default_image_url
|
|
9
12
|
|
|
10
13
|
def initialize
|
|
11
14
|
@admin_parent_controller = "ApplicationController"
|
|
@@ -13,6 +16,9 @@ module Helios
|
|
|
13
16
|
@api_parent_controller = "ActionController::API"
|
|
14
17
|
@api_authentication = nil
|
|
15
18
|
@post_slug_prefix = nil
|
|
19
|
+
@site_name = nil
|
|
20
|
+
@twitter_site = nil
|
|
21
|
+
@default_image_url = nil
|
|
16
22
|
end
|
|
17
23
|
end
|
|
18
24
|
end
|
data/lib/helios/press/engine.rb
CHANGED
|
@@ -12,6 +12,24 @@ module Helios
|
|
|
12
12
|
helper Rails.application.routes.url_helpers
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
# Adapt Post to helios-seo: supply the per-post social-share image. The
|
|
17
|
+
# host app still provides site identity (site_name, site_url, author, ...)
|
|
18
|
+
# and may override this resolver. Canonical URLs are left to helios-seo's
|
|
19
|
+
# default ("{site_url}/{slug}"), which matches the public "get :slug" route
|
|
20
|
+
# when the Public engine is mounted at root; hosts on a subpath set their
|
|
21
|
+
# own `canonical_url`.
|
|
22
|
+
initializer "helios_press.seo" do
|
|
23
|
+
if defined?(Helios::Seo)
|
|
24
|
+
Helios::Seo.configure do |seo|
|
|
25
|
+
# Two-arg form: helios-seo passes the view context so we can build an
|
|
26
|
+
# absolute ActiveStorage proxy URL for the share image.
|
|
27
|
+
seo.image_resolver = ->(post, view) do
|
|
28
|
+
post.respond_to?(:og_image_url) ? post.og_image_url(view: view) : nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
15
33
|
end
|
|
16
34
|
|
|
17
35
|
module Admin
|
data/lib/helios/press/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: helios-press
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: '0.
|
|
4
|
+
version: '0.5'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jason Fleetwood-Boldt
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -80,6 +79,20 @@ dependencies:
|
|
|
80
79
|
- - ">="
|
|
81
80
|
- !ruby/object:Gem::Version
|
|
82
81
|
version: '0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: helios-seo
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
83
96
|
description: A Rails engine providing a WordPress-like block editor for blog posts.
|
|
84
97
|
Supports text (ActionText), image stacks, and video blocks with drag-to-reorder.
|
|
85
98
|
email:
|
|
@@ -146,11 +159,10 @@ files:
|
|
|
146
159
|
- lib/tasks/helios/press_tasks.rake
|
|
147
160
|
homepage: https://github.com/heliosdev-shop/helios-press
|
|
148
161
|
licenses:
|
|
149
|
-
-
|
|
162
|
+
- MIT
|
|
150
163
|
metadata:
|
|
151
164
|
homepage_uri: https://github.com/heliosdev-shop/helios-press
|
|
152
165
|
source_code_uri: https://github.com/heliosdev-shop/helios-press
|
|
153
|
-
post_install_message:
|
|
154
166
|
rdoc_options: []
|
|
155
167
|
require_paths:
|
|
156
168
|
- lib
|
|
@@ -165,8 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
165
177
|
- !ruby/object:Gem::Version
|
|
166
178
|
version: '0'
|
|
167
179
|
requirements: []
|
|
168
|
-
rubygems_version: 3.
|
|
169
|
-
signing_key:
|
|
180
|
+
rubygems_version: 3.6.9
|
|
170
181
|
specification_version: 4
|
|
171
182
|
summary: Multi-block blog post editor for Rails with drag-to-reorder
|
|
172
183
|
test_files: []
|