railspress-engine 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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +67 -0
- data/Rakefile +10 -0
- data/app/assets/javascripts/railspress/admin.js +210 -0
- data/app/assets/stylesheets/actiontext.css +440 -0
- data/app/assets/stylesheets/railspress/admin.css +1199 -0
- data/app/assets/stylesheets/railspress/application.css +15 -0
- data/app/controllers/railspress/admin/base_controller.rb +38 -0
- data/app/controllers/railspress/admin/categories_controller.rb +53 -0
- data/app/controllers/railspress/admin/dashboard_controller.rb +12 -0
- data/app/controllers/railspress/admin/posts_controller.rb +71 -0
- data/app/controllers/railspress/admin/tags_controller.rb +50 -0
- data/app/controllers/railspress/application_controller.rb +4 -0
- data/app/helpers/railspress/application_helper.rb +4 -0
- data/app/jobs/railspress/application_job.rb +4 -0
- data/app/mailers/railspress/application_mailer.rb +6 -0
- data/app/models/railspress/application_record.rb +6 -0
- data/app/models/railspress/category.rb +18 -0
- data/app/models/railspress/post.rb +74 -0
- data/app/models/railspress/post_tag.rb +8 -0
- data/app/models/railspress/tag.rb +32 -0
- data/app/views/active_storage/blobs/_blob.html.erb +14 -0
- data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
- data/app/views/layouts/railspress/admin.html.erb +41 -0
- data/app/views/layouts/railspress/application.html.erb +17 -0
- data/app/views/railspress/admin/categories/_form.html.erb +32 -0
- data/app/views/railspress/admin/categories/edit.html.erb +6 -0
- data/app/views/railspress/admin/categories/index.html.erb +42 -0
- data/app/views/railspress/admin/categories/new.html.erb +6 -0
- data/app/views/railspress/admin/dashboard/index.html.erb +45 -0
- data/app/views/railspress/admin/posts/_form.html.erb +129 -0
- data/app/views/railspress/admin/posts/edit.html.erb +2 -0
- data/app/views/railspress/admin/posts/index.html.erb +55 -0
- data/app/views/railspress/admin/posts/new.html.erb +2 -0
- data/app/views/railspress/admin/posts/show.html.erb +47 -0
- data/app/views/railspress/admin/shared/_flash.html.erb +5 -0
- data/app/views/railspress/admin/shared/_sidebar.html.erb +38 -0
- data/app/views/railspress/admin/tags/_form.html.erb +27 -0
- data/app/views/railspress/admin/tags/edit.html.erb +6 -0
- data/app/views/railspress/admin/tags/index.html.erb +42 -0
- data/app/views/railspress/admin/tags/new.html.erb +6 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20241218000001_create_railspress_categories.rb +14 -0
- data/db/migrate/20241218000002_create_railspress_tags.rb +13 -0
- data/db/migrate/20241218000003_create_railspress_posts.rb +19 -0
- data/db/migrate/20241218000004_create_railspress_post_tags.rb +12 -0
- data/lib/generators/railspress/authors/authors_generator.rb +73 -0
- data/lib/generators/railspress/authors/templates/add_author_to_railspress_posts.rb.tt +8 -0
- data/lib/generators/railspress/authors/templates/railspress.rb.tt +31 -0
- data/lib/generators/railspress/install/install_generator.rb +144 -0
- data/lib/railspress/engine.rb +14 -0
- data/lib/railspress/version.rb +3 -0
- data/lib/railspress.rb +79 -0
- data/lib/tasks/railspress_tasks.rake +4 -0
- metadata +141 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<%= form_with model: [:admin, @post], class: "rp-form" do |form| %>
|
|
2
|
+
<% if @post.errors.any? %>
|
|
3
|
+
<div class="rp-form-errors">
|
|
4
|
+
<ul>
|
|
5
|
+
<% @post.errors.full_messages.each do |msg| %>
|
|
6
|
+
<li><%= msg %></li>
|
|
7
|
+
<% end %>
|
|
8
|
+
</ul>
|
|
9
|
+
</div>
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
<div class="rp-form-layout">
|
|
13
|
+
<!-- Main content column -->
|
|
14
|
+
<div class="rp-form-main">
|
|
15
|
+
<div class="rp-form-group">
|
|
16
|
+
<%= form.label :title, class: "rp-label" %>
|
|
17
|
+
<%= form.text_field :title,
|
|
18
|
+
class: "rp-input rp-input--title",
|
|
19
|
+
placeholder: "Enter post title...",
|
|
20
|
+
data: { slug_source: true },
|
|
21
|
+
required: true %>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="rp-form-group">
|
|
25
|
+
<%= form.label :slug, class: "rp-label" %>
|
|
26
|
+
<%= form.text_field :slug,
|
|
27
|
+
class: "rp-input rp-input--mono",
|
|
28
|
+
placeholder: "auto-generated-from-title",
|
|
29
|
+
data: { slug_target: true } %>
|
|
30
|
+
<p class="rp-hint">Leave blank to auto-generate from title</p>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="rp-form-group">
|
|
34
|
+
<%= form.label :content, class: "rp-label" %>
|
|
35
|
+
<%= form.rich_text_area :content, class: "rp-rich-text" %>
|
|
36
|
+
<p class="rp-hint">Rich text editor powered by Lexxy</p>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Sidebar column -->
|
|
41
|
+
<div class="rp-form-sidebar">
|
|
42
|
+
<div class="rp-sidebar-section">
|
|
43
|
+
<h3 class="rp-sidebar-title">Publishing</h3>
|
|
44
|
+
|
|
45
|
+
<div class="rp-form-group">
|
|
46
|
+
<%= form.label :status, class: "rp-label" %>
|
|
47
|
+
<%= form.select :status,
|
|
48
|
+
Railspress::Post.statuses.keys.map { |s| [s.titleize, s] },
|
|
49
|
+
{},
|
|
50
|
+
class: "rp-select" %>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="rp-form-group">
|
|
54
|
+
<%= form.label :category_id, "Category", class: "rp-label" %>
|
|
55
|
+
<%= form.collection_select :category_id, @categories, :id, :name,
|
|
56
|
+
{ prompt: "No category" },
|
|
57
|
+
{ class: "rp-select" } %>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<% if authors_enabled? %>
|
|
61
|
+
<div class="rp-form-group">
|
|
62
|
+
<%= form.label :author_id, "Author", class: "rp-label" %>
|
|
63
|
+
<%= form.collection_select :author_id, available_authors, :id, Railspress.author_display_method,
|
|
64
|
+
{ prompt: "No author", selected: @post.author_id },
|
|
65
|
+
{ class: "rp-select" } %>
|
|
66
|
+
</div>
|
|
67
|
+
<% end %>
|
|
68
|
+
|
|
69
|
+
<div class="rp-form-group">
|
|
70
|
+
<%= form.label :tag_list, "Tags", class: "rp-label" %>
|
|
71
|
+
<%= form.text_field :tag_list,
|
|
72
|
+
class: "rp-input",
|
|
73
|
+
placeholder: "ruby, rails, tutorial",
|
|
74
|
+
value: @post.tag_list %>
|
|
75
|
+
<p class="rp-hint">Comma-separated. New tags created automatically.</p>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<% if header_images_enabled? %>
|
|
80
|
+
<div class="rp-sidebar-section">
|
|
81
|
+
<h3 class="rp-sidebar-title">Header Image</h3>
|
|
82
|
+
|
|
83
|
+
<div class="rp-form-group">
|
|
84
|
+
<% if @post.header_image.attached? %>
|
|
85
|
+
<div class="rp-header-image-preview">
|
|
86
|
+
<%= image_tag @post.header_image.variant(resize_to_limit: [400, 300]), class: "rp-header-image-thumb" %>
|
|
87
|
+
</div>
|
|
88
|
+
<% end %>
|
|
89
|
+
|
|
90
|
+
<%= form.label :header_image, class: "rp-label" %>
|
|
91
|
+
<%= form.file_field :header_image, accept: "image/*", class: "rp-file-input" %>
|
|
92
|
+
<p class="rp-hint">Recommended: 1200x630px for social sharing</p>
|
|
93
|
+
|
|
94
|
+
<% if @post.header_image.attached? %>
|
|
95
|
+
<label class="rp-checkbox-label">
|
|
96
|
+
<%= form.check_box :remove_header_image, {}, "1", "0" %>
|
|
97
|
+
Remove current image
|
|
98
|
+
</label>
|
|
99
|
+
<% end %>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<% end %>
|
|
103
|
+
|
|
104
|
+
<div class="rp-sidebar-section">
|
|
105
|
+
<h3 class="rp-sidebar-title">SEO</h3>
|
|
106
|
+
|
|
107
|
+
<div class="rp-form-group">
|
|
108
|
+
<%= form.label :meta_title, class: "rp-label" %>
|
|
109
|
+
<%= form.text_field :meta_title,
|
|
110
|
+
class: "rp-input",
|
|
111
|
+
placeholder: "Defaults to post title" %>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<div class="rp-form-group">
|
|
115
|
+
<%= form.label :meta_description, class: "rp-label" %>
|
|
116
|
+
<%= form.text_area :meta_description,
|
|
117
|
+
class: "rp-input",
|
|
118
|
+
rows: 3,
|
|
119
|
+
placeholder: "Brief description for search engines" %>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div class="rp-form-actions">
|
|
126
|
+
<%= form.submit class: "rp-btn rp-btn--primary" %>
|
|
127
|
+
<%= link_to "Cancel", admin_posts_path, class: "rp-btn rp-btn--secondary" %>
|
|
128
|
+
</div>
|
|
129
|
+
<% end %>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<div class="rp-page-header">
|
|
2
|
+
<h1 class="rp-page-title">Posts</h1>
|
|
3
|
+
<div class="rp-page-actions">
|
|
4
|
+
<%= link_to "New Post", new_admin_post_path, class: "rp-btn rp-btn--primary" %>
|
|
5
|
+
</div>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="rp-card">
|
|
9
|
+
<div class="rp-table--responsive">
|
|
10
|
+
<table class="rp-table">
|
|
11
|
+
<thead>
|
|
12
|
+
<tr>
|
|
13
|
+
<th>Title</th>
|
|
14
|
+
<th>Category</th>
|
|
15
|
+
<% if authors_enabled? %><th>Author</th><% end %>
|
|
16
|
+
<th>Status</th>
|
|
17
|
+
<th>Date</th>
|
|
18
|
+
<th class="rp-table-actions">Actions</th>
|
|
19
|
+
</tr>
|
|
20
|
+
</thead>
|
|
21
|
+
<tbody>
|
|
22
|
+
<% @posts.each do |post| %>
|
|
23
|
+
<tr>
|
|
24
|
+
<td>
|
|
25
|
+
<%= link_to post.title, admin_post_path(post), class: "rp-link rp-table-primary" %>
|
|
26
|
+
<% if post.tags.any? %>
|
|
27
|
+
<div class="rp-tag-list">
|
|
28
|
+
<% post.tags.each do |tag| %>
|
|
29
|
+
<span class="rp-tag"><%= tag.name %></span>
|
|
30
|
+
<% end %>
|
|
31
|
+
</div>
|
|
32
|
+
<% end %>
|
|
33
|
+
</td>
|
|
34
|
+
<td><%= post.category&.name || "—" %></td>
|
|
35
|
+
<% if authors_enabled? %>
|
|
36
|
+
<td><%= post.author&.public_send(Railspress.author_display_method) || "—" %></td>
|
|
37
|
+
<% end %>
|
|
38
|
+
<td>
|
|
39
|
+
<span class="rp-badge rp-badge--<%= post.status %>">
|
|
40
|
+
<%= post.status.titleize %>
|
|
41
|
+
</span>
|
|
42
|
+
</td>
|
|
43
|
+
<td class="rp-table-secondary"><%= post.created_at.strftime("%b %d, %Y") %></td>
|
|
44
|
+
<td class="rp-table-actions">
|
|
45
|
+
<%= link_to "Edit", edit_admin_post_path(post), class: "rp-link" %>
|
|
46
|
+
<%= button_to "Delete", admin_post_path(post), method: :delete,
|
|
47
|
+
data: { turbo_confirm: "Delete this post?" },
|
|
48
|
+
class: "rp-link rp-link--danger" %>
|
|
49
|
+
</td>
|
|
50
|
+
</tr>
|
|
51
|
+
<% end %>
|
|
52
|
+
</tbody>
|
|
53
|
+
</table>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<div class="rp-page-header">
|
|
2
|
+
<div>
|
|
3
|
+
<h1 class="rp-page-title"><%= @post.title %></h1>
|
|
4
|
+
<div class="rp-post-meta">
|
|
5
|
+
<span class="rp-badge rp-badge--<%= @post.status %>"><%= @post.status.titleize %></span>
|
|
6
|
+
<% if @post.category %>
|
|
7
|
+
<span><%= @post.category.name %></span>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% if authors_enabled? && @post.author %>
|
|
10
|
+
<span>by <%= @post.author.public_send(Railspress.author_display_method) %></span>
|
|
11
|
+
<% end %>
|
|
12
|
+
<span><%= @post.created_at.strftime("%b %d, %Y") %></span>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="rp-page-actions">
|
|
16
|
+
<%= link_to "Edit", edit_admin_post_path(@post), class: "rp-btn rp-btn--primary" %>
|
|
17
|
+
<%= link_to "Back", admin_posts_path, class: "rp-btn rp-btn--secondary" %>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<% if @post.tags.any? %>
|
|
22
|
+
<div class="rp-tag-list rp-post-tags">
|
|
23
|
+
<% @post.tags.each do |tag| %>
|
|
24
|
+
<span class="rp-tag"><%= tag.name %></span>
|
|
25
|
+
<% end %>
|
|
26
|
+
</div>
|
|
27
|
+
<% end %>
|
|
28
|
+
|
|
29
|
+
<% if header_images_enabled? && @post.header_image.attached? %>
|
|
30
|
+
<div class="rp-header-image">
|
|
31
|
+
<%= image_tag @post.header_image.variant(resize_to_limit: [1200, 630]), class: "rp-header-image-full" %>
|
|
32
|
+
</div>
|
|
33
|
+
<% end %>
|
|
34
|
+
|
|
35
|
+
<article class="rp-card rp-card--padded rp-prose">
|
|
36
|
+
<%= @post.content %>
|
|
37
|
+
</article>
|
|
38
|
+
|
|
39
|
+
<% if @post.meta_title.present? || @post.meta_description.present? %>
|
|
40
|
+
<div class="rp-seo-preview">
|
|
41
|
+
<h3 class="rp-seo-preview-title">SEO Preview</h3>
|
|
42
|
+
<div class="rp-seo-title"><%= @post.meta_title.presence || @post.title %></div>
|
|
43
|
+
<% if @post.meta_description.present? %>
|
|
44
|
+
<div class="rp-seo-description"><%= @post.meta_description %></div>
|
|
45
|
+
<% end %>
|
|
46
|
+
</div>
|
|
47
|
+
<% end %>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<div class="rp-sidebar-content">
|
|
2
|
+
<h1 class="rp-logo">RailsPress</h1>
|
|
3
|
+
|
|
4
|
+
<nav class="rp-nav">
|
|
5
|
+
<%= link_to railspress.admin_root_path, class: "rp-nav-link #{current_page?(railspress.admin_root_path) ? 'rp-nav-link--active' : ''}" do %>
|
|
6
|
+
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
7
|
+
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
|
8
|
+
<polyline points="9 22 9 12 15 12 15 22"/>
|
|
9
|
+
</svg>
|
|
10
|
+
Dashboard
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
<%= link_to railspress.admin_posts_path, class: "rp-nav-link #{controller_name == 'posts' ? 'rp-nav-link--active' : ''}" do %>
|
|
14
|
+
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
15
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
|
16
|
+
<polyline points="14 2 14 8 20 8"/>
|
|
17
|
+
<line x1="16" y1="13" x2="8" y2="13"/>
|
|
18
|
+
<line x1="16" y1="17" x2="8" y2="17"/>
|
|
19
|
+
</svg>
|
|
20
|
+
Posts
|
|
21
|
+
<% end %>
|
|
22
|
+
|
|
23
|
+
<%= link_to railspress.admin_categories_path, class: "rp-nav-link #{controller_name == 'categories' ? 'rp-nav-link--active' : ''}" do %>
|
|
24
|
+
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
25
|
+
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
|
|
26
|
+
</svg>
|
|
27
|
+
Categories
|
|
28
|
+
<% end %>
|
|
29
|
+
|
|
30
|
+
<%= link_to railspress.admin_tags_path, class: "rp-nav-link #{controller_name == 'tags' ? 'rp-nav-link--active' : ''}" do %>
|
|
31
|
+
<svg class="rp-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
32
|
+
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"/>
|
|
33
|
+
<line x1="7" y1="7" x2="7.01" y2="7"/>
|
|
34
|
+
</svg>
|
|
35
|
+
Tags
|
|
36
|
+
<% end %>
|
|
37
|
+
</nav>
|
|
38
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<%= form_with model: [:admin, @tag], class: "rp-form rp-form--narrow" do |form| %>
|
|
2
|
+
<% if @tag.errors.any? %>
|
|
3
|
+
<div class="rp-form-errors">
|
|
4
|
+
<ul>
|
|
5
|
+
<% @tag.errors.full_messages.each do |msg| %>
|
|
6
|
+
<li><%= msg %></li>
|
|
7
|
+
<% end %>
|
|
8
|
+
</ul>
|
|
9
|
+
</div>
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
<div class="rp-form-group">
|
|
13
|
+
<%= form.label :name, class: "rp-label rp-label--lg rp-label--required" %>
|
|
14
|
+
<%= form.text_field :name, class: "rp-input rp-input--lg", placeholder: "Tag name", data: { slug_source: true }, required: true %>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div class="rp-form-group">
|
|
18
|
+
<%= form.label :slug, class: "rp-label" %>
|
|
19
|
+
<%= form.text_field :slug, class: "rp-input rp-input--mono", placeholder: "auto-generated-if-blank", data: { slug_target: true } %>
|
|
20
|
+
<p class="rp-hint">Leave blank to auto-generate from name</p>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="rp-form-actions">
|
|
24
|
+
<%= form.submit class: "rp-btn rp-btn--primary" %>
|
|
25
|
+
<%= link_to "Cancel", admin_tags_path, class: "rp-btn rp-btn--secondary" %>
|
|
26
|
+
</div>
|
|
27
|
+
<% end %>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<% content_for :title, "Tags" %>
|
|
2
|
+
|
|
3
|
+
<div class="rp-page-header">
|
|
4
|
+
<h1 class="rp-page-title">Tags</h1>
|
|
5
|
+
<div class="rp-page-actions">
|
|
6
|
+
<%= link_to "New Tag", new_admin_tag_path, class: "rp-btn rp-btn--primary" %>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="rp-card">
|
|
11
|
+
<% if @tags.any? %>
|
|
12
|
+
<div class="rp-table--responsive">
|
|
13
|
+
<table class="rp-table">
|
|
14
|
+
<thead>
|
|
15
|
+
<tr>
|
|
16
|
+
<th>Name</th>
|
|
17
|
+
<th>Slug</th>
|
|
18
|
+
<th>Posts</th>
|
|
19
|
+
<th class="rp-table-actions">Actions</th>
|
|
20
|
+
</tr>
|
|
21
|
+
</thead>
|
|
22
|
+
<tbody>
|
|
23
|
+
<% @tags.each do |tag| %>
|
|
24
|
+
<tr>
|
|
25
|
+
<td class="rp-table-primary"><%= tag.name %></td>
|
|
26
|
+
<td class="rp-table-secondary"><%= tag.slug %></td>
|
|
27
|
+
<td><%= tag.posts.count %></td>
|
|
28
|
+
<td class="rp-table-actions">
|
|
29
|
+
<%= link_to "Edit", edit_admin_tag_path(tag), class: "rp-link" %>
|
|
30
|
+
<%= button_to "Delete", admin_tag_path(tag), method: :delete,
|
|
31
|
+
data: { turbo_confirm: "Delete this tag?" },
|
|
32
|
+
class: "rp-link rp-link--danger" %>
|
|
33
|
+
</td>
|
|
34
|
+
</tr>
|
|
35
|
+
<% end %>
|
|
36
|
+
</tbody>
|
|
37
|
+
</table>
|
|
38
|
+
</div>
|
|
39
|
+
<% else %>
|
|
40
|
+
<p class="rp-empty-state">No tags yet. <%= link_to "Create your first tag", new_admin_tag_path, class: "rp-link" %>.</p>
|
|
41
|
+
<% end %>
|
|
42
|
+
</div>
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class CreateRailspressCategories < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :railspress_categories do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :slug, null: false
|
|
6
|
+
t.text :description
|
|
7
|
+
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
add_index :railspress_categories, :name, unique: true
|
|
12
|
+
add_index :railspress_categories, :slug, unique: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateRailspressTags < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :railspress_tags do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :slug, null: false
|
|
6
|
+
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :railspress_tags, :name, unique: true
|
|
11
|
+
add_index :railspress_tags, :slug, unique: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class CreateRailspressPosts < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :railspress_posts do |t|
|
|
4
|
+
t.string :title, null: false
|
|
5
|
+
t.string :slug, null: false
|
|
6
|
+
t.references :category, null: true, foreign_key: { to_table: :railspress_categories }
|
|
7
|
+
t.integer :status, default: 0, null: false
|
|
8
|
+
t.datetime :published_at
|
|
9
|
+
t.string :meta_title
|
|
10
|
+
t.text :meta_description
|
|
11
|
+
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index :railspress_posts, :slug, unique: true
|
|
16
|
+
add_index :railspress_posts, :status
|
|
17
|
+
add_index :railspress_posts, :published_at
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CreateRailspressPostTags < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :railspress_post_tags do |t|
|
|
4
|
+
t.references :post, null: false, foreign_key: { to_table: :railspress_posts }
|
|
5
|
+
t.references :tag, null: false, foreign_key: { to_table: :railspress_tags }
|
|
6
|
+
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :railspress_post_tags, [:post_id, :tag_id], unique: true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/base"
|
|
5
|
+
|
|
6
|
+
module Railspress
|
|
7
|
+
module Generators
|
|
8
|
+
class AuthorsGenerator < Rails::Generators::Base
|
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
|
10
|
+
|
|
11
|
+
desc "Enable author support for RailsPress posts"
|
|
12
|
+
|
|
13
|
+
def create_migration
|
|
14
|
+
migration_template(
|
|
15
|
+
"add_author_to_railspress_posts.rb.tt",
|
|
16
|
+
"db/migrate/add_author_to_railspress_posts.rb",
|
|
17
|
+
migration_version: migration_version
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create_initializer
|
|
22
|
+
if initializer_exists?
|
|
23
|
+
say_status :skip, "config/initializers/railspress.rb already exists", :yellow
|
|
24
|
+
say ""
|
|
25
|
+
say "Add the following to your existing initializer:", :yellow
|
|
26
|
+
say ""
|
|
27
|
+
say " config.enable_authors", :cyan
|
|
28
|
+
say " config.author_class_name = \"User\"", :cyan
|
|
29
|
+
say " # config.author_scope = :admins", :cyan
|
|
30
|
+
say " # config.author_display_method = :name", :cyan
|
|
31
|
+
say ""
|
|
32
|
+
else
|
|
33
|
+
template "railspress.rb.tt", "config/initializers/railspress.rb"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def show_post_install_message
|
|
38
|
+
say ""
|
|
39
|
+
say "=" * 60, :green
|
|
40
|
+
say " RailsPress Authors enabled!", :green
|
|
41
|
+
say "=" * 60, :green
|
|
42
|
+
say ""
|
|
43
|
+
say "Next steps:", :yellow
|
|
44
|
+
say ""
|
|
45
|
+
say " 1. Run migrations:"
|
|
46
|
+
say " $ rails db:migrate", :cyan
|
|
47
|
+
say ""
|
|
48
|
+
say " 2. Configure authors in config/initializers/railspress.rb", :cyan
|
|
49
|
+
say ""
|
|
50
|
+
say " 3. (Optional) Add an author scope to your User model:"
|
|
51
|
+
say " scope :authors, -> { where(role: :admin) }", :cyan
|
|
52
|
+
say ""
|
|
53
|
+
say "=" * 60, :green
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def initializer_exists?
|
|
59
|
+
File.exist?(Rails.root.join("config", "initializers", "railspress.rb"))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def migration_version
|
|
63
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Required for migration_template to work
|
|
67
|
+
def self.next_migration_number(dirname)
|
|
68
|
+
next_migration_number = current_migration_number(dirname) + 1
|
|
69
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Railspress.configure do |config|
|
|
4
|
+
# ==========================================================================
|
|
5
|
+
# Authors
|
|
6
|
+
# ==========================================================================
|
|
7
|
+
|
|
8
|
+
# Enable author tracking on posts
|
|
9
|
+
# When enabled, posts can have an author and the admin form shows an author select
|
|
10
|
+
config.enable_authors
|
|
11
|
+
|
|
12
|
+
# The class name of your author model (default: "User")
|
|
13
|
+
config.author_class_name = "User"
|
|
14
|
+
|
|
15
|
+
# Controller method that returns the current user (default: :current_user)
|
|
16
|
+
# Used to pre-select the author on new posts
|
|
17
|
+
# config.current_author_method = :current_user
|
|
18
|
+
|
|
19
|
+
# Scope for which users appear in the author dropdown (default: nil = all)
|
|
20
|
+
# Pass a symbol to call a scope on the author class, or a lambda for custom logic
|
|
21
|
+
#
|
|
22
|
+
# Examples:
|
|
23
|
+
# config.author_scope = :admins # calls User.admins
|
|
24
|
+
# config.author_scope = :authors # calls User.authors
|
|
25
|
+
# config.author_scope = ->(klass) { klass.active } # custom filtering
|
|
26
|
+
#
|
|
27
|
+
# config.author_scope = nil
|
|
28
|
+
|
|
29
|
+
# Method called on author to display in the dropdown (default: :name)
|
|
30
|
+
# config.author_display_method = :name
|
|
31
|
+
end
|