macron-cms 1.0.0 → 2.0.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.
- checksums.yaml +4 -4
- data/README.md +298 -21
- data/app/assets/javascripts/macron/cms/codemirror.js +6 -0
- data/app/assets/javascripts/macron/cms/editor.js +101 -0
- data/app/assets/stylesheets/macron/cms/application.css +1 -1
- data/app/assets/stylesheets/macron/cms/codemirror.css +64 -0
- data/app/assets/tailwind/macron/cms/application.css +1 -0
- data/app/components/macron/cms/widget_component.rb +25 -3
- data/app/controllers/macron/cms/article_contents_controller.rb +117 -10
- data/app/controllers/macron/cms/articles_controller.rb +16 -2
- data/app/helpers/macron/cms/content_helper.rb +31 -0
- data/app/helpers/macron/cms/fields_helper.rb +11 -1
- data/app/models/macron/cms/article.rb +36 -0
- data/app/models/macron/cms/article_content.rb +33 -1
- data/app/views/layouts/macron/cms/application.html.erb +6 -18
- data/app/views/macron/cms/articles/_content_blocks_section.html.erb +531 -0
- data/app/views/macron/cms/articles/_form.html.erb +121 -0
- data/app/views/macron/cms/articles/show.html.erb +11 -298
- data/app/views/macron/cms/content_types/show.html.erb +33 -9
- data/app/views/macron/cms/fields/_color.html.erb +35 -0
- data/app/views/macron/cms/fields/_dropdown.html.erb +28 -0
- data/app/views/macron/cms/fields/_file.html.erb +27 -0
- data/app/views/macron/cms/fields/_json.html.erb +17 -0
- data/app/views/macron/cms/fields/_markdown.html.erb +17 -0
- data/app/views/macron/cms/shared/_flash.html.erb +34 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20261028000007_add_background_fields_to_macron_cms_articles.rb +6 -0
- data/db/migrate/20261028000008_add_text_color_to_macron_cms_articles.rb +5 -0
- data/lib/macron/cms/version.rb +1 -1
- metadata +55 -1
|
@@ -22,10 +22,41 @@ module Macron
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
render widget_class.new(
|
|
25
|
+
article_content: article_content,
|
|
25
26
|
payload: article_content.payload,
|
|
26
27
|
fields: article_content.content.normalized_fields
|
|
27
28
|
)
|
|
28
29
|
end
|
|
30
|
+
|
|
31
|
+
def cms_article_background_style(article)
|
|
32
|
+
return "" if article.nil?
|
|
33
|
+
|
|
34
|
+
styles = []
|
|
35
|
+
if article.background_color.present?
|
|
36
|
+
styles << "background-color: #{article.background_color};"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if article.respond_to?(:text_color) && article.text_color.present?
|
|
40
|
+
styles << "color: #{article.text_color};"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if article.respond_to?(:background_image) && article.background_image.attached?
|
|
44
|
+
image_path = if respond_to?(:rails_blob_path)
|
|
45
|
+
rails_blob_path(article.background_image, only_path: true)
|
|
46
|
+
elsif respond_to?(:url_for)
|
|
47
|
+
url_for(article.background_image)
|
|
48
|
+
else
|
|
49
|
+
Rails.application.routes.url_helpers.rails_blob_path(article.background_image, only_path: true)
|
|
50
|
+
end
|
|
51
|
+
styles << "background-image: url('#{image_path}');"
|
|
52
|
+
styles << "background-repeat: #{article.background_repeat};"
|
|
53
|
+
styles << "background-size: #{article.background_size};"
|
|
54
|
+
styles << "background-position: #{article.background_position};"
|
|
55
|
+
styles << "background-attachment: #{article.background_attachment};"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
styles.join(" ")
|
|
59
|
+
end
|
|
29
60
|
end
|
|
30
61
|
end
|
|
31
62
|
end
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
require "kramdown"
|
|
2
|
+
require "kramdown-parser-gfm"
|
|
3
|
+
|
|
1
4
|
module Macron
|
|
2
5
|
module Cms
|
|
3
6
|
module FieldsHelper
|
|
4
|
-
SUPPORTED_KINDS = %w[string text].freeze
|
|
7
|
+
SUPPORTED_KINDS = %w[string text dropdown file color markdown json].freeze
|
|
8
|
+
|
|
9
|
+
def cms_render_markdown(text)
|
|
10
|
+
return "".html_safe if text.blank?
|
|
11
|
+
|
|
12
|
+
raw_html = Kramdown::Document.new(text.to_s, input: "GFM").to_html
|
|
13
|
+
sanitize(raw_html)
|
|
14
|
+
end
|
|
5
15
|
|
|
6
16
|
def cms_render_field(field_def, value: nil, input_name: nil, input_id: nil)
|
|
7
17
|
field_hash = field_def.stringify_keys
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
module Macron
|
|
2
2
|
module Cms
|
|
3
3
|
class Article < ApplicationRecord
|
|
4
|
+
VALID_BACKGROUND_REPEATS = %w[no-repeat repeat repeat-x repeat-y].freeze
|
|
5
|
+
VALID_BACKGROUND_SIZES = %w[cover contain auto].freeze
|
|
6
|
+
VALID_BACKGROUND_POSITIONS = %w[center top bottom left right].freeze
|
|
7
|
+
VALID_BACKGROUND_ATTACHMENTS = %w[scroll fixed].freeze
|
|
8
|
+
|
|
9
|
+
DEFAULT_BACKGROUND_REPEAT = "no-repeat"
|
|
10
|
+
DEFAULT_BACKGROUND_SIZE = "cover"
|
|
11
|
+
DEFAULT_BACKGROUND_POSITION = "center"
|
|
12
|
+
DEFAULT_BACKGROUND_ATTACHMENT = "scroll"
|
|
13
|
+
|
|
4
14
|
before_validation :derive_name_from_title
|
|
5
15
|
|
|
6
16
|
extend FriendlyId
|
|
@@ -12,6 +22,8 @@ module Macron
|
|
|
12
22
|
has_many :article_contents, -> { order(:sort_order) }, class_name: "Macron::Cms::ArticleContent", dependent: :destroy
|
|
13
23
|
has_many :contents, through: :article_contents
|
|
14
24
|
|
|
25
|
+
has_one_attached :background_image
|
|
26
|
+
|
|
15
27
|
def seo_title
|
|
16
28
|
meta_title.presence || title
|
|
17
29
|
end
|
|
@@ -20,6 +32,30 @@ module Macron
|
|
|
20
32
|
meta_description.presence
|
|
21
33
|
end
|
|
22
34
|
|
|
35
|
+
def normalized_background_options
|
|
36
|
+
(background_options || {}).to_h.stringify_keys
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def background_repeat
|
|
40
|
+
val = normalized_background_options["repeat"].to_s.strip.presence
|
|
41
|
+
VALID_BACKGROUND_REPEATS.include?(val) ? val : DEFAULT_BACKGROUND_REPEAT
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def background_size
|
|
45
|
+
val = normalized_background_options["size"].to_s.strip.presence
|
|
46
|
+
VALID_BACKGROUND_SIZES.include?(val) ? val : DEFAULT_BACKGROUND_SIZE
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def background_position
|
|
50
|
+
val = normalized_background_options["position"].to_s.strip.presence
|
|
51
|
+
VALID_BACKGROUND_POSITIONS.include?(val) ? val : DEFAULT_BACKGROUND_POSITION
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def background_attachment
|
|
55
|
+
val = normalized_background_options["attachment"].to_s.strip.presence
|
|
56
|
+
VALID_BACKGROUND_ATTACHMENTS.include?(val) ? val : DEFAULT_BACKGROUND_ATTACHMENT
|
|
57
|
+
end
|
|
58
|
+
|
|
23
59
|
private
|
|
24
60
|
|
|
25
61
|
def derive_name_from_title
|
|
@@ -4,17 +4,49 @@ module Macron
|
|
|
4
4
|
belongs_to :article, class_name: "Macron::Cms::Article"
|
|
5
5
|
belongs_to :content, class_name: "Macron::Cms::Content"
|
|
6
6
|
|
|
7
|
+
has_many_attached :files
|
|
8
|
+
|
|
7
9
|
before_validation :assign_default_sort_order, on: :create
|
|
8
10
|
|
|
9
11
|
validates :sort_order, numericality: { only_integer: true }
|
|
10
12
|
|
|
11
13
|
default_scope -> { order(:sort_order, :id) }
|
|
12
14
|
|
|
15
|
+
def upload
|
|
16
|
+
files.first
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def file
|
|
20
|
+
files.first
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def uploads
|
|
24
|
+
files
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def attach_upload(uploaded, replace: false)
|
|
28
|
+
return unless uploaded.present?
|
|
29
|
+
|
|
30
|
+
if replace && files.attached?
|
|
31
|
+
files.purge
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
files.attach(uploaded)
|
|
35
|
+
end
|
|
36
|
+
|
|
13
37
|
def filter_payload(raw_payload)
|
|
14
38
|
return {} unless content && raw_payload.respond_to?(:to_h)
|
|
15
39
|
|
|
16
40
|
allowed = content.field_names
|
|
17
|
-
raw_payload.to_h.stringify_keys.slice(*allowed)
|
|
41
|
+
raw_payload.to_h.stringify_keys.slice(*allowed).transform_values do |val|
|
|
42
|
+
if val.respond_to?(:original_filename)
|
|
43
|
+
val.original_filename
|
|
44
|
+
elsif val.is_a?(Array) && val.any? { |v| v.respond_to?(:original_filename) }
|
|
45
|
+
val.filter_map { |v| v.respond_to?(:original_filename) ? v.original_filename : v }
|
|
46
|
+
else
|
|
47
|
+
val
|
|
48
|
+
end
|
|
49
|
+
end
|
|
18
50
|
end
|
|
19
51
|
|
|
20
52
|
private
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
<%= csp_meta_tag %>
|
|
8
8
|
|
|
9
9
|
<%= stylesheet_link_tag "macron/cms/application", media: "all" %>
|
|
10
|
+
<%= stylesheet_link_tag "macron/cms/codemirror", media: "all" %>
|
|
11
|
+
<%= turbo_include_tags %>
|
|
12
|
+
<%= javascript_include_tag "macron/cms/codemirror", defer: true %>
|
|
13
|
+
<%= javascript_include_tag "macron/cms/editor", defer: true %>
|
|
10
14
|
<%= yield :head %>
|
|
11
15
|
</head>
|
|
12
16
|
<body class="min-h-screen text-zinc-900 antialiased flex bg-zinc-50">
|
|
@@ -100,24 +104,8 @@
|
|
|
100
104
|
</header>
|
|
101
105
|
|
|
102
106
|
<!-- Flash Notifications -->
|
|
103
|
-
<div class="max-w-7xl w-full mx-auto px-6 sm:px-8 pt-6">
|
|
104
|
-
|
|
105
|
-
<div class="p-4 mb-4 rounded-xl bg-emerald-50 border border-emerald-200 text-emerald-800 text-sm flex items-center gap-3">
|
|
106
|
-
<svg class="w-5 h-5 text-emerald-600 shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
|
107
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
|
108
|
-
</svg>
|
|
109
|
-
<span class="font-medium"><%= notice %></span>
|
|
110
|
-
</div>
|
|
111
|
-
<% end %>
|
|
112
|
-
|
|
113
|
-
<% if alert.present? %>
|
|
114
|
-
<div class="p-4 mb-4 rounded-xl bg-rose-50 border border-rose-200 text-rose-800 text-sm flex items-center gap-3">
|
|
115
|
-
<svg class="w-5 h-5 text-rose-600 shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
|
116
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
|
117
|
-
</svg>
|
|
118
|
-
<span class="font-medium"><%= alert %></span>
|
|
119
|
-
</div>
|
|
120
|
-
<% end %>
|
|
107
|
+
<div id="flash-messages" class="max-w-7xl w-full mx-auto px-6 sm:px-8 pt-6">
|
|
108
|
+
<%= render "macron/cms/shared/flash" %>
|
|
121
109
|
</div>
|
|
122
110
|
|
|
123
111
|
<!-- Main Content Canvas -->
|