helios-press 0.1.0

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +122 -0
  4. data/Rakefile +3 -0
  5. data/app/assets/stylesheets/helios/press/application.css +15 -0
  6. data/app/assets/stylesheets/helios/press/blocks.css +231 -0
  7. data/app/controllers/helios/press/admin/base_controller.rb +9 -0
  8. data/app/controllers/helios/press/admin/block_images_controller.rb +75 -0
  9. data/app/controllers/helios/press/admin/blocks_controller.rb +103 -0
  10. data/app/controllers/helios/press/admin/posts_controller.rb +72 -0
  11. data/app/controllers/helios/press/api/base_controller.rb +25 -0
  12. data/app/controllers/helios/press/api/posts_controller.rb +54 -0
  13. data/app/controllers/helios/press/application_controller.rb +6 -0
  14. data/app/controllers/helios/press/posts_controller.rb +9 -0
  15. data/app/helpers/helios/press/application_helper.rb +39 -0
  16. data/app/javascript/helios/press/controllers/blocks_controller.js +276 -0
  17. data/app/javascript/helios/press/controllers/image_block_controller.js +178 -0
  18. data/app/javascript/helios/press/controllers/text_block_controller.js +63 -0
  19. data/app/javascript/helios/press/index.js +6 -0
  20. data/app/jobs/helios/press/application_job.rb +6 -0
  21. data/app/mailers/helios/press/application_mailer.rb +8 -0
  22. data/app/models/helios/press/application_record.rb +7 -0
  23. data/app/models/helios/press/block.rb +34 -0
  24. data/app/models/helios/press/block_image.rb +16 -0
  25. data/app/models/helios/press/post.rb +39 -0
  26. data/app/views/helios/press/admin/blocks/_block.html.erb +25 -0
  27. data/app/views/helios/press/admin/blocks/_image_block.html.erb +47 -0
  28. data/app/views/helios/press/admin/blocks/_image_item.html.erb +38 -0
  29. data/app/views/helios/press/admin/blocks/_text_block.html.erb +39 -0
  30. data/app/views/helios/press/admin/blocks/_video_block.html.erb +43 -0
  31. data/app/views/helios/press/admin/posts/_blocks_container.html.erb +27 -0
  32. data/app/views/helios/press/admin/posts/_form.html.erb +48 -0
  33. data/app/views/helios/press/admin/posts/edit.html.erb +26 -0
  34. data/app/views/helios/press/admin/posts/index.html.erb +31 -0
  35. data/app/views/helios/press/admin/posts/new.html.erb +12 -0
  36. data/app/views/helios/press/posts/blocks/_image_container.html.erb +18 -0
  37. data/app/views/helios/press/posts/blocks/_text.html.erb +3 -0
  38. data/app/views/helios/press/posts/blocks/_video_container.html.erb +12 -0
  39. data/app/views/helios/press/posts/show.html.erb +45 -0
  40. data/app/views/layouts/helios/press/admin.html.erb +1 -0
  41. data/app/views/layouts/helios/press/application.html.erb +17 -0
  42. data/config/routes.rb +21 -0
  43. data/db/migrate/20250510000001_create_helios_press_posts.rb +18 -0
  44. data/db/migrate/20250510000002_create_helios_press_blocks.rb +14 -0
  45. data/db/migrate/20250510000003_create_helios_press_block_images.rb +13 -0
  46. data/lib/helios/press/configuration.rb +17 -0
  47. data/lib/helios/press/engine.rb +11 -0
  48. data/lib/helios/press/version.rb +5 -0
  49. data/lib/helios/press.rb +23 -0
  50. data/lib/tasks/helios/press_tasks.rake +4 -0
  51. metadata +148 -0
@@ -0,0 +1,16 @@
1
+ module Helios
2
+ module Press
3
+ class BlockImage < ActiveRecord::Base
4
+ self.table_name = "helios_press_block_images"
5
+
6
+ belongs_to :block, class_name: "Helios::Press::Block", touch: true
7
+
8
+ has_one_attached :file
9
+
10
+ validates :position, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
11
+ validates :file, presence: true
12
+
13
+ acts_as_list scope: :block
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ module Helios
2
+ module Press
3
+ class Post < ActiveRecord::Base
4
+ self.table_name = "helios_press_posts"
5
+
6
+ has_many :blocks, -> { order(position: :asc) },
7
+ class_name: "Helios::Press::Block",
8
+ foreign_key: :post_id,
9
+ dependent: :destroy
10
+
11
+ accepts_nested_attributes_for :blocks, allow_destroy: true
12
+
13
+ validates :name, presence: true
14
+ validates :slug, presence: true, uniqueness: true
15
+
16
+ before_validation :generate_slug, if: -> { name.present? && (slug.blank? || name_changed?) }
17
+
18
+ scope :published, -> { where(published: true) }
19
+ scope :reverse_sorted, -> { order(updated_at: :desc) }
20
+
21
+ private
22
+
23
+ def generate_slug
24
+ prefix = Helios::Press.configuration.post_slug_prefix
25
+ base_slug = name.parameterize
26
+ base_slug = "#{prefix}#{base_slug}" if prefix.present?
27
+ candidate_slug = base_slug
28
+ counter = 1
29
+
30
+ while self.class.where(slug: candidate_slug).where.not(id: id).exists?
31
+ candidate_slug = "#{base_slug}-#{counter}"
32
+ counter += 1
33
+ end
34
+
35
+ self.slug = candidate_slug
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ <div id="block-<%= block.id %>"
2
+ class="content-block"
3
+ data-helios-press-blocks-target="block"
4
+ data-block-id="<%= block.id %>">
5
+
6
+ <!-- Drag handle in left margin -->
7
+ <div class="drag-handle">
8
+ <i class="bi bi-grip-vertical"></i>
9
+ </div>
10
+
11
+ <div class="block-content">
12
+ <% case block.block_type %>
13
+ <% when "text" %>
14
+ <%= render "helios/press/admin/blocks/text_block", block: block, post: post %>
15
+ <% when "image_container" %>
16
+ <%= render "helios/press/admin/blocks/image_block", block: block, post: post %>
17
+ <% when "video_container" %>
18
+ <% if Helios::Press.videos_enabled? %>
19
+ <%= render "helios/press/admin/blocks/video_block", block: block, post: post %>
20
+ <% else %>
21
+ <div class="alert alert-warning mb-0">Video blocks require the helios-videos gem.</div>
22
+ <% end %>
23
+ <% end %>
24
+ </div>
25
+ </div>
@@ -0,0 +1,47 @@
1
+ <div data-controller="helios-press-image-block"
2
+ data-helios-press-image-block-id-value="<%= block.id %>"
3
+ data-helios-press-image-block-post-id-value="<%= post.id %>"
4
+ data-helios-press-image-block-base-url-value="<%= helios_press.admin_post_block_images_path(post, block) %>"
5
+ data-helios-press-image-block-block-url-value="<%= helios_press.admin_post_block_path(post, block) %>"
6
+ class="image-block-container">
7
+
8
+ <!-- Block Controls -->
9
+ <div class="d-flex justify-content-between align-items-center mb-2">
10
+ <div class="d-flex align-items-center gap-2">
11
+ <label class="form-label mb-0 small">Images per row:</label>
12
+ <select class="form-select form-select-sm"
13
+ style="width: auto;"
14
+ data-action="change->helios-press-image-block#updateColumns"
15
+ data-helios-press-image-block-target="columnsSelect">
16
+ <% (1..6).each do |n| %>
17
+ <option value="<%= n %>" <%= "selected" if (block.columns || 3) == n %>><%= n %></option>
18
+ <% end %>
19
+ </select>
20
+ </div>
21
+ <button type="button"
22
+ class="btn btn-sm btn-danger"
23
+ data-action="click->helios-press-blocks#deleteBlock">
24
+ Delete Block
25
+ </button>
26
+ </div>
27
+
28
+ <!-- Images Grid -->
29
+ <div class="images-grid"
30
+ data-helios-press-image-block-target="grid"
31
+ data-columns="<%= block.columns || 3 %>"
32
+ style="--grid-columns: <%= block.columns || 3 %>;">
33
+ <% block.block_images.each do |block_image| %>
34
+ <%= render "helios/press/admin/blocks/image_item", block_image: block_image, block: block, post: post %>
35
+ <% end %>
36
+
37
+ <!-- Dropzone as a grid item -->
38
+ <div class="image-dropzone"
39
+ data-helios-press-image-block-target="dropzone"
40
+ data-action="dragover->helios-press-image-block#handleDragOver
41
+ dragleave->helios-press-image-block#handleDragLeave
42
+ drop->helios-press-image-block#handleDrop">
43
+ <i class="bi bi-plus-circle"></i>
44
+ <p>Drop to add</p>
45
+ </div>
46
+ </div>
47
+ </div>
@@ -0,0 +1,38 @@
1
+ <div class="image-item"
2
+ data-helios-press-image-block-target="image"
3
+ data-image-id="<%= block_image.id %>">
4
+
5
+ <!-- Image -->
6
+ <div class="image-wrapper">
7
+ <% if block_image.file.attached? %>
8
+ <%= image_tag block_image.file.variant(resize_to_limit: [800, 800]), class: "block-image" %>
9
+ <% end %>
10
+
11
+ <!-- Delete button -->
12
+ <button type="button"
13
+ class="btn btn-sm btn-danger image-delete"
14
+ data-action="click->helios-press-image-block#deleteImage">
15
+ <i class="bi bi-trash"></i>
16
+ </button>
17
+ </div>
18
+
19
+ <!-- Caption -->
20
+ <div class="image-caption">
21
+ <!-- View Mode -->
22
+ <div class="caption-view"
23
+ data-image-id="<%= block_image.id %>"
24
+ data-action="click->helios-press-image-block#editCaption">
25
+ <%= block_image.caption.presence || "Click to add caption..." %>
26
+ </div>
27
+
28
+ <!-- Edit Mode -->
29
+ <div class="caption-edit d-none">
30
+ <input type="text"
31
+ class="form-control form-control-sm"
32
+ value="<%= block_image.caption %>"
33
+ placeholder="Enter caption..."
34
+ data-image-id="<%= block_image.id %>"
35
+ data-action="blur->helios-press-image-block#saveCaption keydown.enter->helios-press-image-block#saveCaption keydown.esc->helios-press-image-block#cancelCaption">
36
+ </div>
37
+ </div>
38
+ </div>
@@ -0,0 +1,39 @@
1
+ <div data-controller="helios-press-text-block"
2
+ data-helios-press-text-block-id-value="<%= block.id %>"
3
+ data-helios-press-text-block-post-id-value="<%= post.id %>"
4
+ data-helios-press-text-block-base-url-value="<%= helios_press.admin_post_block_path(post, block) %>"
5
+ data-helios-press-text-block-mode-value="view">
6
+
7
+ <!-- View Mode -->
8
+ <div data-helios-press-text-block-target="viewMode"
9
+ data-action="dblclick->helios-press-text-block#enterEditMode"
10
+ class="block-view-mode">
11
+ <% if block.content.present? %>
12
+ <%= helios_press_process_rich_text_links(block.content) %>
13
+ <% else %>
14
+ <p class="text-muted fst-italic">Double-click to add content...</p>
15
+ <% end %>
16
+ </div>
17
+
18
+ <!-- Edit Mode -->
19
+ <div data-helios-press-text-block-target="editMode" class="block-edit-mode d-none">
20
+ <%= form_with(model: block,
21
+ url: helios_press.admin_post_block_path(post, block),
22
+ data: { helios_press_text_block_target: "form" }) do |f| %>
23
+ <%= f.rich_text_area :content,
24
+ data: { helios_press_text_block_target: "editor" },
25
+ class: "form-control mb-2" %>
26
+ <div class="d-flex gap-2">
27
+ <%= f.submit "Save", class: "btn btn-primary btn-sm", data: { action: "click->helios-press-text-block#save" } %>
28
+ <button type="button"
29
+ class="btn btn-secondary btn-sm"
30
+ data-action="click->helios-press-text-block#cancelEdit">Cancel</button>
31
+ <button type="button"
32
+ class="btn btn-danger btn-sm"
33
+ data-action="click->helios-press-blocks#deleteBlock">
34
+ Delete
35
+ </button>
36
+ </div>
37
+ <% end %>
38
+ </div>
39
+ </div>
@@ -0,0 +1,43 @@
1
+ <% if block.respond_to?(:video) && block.video.present? %>
2
+ <% video = block.video %>
3
+ <div data-controller="helios-video-block"
4
+ data-helios-video-block-video-id-value="<%= video.id %>"
5
+ class="video-block-container">
6
+ <!-- Block Controls -->
7
+ <div class="d-flex justify-content-end mb-2">
8
+ <button type="button"
9
+ class="btn btn-sm btn-danger"
10
+ data-action="click->helios-press-blocks#deleteBlock">
11
+ Delete Block
12
+ </button>
13
+ </div>
14
+
15
+ <!-- Video Name (editable) -->
16
+ <div class="mb-3">
17
+ <!-- View Mode -->
18
+ <div class="name-view"
19
+ data-action="click->helios-video-block#editName"
20
+ style="cursor: pointer; padding: 0.5rem; border: 1px solid transparent; border-radius: 0.25rem;">
21
+ <%= video.name.presence || "Click to add name..." %>
22
+ </div>
23
+
24
+ <!-- Edit Mode -->
25
+ <div class="name-edit d-none">
26
+ <input type="text"
27
+ class="form-control form-control-sm"
28
+ value="<%= video.name %>"
29
+ placeholder="Enter video name..."
30
+ data-action="blur->helios-video-block#saveName keydown.enter->helios-video-block#saveName keydown.esc->helios-video-block#cancelName">
31
+ </div>
32
+ </div>
33
+
34
+ <!-- Video Player -->
35
+ <div class="video-player-wrapper">
36
+ <%= video.player_component.html_safe %>
37
+ </div>
38
+ </div>
39
+ <% else %>
40
+ <div class="alert alert-warning mb-0">
41
+ No video found for this block.
42
+ </div>
43
+ <% end %>
@@ -0,0 +1,27 @@
1
+ <div id="blocks-container" data-helios-press-blocks-target="blocksContainer">
2
+ <!-- Placeholder at the top -->
3
+ <div class="block-placeholder"
4
+ data-helios-press-blocks-target="placeholder"
5
+ data-position="0"
6
+ data-action="click->helios-press-blocks#addBlock
7
+ dragover->helios-press-blocks#handlePlaceholderDragOver
8
+ dragleave->helios-press-blocks#handlePlaceholderDragLeave
9
+ drop->helios-press-blocks#handlePlaceholderDrop">
10
+ <div class="placeholder-line"></div>
11
+ </div>
12
+
13
+ <% post.blocks.each do |block| %>
14
+ <%= render "helios/press/admin/blocks/block", block: block, post: post %>
15
+
16
+ <!-- Placeholder after each block -->
17
+ <div class="block-placeholder"
18
+ data-helios-press-blocks-target="placeholder"
19
+ data-position="<%= block.position + 1 %>"
20
+ data-action="click->helios-press-blocks#addBlock
21
+ dragover->helios-press-blocks#handlePlaceholderDragOver
22
+ dragleave->helios-press-blocks#handlePlaceholderDragLeave
23
+ drop->helios-press-blocks#handlePlaceholderDrop">
24
+ <div class="placeholder-line"></div>
25
+ </div>
26
+ <% end %>
27
+ </div>
@@ -0,0 +1,48 @@
1
+ <%= form_with(model: post, url: post.persisted? ? helios_press.admin_post_path(post) : helios_press.admin_posts_path, local: true) do |form| %>
2
+ <% if post.errors.any? %>
3
+ <div class="alert alert-danger">
4
+ <h4><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h4>
5
+ <ul class="mb-0">
6
+ <% post.errors.full_messages.each do |message| %>
7
+ <li><%= message %></li>
8
+ <% end %>
9
+ </ul>
10
+ </div>
11
+ <% end %>
12
+
13
+ <div class="mb-3">
14
+ <%= form.label :name, "Title", class: "form-label" %>
15
+ <%= form.text_field :name, class: "form-control", placeholder: "Enter post title" %>
16
+ </div>
17
+
18
+ <div class="mb-3">
19
+ <%= form.label :slug, "Slug", class: "form-label" %>
20
+ <%= form.text_field :slug, class: "form-control", placeholder: "abc/xyz" %>
21
+ </div>
22
+
23
+ <div class="mb-3">
24
+ <%= form.label :description, class: "form-label" %>
25
+ <%= form.text_area :description, class: "form-control", rows: 3, placeholder: "Brief description for social media previews" %>
26
+ <div class="form-text">This description will appear when the post is shared on social media</div>
27
+ </div>
28
+
29
+ <div class="mb-3">
30
+ <%= form.label :keywords, class: "form-label" %>
31
+ <%= form.text_field :keywords, class: "form-control", placeholder: "Separate keywords with commas" %>
32
+ </div>
33
+
34
+ <div class="mb-3">
35
+ <div class="form-check">
36
+ <%= form.check_box :published, class: "form-check-input" %>
37
+ <%= form.label :published, "Published", class: "form-check-label" %>
38
+ </div>
39
+ </div>
40
+
41
+ <div class="d-flex gap-2">
42
+ <%= form.submit "Save", class: "btn btn-primary" %>
43
+ <% if post.persisted? && !post.published? %>
44
+ <%= form.submit "Publish Now", name: "publish", class: "btn btn-success", data: { turbo_confirm: "Publish this post now?" } %>
45
+ <% end %>
46
+ <%= link_to "Cancel", helios_press.admin_posts_path, class: "btn btn-secondary" %>
47
+ </div>
48
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <div class="container py-5">
2
+ <div class="row">
3
+ <div class="col-lg-8">
4
+ <h1 class="mb-4">Edit Post</h1>
5
+
6
+ <!-- Basic Post Info -->
7
+ <div class="card mb-4">
8
+ <div class="card-body">
9
+ <%= render "form", post: @post %>
10
+ </div>
11
+ </div>
12
+
13
+ <!-- Blocks Editor -->
14
+ <div class="card">
15
+ <div class="card-header">
16
+ <h5 class="mb-0">Content Blocks</h5>
17
+ </div>
18
+ <div class="card-body">
19
+ <div data-controller="helios-press-blocks" data-helios-press-blocks-post-id-value="<%= @post.id %>" data-helios-press-blocks-base-url-value="<%= helios_press.admin_post_blocks_path(@post) %>">
20
+ <%= render "blocks_container", post: @post %>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ </div>
@@ -0,0 +1,31 @@
1
+ <div class="container py-5">
2
+ <div class="d-flex justify-content-between align-items-center mb-4">
3
+ <h1>Posts</h1>
4
+ <%= link_to "New Post", helios_press.new_admin_post_path, class: "btn btn-primary" %>
5
+ </div>
6
+
7
+ <% if @posts.any? %>
8
+ <div class="list-group">
9
+ <% @posts.each do |post| %>
10
+ <div class="list-group-item d-flex justify-content-between align-items-center">
11
+ <div>
12
+ <h5 class="mb-1">
13
+ <%= link_to post.name, helios_press.edit_admin_post_path(post) %>
14
+ </h5>
15
+ <small class="text-muted">/<%= post.slug %></small>
16
+ <% if post.published? %>
17
+ <span class="badge bg-success ms-2">Published</span>
18
+ <% else %>
19
+ <span class="badge bg-secondary ms-2">Draft</span>
20
+ <% end %>
21
+ </div>
22
+ <div>
23
+ <small class="text-muted"><%= post.updated_at.strftime("%m/%d/%y") %></small>
24
+ </div>
25
+ </div>
26
+ <% end %>
27
+ </div>
28
+ <% else %>
29
+ <p class="text-muted">No posts yet. Create your first post!</p>
30
+ <% end %>
31
+ </div>
@@ -0,0 +1,12 @@
1
+ <div class="container py-5">
2
+ <div class="row">
3
+ <div class="col-lg-8">
4
+ <h1 class="mb-4">New Post</h1>
5
+ <div class="card">
6
+ <div class="card-body">
7
+ <%= render "form", post: @post %>
8
+ </div>
9
+ </div>
10
+ </div>
11
+ </div>
12
+ </div>
@@ -0,0 +1,18 @@
1
+ <div class="image-block mb-5">
2
+ <div class="images-grid" style="--grid-columns: <%= block.columns || 3 %>;">
3
+ <% block.block_images.each do |block_image| %>
4
+ <div class="image-item">
5
+ <% if block_image.file.attached? %>
6
+ <%= image_tag block_image.file.variant(resize_to_limit: [1200, 1200]),
7
+ class: "img-fluid rounded",
8
+ alt: block_image.caption.presence || "" %>
9
+ <% end %>
10
+ <% if block_image.caption.present? %>
11
+ <p class="image-caption text-muted mt-2 small">
12
+ <%= block_image.caption %>
13
+ </p>
14
+ <% end %>
15
+ </div>
16
+ <% end %>
17
+ </div>
18
+ </div>
@@ -0,0 +1,3 @@
1
+ <div class="text-block mb-4">
2
+ <%= helios_press_process_rich_text_links(block.content) %>
3
+ </div>
@@ -0,0 +1,12 @@
1
+ <% if Helios::Press.videos_enabled? && block.respond_to?(:video) && block.video.present? %>
2
+ <% video = block.video %>
3
+ <div class="video-block mb-5">
4
+ <% if video.name.present? %>
5
+ <h3 class="h5 mb-3"><%= video.name %></h3>
6
+ <% end %>
7
+
8
+ <div class="video-player-wrapper">
9
+ <%= video.player_component.html_safe %>
10
+ </div>
11
+ </div>
12
+ <% end %>
@@ -0,0 +1,45 @@
1
+ <% content_for :title, @post.name %>
2
+
3
+ <% 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 %>
24
+ <% end %>
25
+
26
+ <div class="container my-5">
27
+ <article class="post">
28
+ <header class="post-header mb-5">
29
+ <h1 class="display-4 mb-3"><%= @post.name %></h1>
30
+ <p class="text-muted">
31
+ <time datetime="<%= @post.updated_at.iso8601 %>">
32
+ <%= @post.updated_at.strftime("%m/%d/%y") %>
33
+ </time>
34
+ </p>
35
+ </header>
36
+
37
+ <div class="post-content">
38
+ <% @post.blocks.each_with_index do |block, index| %>
39
+ <div id="block-<%= index %>">
40
+ <%= render "helios/press/posts/blocks/#{block.block_type}", block: block, block_index: index %>
41
+ </div>
42
+ <% end %>
43
+ </div>
44
+ </article>
45
+ </div>
@@ -0,0 +1 @@
1
+ <%= render template: "layouts/application" %>
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Helios press</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= yield :head %>
9
+
10
+ <%= stylesheet_link_tag "helios/press/application", media: "all" %>
11
+ </head>
12
+ <body>
13
+
14
+ <%= yield %>
15
+
16
+ </body>
17
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,21 @@
1
+ Helios::Press::Engine.routes.draw do
2
+ namespace :admin do
3
+ root to: "posts#index"
4
+ resources :posts do
5
+ resources :blocks, only: [:create, :update, :destroy] do
6
+ collection do
7
+ patch :reorder
8
+ end
9
+ resources :images, controller: "block_images", only: [:create, :update, :destroy] do
10
+ collection do
11
+ patch :reorder
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ namespace :api do
19
+ resources :posts, only: [:create]
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ class CreateHeliosPressPosts < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :helios_press_posts do |t|
4
+ t.string :name, null: false
5
+ t.string :slug, null: false
6
+ t.text :description
7
+ t.string :keywords
8
+ t.boolean :published, default: false, null: false
9
+ t.string :external_id
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ add_index :helios_press_posts, :slug, unique: true
15
+ add_index :helios_press_posts, :external_id, unique: true
16
+ add_index :helios_press_posts, :published
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ class CreateHeliosPressBlocks < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :helios_press_blocks do |t|
4
+ t.references :post, null: false, foreign_key: { to_table: :helios_press_posts }
5
+ t.string :block_type, null: false
6
+ t.integer :position, null: false
7
+ t.integer :columns, default: 3
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :helios_press_blocks, [:post_id, :position]
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class CreateHeliosPressBlockImages < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :helios_press_block_images do |t|
4
+ t.references :block, null: false, foreign_key: { to_table: :helios_press_blocks }
5
+ t.integer :position, null: false
6
+ t.text :caption
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :helios_press_block_images, [:block_id, :position]
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ module Helios
2
+ module Press
3
+ class Configuration
4
+ attr_accessor :admin_parent_controller,
5
+ :api_parent_controller,
6
+ :api_authentication,
7
+ :post_slug_prefix
8
+
9
+ def initialize
10
+ @admin_parent_controller = "ApplicationController"
11
+ @api_parent_controller = "ActionController::API"
12
+ @api_authentication = nil
13
+ @post_slug_prefix = nil
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Helios
2
+ module Press
3
+ class Engine < ::Rails::Engine
4
+ isolate_namespace Helios::Press
5
+
6
+ initializer "helios_press.assets" do |app|
7
+ app.config.assets.precompile += %w[helios/press/blocks.css] if app.config.respond_to?(:assets)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Helios
2
+ module Press
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ require "helios/press/version"
2
+ require "helios/press/engine"
3
+ require "helios/press/configuration"
4
+ require "helios-videos"
5
+ require "helios-sitemap"
6
+
7
+ module Helios
8
+ module Press
9
+ class << self
10
+ def configuration
11
+ @configuration ||= Configuration.new
12
+ end
13
+
14
+ def configure
15
+ yield(configuration)
16
+ end
17
+
18
+ def videos_enabled?
19
+ defined?(Helios::Videos)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :helios_press do
3
+ # # Task goes here
4
+ # end