not_pressed-core 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 (157) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +41 -0
  3. data/README.md +285 -0
  4. data/app/assets/javascripts/not_pressed/lightbox.js +110 -0
  5. data/app/assets/stylesheets/not_pressed/admin.css +1161 -0
  6. data/app/assets/stylesheets/not_pressed/content.css +193 -0
  7. data/app/assets/stylesheets/not_pressed/gallery.css +117 -0
  8. data/app/assets/stylesheets/not_pressed/themes/starter.css +118 -0
  9. data/app/controllers/not_pressed/admin/base_controller.rb +21 -0
  10. data/app/controllers/not_pressed/admin/categories_controller.rb +53 -0
  11. data/app/controllers/not_pressed/admin/content_blocks_controller.rb +73 -0
  12. data/app/controllers/not_pressed/admin/dashboard_controller.rb +19 -0
  13. data/app/controllers/not_pressed/admin/forms_controller.rb +86 -0
  14. data/app/controllers/not_pressed/admin/media_attachments_controller.rb +94 -0
  15. data/app/controllers/not_pressed/admin/pages_controller.rb +122 -0
  16. data/app/controllers/not_pressed/admin/plugins_controller.rb +121 -0
  17. data/app/controllers/not_pressed/admin/settings_controller.rb +19 -0
  18. data/app/controllers/not_pressed/admin/tags_controller.rb +37 -0
  19. data/app/controllers/not_pressed/admin/themes_controller.rb +104 -0
  20. data/app/controllers/not_pressed/application_controller.rb +6 -0
  21. data/app/controllers/not_pressed/blog_controller.rb +83 -0
  22. data/app/controllers/not_pressed/form_submissions_controller.rb +70 -0
  23. data/app/controllers/not_pressed/pages_controller.rb +36 -0
  24. data/app/controllers/not_pressed/robots_controller.rb +34 -0
  25. data/app/controllers/not_pressed/sitemaps_controller.rb +12 -0
  26. data/app/helpers/not_pressed/admin_helper.rb +41 -0
  27. data/app/helpers/not_pressed/application_helper.rb +6 -0
  28. data/app/helpers/not_pressed/code_injection_helper.rb +29 -0
  29. data/app/helpers/not_pressed/content_helper.rb +13 -0
  30. data/app/helpers/not_pressed/form_helper.rb +80 -0
  31. data/app/helpers/not_pressed/media_helper.rb +28 -0
  32. data/app/helpers/not_pressed/seo_helper.rb +69 -0
  33. data/app/helpers/not_pressed/theme_helper.rb +42 -0
  34. data/app/mailers/not_pressed/application_mailer.rb +10 -0
  35. data/app/mailers/not_pressed/form_mailer.rb +15 -0
  36. data/app/models/concerns/not_pressed/sluggable.rb +43 -0
  37. data/app/models/not_pressed/category.rb +16 -0
  38. data/app/models/not_pressed/content_block.rb +46 -0
  39. data/app/models/not_pressed/form.rb +55 -0
  40. data/app/models/not_pressed/form_field.rb +23 -0
  41. data/app/models/not_pressed/form_submission.rb +19 -0
  42. data/app/models/not_pressed/media_attachment.rb +68 -0
  43. data/app/models/not_pressed/page.rb +182 -0
  44. data/app/models/not_pressed/page_version.rb +15 -0
  45. data/app/models/not_pressed/setting.rb +20 -0
  46. data/app/models/not_pressed/tag.rb +15 -0
  47. data/app/models/not_pressed/tagging.rb +12 -0
  48. data/app/plugins/not_pressed/analytics_plugin.rb +106 -0
  49. data/app/plugins/not_pressed/callout_block_plugin.rb +43 -0
  50. data/app/themes/not_pressed/starter_theme.rb +26 -0
  51. data/app/views/layouts/not_pressed/admin.html.erb +745 -0
  52. data/app/views/layouts/not_pressed/application.html.erb +12 -0
  53. data/app/views/layouts/not_pressed/page.html.erb +22 -0
  54. data/app/views/not_pressed/admin/categories/index.html.erb +58 -0
  55. data/app/views/not_pressed/admin/content_blocks/_block.html.erb +18 -0
  56. data/app/views/not_pressed/admin/content_blocks/_block_picker.html.erb +32 -0
  57. data/app/views/not_pressed/admin/content_blocks/create.turbo_stream.erb +3 -0
  58. data/app/views/not_pressed/admin/content_blocks/destroy.turbo_stream.erb +1 -0
  59. data/app/views/not_pressed/admin/content_blocks/editors/_callout.html.erb +38 -0
  60. data/app/views/not_pressed/admin/content_blocks/editors/_code.html.erb +26 -0
  61. data/app/views/not_pressed/admin/content_blocks/editors/_divider.html.erb +4 -0
  62. data/app/views/not_pressed/admin/content_blocks/editors/_form.html.erb +16 -0
  63. data/app/views/not_pressed/admin/content_blocks/editors/_gallery.html.erb +75 -0
  64. data/app/views/not_pressed/admin/content_blocks/editors/_heading.html.erb +26 -0
  65. data/app/views/not_pressed/admin/content_blocks/editors/_html.html.erb +13 -0
  66. data/app/views/not_pressed/admin/content_blocks/editors/_image.html.erb +56 -0
  67. data/app/views/not_pressed/admin/content_blocks/editors/_quote.html.erb +24 -0
  68. data/app/views/not_pressed/admin/content_blocks/editors/_text.html.erb +28 -0
  69. data/app/views/not_pressed/admin/content_blocks/editors/_video.html.erb +25 -0
  70. data/app/views/not_pressed/admin/dashboard/index.html.erb +60 -0
  71. data/app/views/not_pressed/admin/forms/_field_row.html.erb +33 -0
  72. data/app/views/not_pressed/admin/forms/_form.html.erb +75 -0
  73. data/app/views/not_pressed/admin/forms/edit.html.erb +1 -0
  74. data/app/views/not_pressed/admin/forms/index.html.erb +32 -0
  75. data/app/views/not_pressed/admin/forms/new.html.erb +1 -0
  76. data/app/views/not_pressed/admin/forms/submissions.html.erb +34 -0
  77. data/app/views/not_pressed/admin/media_attachments/_media_card.html.erb +21 -0
  78. data/app/views/not_pressed/admin/media_attachments/_picker.html.erb +19 -0
  79. data/app/views/not_pressed/admin/media_attachments/edit.html.erb +57 -0
  80. data/app/views/not_pressed/admin/media_attachments/index.html.erb +48 -0
  81. data/app/views/not_pressed/admin/pages/_form.html.erb +177 -0
  82. data/app/views/not_pressed/admin/pages/_page_tree_node.html.erb +32 -0
  83. data/app/views/not_pressed/admin/pages/edit.html.erb +69 -0
  84. data/app/views/not_pressed/admin/pages/index.html.erb +21 -0
  85. data/app/views/not_pressed/admin/pages/new.html.erb +1 -0
  86. data/app/views/not_pressed/admin/pages/preview.html.erb +17 -0
  87. data/app/views/not_pressed/admin/plugins/_settings_form.html.erb +59 -0
  88. data/app/views/not_pressed/admin/plugins/index.html.erb +48 -0
  89. data/app/views/not_pressed/admin/plugins/show.html.erb +54 -0
  90. data/app/views/not_pressed/admin/settings/code_injection.html.erb +21 -0
  91. data/app/views/not_pressed/admin/shared/_breadcrumbs.html.erb +14 -0
  92. data/app/views/not_pressed/admin/shared/_flash.html.erb +7 -0
  93. data/app/views/not_pressed/admin/shared/_modal.html.erb +9 -0
  94. data/app/views/not_pressed/admin/shared/_sidebar.html.erb +18 -0
  95. data/app/views/not_pressed/admin/tags/index.html.erb +52 -0
  96. data/app/views/not_pressed/admin/themes/index.html.erb +60 -0
  97. data/app/views/not_pressed/admin/themes/show.html.erb +66 -0
  98. data/app/views/not_pressed/blog/_post_card.html.erb +24 -0
  99. data/app/views/not_pressed/blog/feed.rss.builder +22 -0
  100. data/app/views/not_pressed/blog/index.html.erb +56 -0
  101. data/app/views/not_pressed/blog/show.html.erb +41 -0
  102. data/app/views/not_pressed/form_mailer/submission_notification.text.erb +8 -0
  103. data/app/views/not_pressed/pages/show.html.erb +4 -0
  104. data/app/views/themes/starter/layouts/not_pressed/default.html.erb +36 -0
  105. data/app/views/themes/starter/layouts/not_pressed/full_width.html.erb +36 -0
  106. data/app/views/themes/starter/layouts/not_pressed/sidebar.html.erb +41 -0
  107. data/config/routes.rb +81 -0
  108. data/db/migrate/20260310000001_create_not_pressed_pages.rb +20 -0
  109. data/db/migrate/20260310000002_create_not_pressed_content_blocks.rb +17 -0
  110. data/db/migrate/20260310000003_create_not_pressed_media_attachments.rb +14 -0
  111. data/db/migrate/20260310000004_add_content_type_to_not_pressed_pages.rb +8 -0
  112. data/db/migrate/20260310000005_add_publishing_fields_to_not_pressed_pages.rb +8 -0
  113. data/db/migrate/20260310000006_create_not_pressed_page_versions.rb +16 -0
  114. data/db/migrate/20260310000007_add_settings_fields_to_not_pressed_pages.rb +11 -0
  115. data/db/migrate/20260311000001_create_not_pressed_forms.rb +42 -0
  116. data/db/migrate/20260311000002_add_seo_fields_to_not_pressed_pages.rb +10 -0
  117. data/db/migrate/20260311000003_add_code_injection_to_not_pressed_pages.rb +8 -0
  118. data/db/migrate/20260311000004_create_not_pressed_settings.rb +14 -0
  119. data/db/migrate/20260312000001_create_not_pressed_categories.rb +16 -0
  120. data/db/migrate/20260312000002_create_not_pressed_tags.rb +15 -0
  121. data/db/migrate/20260312000003_create_not_pressed_taggings.rb +14 -0
  122. data/db/migrate/20260312000004_add_category_id_to_not_pressed_pages.rb +7 -0
  123. data/lib/generators/not_pressed/install/install_generator.rb +52 -0
  124. data/lib/generators/not_pressed/install/templates/initializer.rb.tt +89 -0
  125. data/lib/generators/not_pressed/install/templates/seeds.rb.tt +131 -0
  126. data/lib/generators/not_pressed/plugin/plugin_generator.rb +37 -0
  127. data/lib/generators/not_pressed/plugin/templates/plugin.rb.tt +23 -0
  128. data/lib/not_pressed/admin/authentication.rb +48 -0
  129. data/lib/not_pressed/admin/menu_registry.rb +100 -0
  130. data/lib/not_pressed/configuration.rb +77 -0
  131. data/lib/not_pressed/content_type.rb +23 -0
  132. data/lib/not_pressed/content_type_builder.rb +51 -0
  133. data/lib/not_pressed/content_type_registry.rb +45 -0
  134. data/lib/not_pressed/engine.rb +132 -0
  135. data/lib/not_pressed/hooks.rb +166 -0
  136. data/lib/not_pressed/navigation/builder.rb +148 -0
  137. data/lib/not_pressed/navigation/menu.rb +54 -0
  138. data/lib/not_pressed/navigation/menu_item.rb +33 -0
  139. data/lib/not_pressed/navigation/node.rb +45 -0
  140. data/lib/not_pressed/navigation/partial_parser.rb +96 -0
  141. data/lib/not_pressed/navigation/route_inspector.rb +98 -0
  142. data/lib/not_pressed/navigation.rb +6 -0
  143. data/lib/not_pressed/plugin.rb +354 -0
  144. data/lib/not_pressed/plugin_importer.rb +133 -0
  145. data/lib/not_pressed/plugin_manager.rb +196 -0
  146. data/lib/not_pressed/plugin_packager.rb +129 -0
  147. data/lib/not_pressed/rendering/block_renderer.rb +222 -0
  148. data/lib/not_pressed/rendering/renderer_registry.rb +154 -0
  149. data/lib/not_pressed/rendering.rb +8 -0
  150. data/lib/not_pressed/seo/sitemap_builder.rb +61 -0
  151. data/lib/not_pressed/theme.rb +191 -0
  152. data/lib/not_pressed/theme_importer.rb +133 -0
  153. data/lib/not_pressed/theme_packager.rb +180 -0
  154. data/lib/not_pressed/theme_registry.rb +123 -0
  155. data/lib/not_pressed/version.rb +5 -0
  156. data/lib/not_pressed.rb +65 -0
  157. metadata +258 -0
@@ -0,0 +1,36 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <%= seo_meta_tags(@page, request) %>
7
+ <%= stylesheet_link_tag "not_pressed/content", media: "all" %>
8
+ <%= stylesheet_link_tag "not_pressed/gallery", media: "all" %>
9
+ <%= theme_stylesheet_tag %>
10
+ <%= theme_color_overrides %>
11
+ <%= csrf_meta_tags %>
12
+ <%= csp_meta_tag %>
13
+ <%= head_injection(@page) %>
14
+ </head>
15
+ <body class="np-page-body <%= active_theme_class %>">
16
+ <header class="np-theme-header">
17
+ <div class="np-theme-header-inner">
18
+ <a href="/" class="np-theme-site-name"><%= NotPressed.configuration.site_name %></a>
19
+ </div>
20
+ </header>
21
+
22
+ <main class="np-theme-main">
23
+ <div class="np-theme-content--full-width">
24
+ <%= yield %>
25
+ </div>
26
+ </main>
27
+
28
+ <footer class="np-theme-footer">
29
+ <div class="np-theme-footer-inner">
30
+ <p>&copy; <%= Date.current.year %> <%= NotPressed.configuration.site_name %></p>
31
+ </div>
32
+ </footer>
33
+ <%= javascript_include_tag "not_pressed/lightbox" %>
34
+ <%= body_injection(@page) %>
35
+ </body>
36
+ </html>
@@ -0,0 +1,41 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <%= seo_meta_tags(@page, request) %>
7
+ <%= stylesheet_link_tag "not_pressed/content", media: "all" %>
8
+ <%= stylesheet_link_tag "not_pressed/gallery", media: "all" %>
9
+ <%= theme_stylesheet_tag %>
10
+ <%= theme_color_overrides %>
11
+ <%= csrf_meta_tags %>
12
+ <%= csp_meta_tag %>
13
+ <%= head_injection(@page) %>
14
+ </head>
15
+ <body class="np-page-body <%= active_theme_class %>">
16
+ <header class="np-theme-header">
17
+ <div class="np-theme-header-inner">
18
+ <a href="/" class="np-theme-site-name"><%= NotPressed.configuration.site_name %></a>
19
+ </div>
20
+ </header>
21
+
22
+ <main class="np-theme-main">
23
+ <div class="np-theme-layout-sidebar">
24
+ <div class="np-theme-content">
25
+ <%= yield %>
26
+ </div>
27
+ <aside class="np-theme-sidebar">
28
+ <%= yield :sidebar if content_for?(:sidebar) %>
29
+ </aside>
30
+ </div>
31
+ </main>
32
+
33
+ <footer class="np-theme-footer">
34
+ <div class="np-theme-footer-inner">
35
+ <p>&copy; <%= Date.current.year %> <%= NotPressed.configuration.site_name %></p>
36
+ </div>
37
+ </footer>
38
+ <%= javascript_include_tag "not_pressed/lightbox" %>
39
+ <%= body_injection(@page) %>
40
+ </body>
41
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ NotPressed::Engine.routes.draw do
4
+ namespace :admin do
5
+ root to: "dashboard#index"
6
+ resources :forms, except: [:show] do
7
+ member do
8
+ get :submissions
9
+ get :export_csv
10
+ end
11
+ end
12
+ resource :settings, only: [] do
13
+ get :code_injection
14
+ patch :code_injection, action: :update_code_injection
15
+ end
16
+ resources :media_attachments, only: [:index, :create, :edit, :update, :destroy], path: "media" do
17
+ get :picker, on: :collection
18
+ end
19
+ resources :themes, only: [:index, :show], param: :theme_name do
20
+ member do
21
+ post :activate
22
+ post :deactivate
23
+ patch :update_colors
24
+ end
25
+ end
26
+ resources :plugins, only: [:index, :show], param: :plugin_name do
27
+ member do
28
+ post :toggle
29
+ patch :update_settings
30
+ end
31
+ end
32
+ get "themes/:id/export", to: "themes#export", as: :export_theme
33
+ post "themes/import", to: "themes#import", as: :import_theme
34
+ get "plugins/:id/export", to: "plugins#export", as: :export_plugin
35
+ post "plugins/import", to: "plugins#import", as: :import_plugin
36
+ resources :categories, only: [:index, :create, :update, :destroy]
37
+ resources :tags, only: [:index, :create, :destroy]
38
+ resources :pages do
39
+ collection do
40
+ patch :reorder
41
+ end
42
+ resources :content_blocks, only: [:create, :update, :destroy] do
43
+ collection do
44
+ patch :reorder
45
+ end
46
+ end
47
+ member do
48
+ get :preview
49
+ post :duplicate
50
+ end
51
+ end
52
+
53
+ # Plugin routes
54
+ NotPressed::PluginManager.registered.each do |_name, info|
55
+ next unless info[:status] == :active
56
+ info[:klass].plugin_routes.each do |route_block|
57
+ instance_exec(&route_block)
58
+ end
59
+ end
60
+ end
61
+
62
+ # Sitemap & Robots
63
+ get "sitemap.xml", to: "sitemaps#show", as: :sitemap, defaults: { format: :xml }
64
+ get "robots.txt", to: "robots#show", as: :robots, defaults: { format: :text }
65
+
66
+ # Public form submission route
67
+ post "forms/:form_slug/submit", to: "form_submissions#create", as: :form_submissions
68
+
69
+ # Blog routes
70
+ get "blog", to: "blog#index", as: :blog
71
+ get "blog/feed.rss", to: "blog#feed", as: :blog_feed, defaults: { format: :rss }
72
+ get "blog/category/:slug", to: "blog#category", as: :blog_category
73
+ get "blog/tag/:slug", to: "blog#tag", as: :blog_tag
74
+ get "blog/archive/:year(/:month)", to: "blog#archive", as: :blog_archive
75
+ get "blog/:slug", to: "blog#show", as: :blog_post
76
+
77
+ # Public page route
78
+ get ":slug", to: "pages#show", as: :page, constraints: { slug: /[a-z0-9]+(?:-[a-z0-9]+)*/ }
79
+
80
+ root to: "admin/dashboard#index"
81
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotPressedPages < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :not_pressed_pages do |t|
6
+ t.string :title, null: false
7
+ t.string :slug, null: false
8
+ t.text :meta_description
9
+ t.integer :status, default: 0, null: false
10
+ t.integer :position, default: 0, null: false
11
+ t.references :parent, foreign_key: { to_table: :not_pressed_pages }, null: true
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :not_pressed_pages, :slug, unique: true
17
+ add_index :not_pressed_pages, :status
18
+ add_index :not_pressed_pages, [:parent_id, :position]
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotPressedContentBlocks < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :not_pressed_content_blocks do |t|
6
+ t.references :page, null: false, foreign_key: { to_table: :not_pressed_pages }
7
+ t.string :block_type, null: false
8
+ t.json :content, default: {}
9
+ t.integer :position, default: 0, null: false
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ add_index :not_pressed_content_blocks, [:page_id, :position]
15
+ add_index :not_pressed_content_blocks, :block_type
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotPressedMediaAttachments < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :not_pressed_media_attachments do |t|
6
+ t.string :title
7
+ t.string :alt_text
8
+ t.string :content_type
9
+ t.integer :file_size
10
+ t.json :metadata, default: {}
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddContentTypeToNotPressedPages < ActiveRecord::Migration[8.0]
4
+ def change
5
+ add_column :not_pressed_pages, :content_type, :string, default: "page", null: false
6
+ add_index :not_pressed_pages, :content_type
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddPublishingFieldsToNotPressedPages < ActiveRecord::Migration[8.0]
4
+ def change
5
+ add_column :not_pressed_pages, :published_at, :datetime
6
+ add_column :not_pressed_pages, :scheduled_at, :datetime
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotPressedPageVersions < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :not_pressed_page_versions do |t|
6
+ t.references :page, null: false, foreign_key: { to_table: :not_pressed_pages }
7
+ t.integer :version_number, null: false
8
+ t.json :snapshot, null: false
9
+ t.string :event, null: false
10
+
11
+ t.datetime :created_at, null: false
12
+ end
13
+
14
+ add_index :not_pressed_page_versions, [:page_id, :version_number], unique: true
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddSettingsFieldsToNotPressedPages < ActiveRecord::Migration[8.0]
4
+ def change
5
+ add_column :not_pressed_pages, :meta_title, :string
6
+ add_column :not_pressed_pages, :og_image_url, :string
7
+ add_column :not_pressed_pages, :layout, :string
8
+ add_column :not_pressed_pages, :visibility, :integer, default: 0, null: false
9
+ add_column :not_pressed_pages, :password_digest, :string
10
+ end
11
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotPressedForms < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :not_pressed_forms do |t|
6
+ t.string :name, null: false
7
+ t.string :slug, null: false
8
+ t.text :description
9
+ t.string :email_recipient
10
+ t.string :redirect_url
11
+ t.text :success_message, default: "Thank you for your submission."
12
+ t.integer :status, default: 0, null: false
13
+ t.integer :submissions_count, default: 0, null: false
14
+
15
+ t.timestamps
16
+ end
17
+
18
+ add_index :not_pressed_forms, :slug, unique: true
19
+
20
+ create_table :not_pressed_form_fields do |t|
21
+ t.references :form, null: false, foreign_key: { to_table: :not_pressed_forms }
22
+ t.string :label, null: false
23
+ t.integer :field_type, default: 0, null: false
24
+ t.boolean :required, default: false
25
+ t.string :placeholder
26
+ t.json :options
27
+ t.integer :position, default: 0, null: false
28
+
29
+ t.timestamps
30
+ end
31
+
32
+ add_index :not_pressed_form_fields, [:form_id, :position]
33
+
34
+ create_table :not_pressed_form_submissions do |t|
35
+ t.references :form, null: false, foreign_key: { to_table: :not_pressed_forms }
36
+ t.json :data, null: false
37
+ t.datetime :submitted_at, null: false
38
+
39
+ t.timestamps
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddSeoFieldsToNotPressedPages < ActiveRecord::Migration[8.1]
4
+ def change
5
+ add_column :not_pressed_pages, :canonical_url, :string
6
+ add_column :not_pressed_pages, :meta_robots, :string
7
+ add_column :not_pressed_pages, :og_type, :string, default: "website"
8
+ add_column :not_pressed_pages, :twitter_card, :string, default: "summary"
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddCodeInjectionToNotPressedPages < ActiveRecord::Migration[8.1]
4
+ def change
5
+ add_column :not_pressed_pages, :head_code, :text
6
+ add_column :not_pressed_pages, :body_code, :text
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotPressedSettings < ActiveRecord::Migration[8.1]
4
+ def change
5
+ create_table :not_pressed_settings do |t|
6
+ t.string :key, null: false
7
+ t.text :value
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :not_pressed_settings, :key, unique: true
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotPressedCategories < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :not_pressed_categories do |t|
6
+ t.string :name, null: false
7
+ t.string :slug, null: false
8
+ t.text :description
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :not_pressed_categories, :name, unique: true
14
+ add_index :not_pressed_categories, :slug, unique: true
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotPressedTags < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :not_pressed_tags do |t|
6
+ t.string :name, null: false
7
+ t.string :slug, null: false
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :not_pressed_tags, :name, unique: true
13
+ add_index :not_pressed_tags, :slug, unique: true
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNotPressedTaggings < ActiveRecord::Migration[8.0]
4
+ def change
5
+ create_table :not_pressed_taggings do |t|
6
+ t.references :tag, null: false, foreign_key: { to_table: :not_pressed_tags }
7
+ t.references :page, null: false, foreign_key: { to_table: :not_pressed_pages }
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :not_pressed_taggings, [:tag_id, :page_id], unique: true
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddCategoryIdToNotPressedPages < ActiveRecord::Migration[8.0]
4
+ def change
5
+ add_reference :not_pressed_pages, :category, foreign_key: { to_table: :not_pressed_categories }, null: true
6
+ end
7
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module NotPressed
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ desc "Install NotPressed CMS into your Rails application"
11
+
12
+ def copy_initializer
13
+ template "initializer.rb.tt", "config/initializers/not_pressed.rb"
14
+ end
15
+
16
+ def copy_seeds
17
+ template "seeds.rb.tt", "db/seeds/not_pressed.rb"
18
+ end
19
+
20
+ def mount_engine
21
+ route 'mount NotPressed::Engine, at: "/not_pressed"'
22
+ end
23
+
24
+ def copy_migrations
25
+ rake "not_pressed:install:migrations"
26
+ end
27
+
28
+ def check_active_storage
29
+ unless defined?(ActiveStorage)
30
+ say "WARNING: Active Storage is not installed.", :yellow
31
+ say " Run: rails active_storage:install"
32
+ end
33
+ end
34
+
35
+ def show_post_install
36
+ say ""
37
+ say "NotPressed installed!", :green
38
+ say ""
39
+ say "Next steps:"
40
+ say " 1. Run `rails db:migrate` to set up the database"
41
+ say " 2. Load seed data by adding this to db/seeds.rb:"
42
+ say " load 'db/seeds/not_pressed.rb'"
43
+ say " Then run `rails db:seed`"
44
+ say " 3. Visit /not_pressed/admin to manage your site"
45
+ say " 4. Visit /not_pressed/blog to see the blog"
46
+ say " 5. Edit config/initializers/not_pressed.rb to customize settings"
47
+ say " 6. Import plugins and themes from the admin panel"
48
+ say ""
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,89 @@
1
+ # NotPressed CMS Configuration
2
+ #
3
+ # Customize your NotPressed installation below.
4
+ # Uncomment and modify any setting to override the defaults.
5
+
6
+ NotPressed.configure do |config|
7
+ # ---------------------------------------------------------------------------
8
+ # Site Settings
9
+ # ---------------------------------------------------------------------------
10
+
11
+ # Site name displayed in the admin header and SEO tags.
12
+ # config.site_name = "My Site"
13
+
14
+ # Default number of items per page in admin listings.
15
+ # config.per_page = 25
16
+
17
+ # Separator between page title and site name in browser title bar.
18
+ # config.seo_title_separator = " | "
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # Admin Authentication
22
+ # ---------------------------------------------------------------------------
23
+
24
+ # Proc that guards admin access. Return true to allow, false to deny.
25
+ # config.admin_authentication = ->(controller) { controller.current_user&.admin? }
26
+
27
+ # Proc that returns the current admin user (used for audit trails).
28
+ # config.admin_current_user = ->(controller) { controller.current_user }
29
+
30
+ # Path to redirect to when admin authentication fails.
31
+ # config.admin_unauthorized_redirect = "/"
32
+
33
+ # URL path prefix for admin routes.
34
+ # config.admin_path = "not_pressed"
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # Layouts & Themes
38
+ # ---------------------------------------------------------------------------
39
+
40
+ # Available layout names for pages (themes may add additional layouts).
41
+ # config.available_layouts = ["default"]
42
+
43
+ # Name of the default theme to activate on startup.
44
+ # config.default_theme = "Starter"
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Navigation
48
+ # ---------------------------------------------------------------------------
49
+
50
+ # Automatically discover navigable routes from your app.
51
+ # config.auto_discover_navigation = true
52
+
53
+ # Cache the discovered navigation structure for performance.
54
+ # config.cache_navigation = true
55
+
56
+ # Define a custom navigation menu:
57
+ # config.menu(:main) do
58
+ # item "Home", "/"
59
+ # item "Blog", "/not_pressed/blog"
60
+ # item "About", "/not_pressed/about"
61
+ # end
62
+
63
+ # ---------------------------------------------------------------------------
64
+ # Global Code Injection
65
+ # ---------------------------------------------------------------------------
66
+
67
+ # HTML injected into the <head> of every public page (analytics, fonts, etc.)
68
+ # config.global_head_code = nil
69
+
70
+ # HTML injected before the closing </body> of every public page.
71
+ # config.global_body_code = nil
72
+
73
+ # Custom robots.txt content (overrides the auto-generated one).
74
+ # config.robots_txt = nil
75
+
76
+ # ---------------------------------------------------------------------------
77
+ # Custom Content Types (Blog is built-in)
78
+ # ---------------------------------------------------------------------------
79
+ # Define custom content types using the DSL:
80
+ #
81
+ # NotPressed.define_content_type(:event) do
82
+ # label "Event"
83
+ # icon "calendar"
84
+ # description "An event page"
85
+ # field :event_date, :datetime, label: "Event Date", required: true
86
+ # field :venue, :string, label: "Venue"
87
+ # field :ticket_url, :url, label: "Ticket URL"
88
+ # end
89
+ end
@@ -0,0 +1,131 @@
1
+ # NotPressed CMS — Sample Seed Data
2
+ #
3
+ # Load this file from db/seeds.rb:
4
+ # load "db/seeds/not_pressed.rb"
5
+
6
+ puts "Seeding NotPressed CMS..."
7
+
8
+ # ---------------------------------------------------------------------------
9
+ # Pages
10
+ # ---------------------------------------------------------------------------
11
+
12
+ home = NotPressed::Page.find_or_create_by!(slug: "home") do |p|
13
+ p.title = "Home"
14
+ p.content_type = "page"
15
+ p.status = :published
16
+ end
17
+
18
+ NotPressed::ContentBlock.find_or_create_by!(page: home, position: 0) do |b|
19
+ b.block_type = "heading"
20
+ b.content = { "text" => "Welcome to Your Site", "level" => 1 }
21
+ end
22
+
23
+ NotPressed::ContentBlock.find_or_create_by!(page: home, position: 1) do |b|
24
+ b.block_type = "text"
25
+ b.content = { "body" => "<p>This is your new site powered by NotPressed CMS. Edit this page from the admin panel.</p>" }
26
+ end
27
+
28
+ about = NotPressed::Page.find_or_create_by!(slug: "about") do |p|
29
+ p.title = "About"
30
+ p.content_type = "page"
31
+ p.status = :published
32
+ end
33
+
34
+ NotPressed::ContentBlock.find_or_create_by!(page: about, position: 0) do |b|
35
+ b.block_type = "heading"
36
+ b.content = { "text" => "About Us", "level" => 1 }
37
+ end
38
+
39
+ NotPressed::ContentBlock.find_or_create_by!(page: about, position: 1) do |b|
40
+ b.block_type = "text"
41
+ b.content = { "body" => "<p>Tell your visitors about your site, team, or mission.</p>" }
42
+ end
43
+
44
+ NotPressed::ContentBlock.find_or_create_by!(page: about, position: 2) do |b|
45
+ b.block_type = "text"
46
+ b.content = { "body" => "<p>This is a second content block — you can add as many as you need.</p>" }
47
+ end
48
+
49
+ contact = NotPressed::Page.find_or_create_by!(slug: "contact") do |p|
50
+ p.title = "Contact"
51
+ p.content_type = "page"
52
+ p.status = :draft
53
+ end
54
+
55
+ NotPressed::ContentBlock.find_or_create_by!(page: contact, position: 0) do |b|
56
+ b.block_type = "heading"
57
+ b.content = { "text" => "Get in Touch", "level" => 1 }
58
+ end
59
+
60
+ NotPressed::ContentBlock.find_or_create_by!(page: contact, position: 1) do |b|
61
+ b.block_type = "text"
62
+ b.content = { "body" => "<p>Drop us a line — we'd love to hear from you.</p>" }
63
+ end
64
+
65
+ # ---------------------------------------------------------------------------
66
+ # Blog — Categories & Tags
67
+ # ---------------------------------------------------------------------------
68
+
69
+ general = NotPressed::Category.find_or_create_by!(name: "General")
70
+
71
+ welcome_tag = NotPressed::Tag.find_or_create_by!(name: "welcome")
72
+ news_tag = NotPressed::Tag.find_or_create_by!(name: "news")
73
+ updates_tag = NotPressed::Tag.find_or_create_by!(name: "updates")
74
+
75
+ # ---------------------------------------------------------------------------
76
+ # Blog Posts
77
+ # ---------------------------------------------------------------------------
78
+
79
+ post1 = NotPressed::Page.find_or_create_by!(slug: "welcome-to-not-pressed") do |p|
80
+ p.title = "Welcome to NotPressed"
81
+ p.content_type = "blog_post"
82
+ p.status = :published
83
+ p.category = general
84
+ p.published_at = Time.current
85
+ end
86
+
87
+ NotPressed::ContentBlock.find_or_create_by!(page: post1, position: 0) do |b|
88
+ b.block_type = "heading"
89
+ b.content = { "text" => "Welcome to NotPressed", "level" => 2 }
90
+ end
91
+
92
+ NotPressed::ContentBlock.find_or_create_by!(page: post1, position: 1) do |b|
93
+ b.block_type = "text"
94
+ b.content = { "body" => "<p>Your new blog is ready! Start writing posts from the admin panel at <code>/not_pressed/admin</code>.</p>" }
95
+ end
96
+
97
+ [welcome_tag, news_tag].each do |tag|
98
+ NotPressed::Tagging.find_or_create_by!(page: post1, tag: tag)
99
+ end
100
+
101
+ post2 = NotPressed::Page.find_or_create_by!(slug: "getting-started") do |p|
102
+ p.title = "Getting Started"
103
+ p.content_type = "blog_post"
104
+ p.status = :published
105
+ p.category = general
106
+ p.published_at = 1.hour.ago
107
+ end
108
+
109
+ NotPressed::ContentBlock.find_or_create_by!(page: post2, position: 0) do |b|
110
+ b.block_type = "heading"
111
+ b.content = { "text" => "Getting Started with NotPressed", "level" => 2 }
112
+ end
113
+
114
+ NotPressed::ContentBlock.find_or_create_by!(page: post2, position: 1) do |b|
115
+ b.block_type = "text"
116
+ b.content = { "body" => "<p>Learn how to create pages, manage content blocks, and customize your theme.</p>" }
117
+ end
118
+
119
+ NotPressed::ContentBlock.find_or_create_by!(page: post2, position: 2) do |b|
120
+ b.block_type = "text"
121
+ b.content = { "body" => "<p>Check out the documentation for more advanced features like custom content types and plugins.</p>" }
122
+ end
123
+
124
+ [news_tag, updates_tag].each do |tag|
125
+ NotPressed::Tagging.find_or_create_by!(page: post2, tag: tag)
126
+ end
127
+
128
+ puts "NotPressed seeding complete!"
129
+ puts " 3 pages (Home, About, Contact)"
130
+ puts " 2 blog posts (Welcome, Getting Started)"
131
+ puts " 1 category, 3 tags"