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.
- checksums.yaml +7 -0
- data/LICENSE.txt +41 -0
- data/README.md +285 -0
- data/app/assets/javascripts/not_pressed/lightbox.js +110 -0
- data/app/assets/stylesheets/not_pressed/admin.css +1161 -0
- data/app/assets/stylesheets/not_pressed/content.css +193 -0
- data/app/assets/stylesheets/not_pressed/gallery.css +117 -0
- data/app/assets/stylesheets/not_pressed/themes/starter.css +118 -0
- data/app/controllers/not_pressed/admin/base_controller.rb +21 -0
- data/app/controllers/not_pressed/admin/categories_controller.rb +53 -0
- data/app/controllers/not_pressed/admin/content_blocks_controller.rb +73 -0
- data/app/controllers/not_pressed/admin/dashboard_controller.rb +19 -0
- data/app/controllers/not_pressed/admin/forms_controller.rb +86 -0
- data/app/controllers/not_pressed/admin/media_attachments_controller.rb +94 -0
- data/app/controllers/not_pressed/admin/pages_controller.rb +122 -0
- data/app/controllers/not_pressed/admin/plugins_controller.rb +121 -0
- data/app/controllers/not_pressed/admin/settings_controller.rb +19 -0
- data/app/controllers/not_pressed/admin/tags_controller.rb +37 -0
- data/app/controllers/not_pressed/admin/themes_controller.rb +104 -0
- data/app/controllers/not_pressed/application_controller.rb +6 -0
- data/app/controllers/not_pressed/blog_controller.rb +83 -0
- data/app/controllers/not_pressed/form_submissions_controller.rb +70 -0
- data/app/controllers/not_pressed/pages_controller.rb +36 -0
- data/app/controllers/not_pressed/robots_controller.rb +34 -0
- data/app/controllers/not_pressed/sitemaps_controller.rb +12 -0
- data/app/helpers/not_pressed/admin_helper.rb +41 -0
- data/app/helpers/not_pressed/application_helper.rb +6 -0
- data/app/helpers/not_pressed/code_injection_helper.rb +29 -0
- data/app/helpers/not_pressed/content_helper.rb +13 -0
- data/app/helpers/not_pressed/form_helper.rb +80 -0
- data/app/helpers/not_pressed/media_helper.rb +28 -0
- data/app/helpers/not_pressed/seo_helper.rb +69 -0
- data/app/helpers/not_pressed/theme_helper.rb +42 -0
- data/app/mailers/not_pressed/application_mailer.rb +10 -0
- data/app/mailers/not_pressed/form_mailer.rb +15 -0
- data/app/models/concerns/not_pressed/sluggable.rb +43 -0
- data/app/models/not_pressed/category.rb +16 -0
- data/app/models/not_pressed/content_block.rb +46 -0
- data/app/models/not_pressed/form.rb +55 -0
- data/app/models/not_pressed/form_field.rb +23 -0
- data/app/models/not_pressed/form_submission.rb +19 -0
- data/app/models/not_pressed/media_attachment.rb +68 -0
- data/app/models/not_pressed/page.rb +182 -0
- data/app/models/not_pressed/page_version.rb +15 -0
- data/app/models/not_pressed/setting.rb +20 -0
- data/app/models/not_pressed/tag.rb +15 -0
- data/app/models/not_pressed/tagging.rb +12 -0
- data/app/plugins/not_pressed/analytics_plugin.rb +106 -0
- data/app/plugins/not_pressed/callout_block_plugin.rb +43 -0
- data/app/themes/not_pressed/starter_theme.rb +26 -0
- data/app/views/layouts/not_pressed/admin.html.erb +745 -0
- data/app/views/layouts/not_pressed/application.html.erb +12 -0
- data/app/views/layouts/not_pressed/page.html.erb +22 -0
- data/app/views/not_pressed/admin/categories/index.html.erb +58 -0
- data/app/views/not_pressed/admin/content_blocks/_block.html.erb +18 -0
- data/app/views/not_pressed/admin/content_blocks/_block_picker.html.erb +32 -0
- data/app/views/not_pressed/admin/content_blocks/create.turbo_stream.erb +3 -0
- data/app/views/not_pressed/admin/content_blocks/destroy.turbo_stream.erb +1 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_callout.html.erb +38 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_code.html.erb +26 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_divider.html.erb +4 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_form.html.erb +16 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_gallery.html.erb +75 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_heading.html.erb +26 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_html.html.erb +13 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_image.html.erb +56 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_quote.html.erb +24 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_text.html.erb +28 -0
- data/app/views/not_pressed/admin/content_blocks/editors/_video.html.erb +25 -0
- data/app/views/not_pressed/admin/dashboard/index.html.erb +60 -0
- data/app/views/not_pressed/admin/forms/_field_row.html.erb +33 -0
- data/app/views/not_pressed/admin/forms/_form.html.erb +75 -0
- data/app/views/not_pressed/admin/forms/edit.html.erb +1 -0
- data/app/views/not_pressed/admin/forms/index.html.erb +32 -0
- data/app/views/not_pressed/admin/forms/new.html.erb +1 -0
- data/app/views/not_pressed/admin/forms/submissions.html.erb +34 -0
- data/app/views/not_pressed/admin/media_attachments/_media_card.html.erb +21 -0
- data/app/views/not_pressed/admin/media_attachments/_picker.html.erb +19 -0
- data/app/views/not_pressed/admin/media_attachments/edit.html.erb +57 -0
- data/app/views/not_pressed/admin/media_attachments/index.html.erb +48 -0
- data/app/views/not_pressed/admin/pages/_form.html.erb +177 -0
- data/app/views/not_pressed/admin/pages/_page_tree_node.html.erb +32 -0
- data/app/views/not_pressed/admin/pages/edit.html.erb +69 -0
- data/app/views/not_pressed/admin/pages/index.html.erb +21 -0
- data/app/views/not_pressed/admin/pages/new.html.erb +1 -0
- data/app/views/not_pressed/admin/pages/preview.html.erb +17 -0
- data/app/views/not_pressed/admin/plugins/_settings_form.html.erb +59 -0
- data/app/views/not_pressed/admin/plugins/index.html.erb +48 -0
- data/app/views/not_pressed/admin/plugins/show.html.erb +54 -0
- data/app/views/not_pressed/admin/settings/code_injection.html.erb +21 -0
- data/app/views/not_pressed/admin/shared/_breadcrumbs.html.erb +14 -0
- data/app/views/not_pressed/admin/shared/_flash.html.erb +7 -0
- data/app/views/not_pressed/admin/shared/_modal.html.erb +9 -0
- data/app/views/not_pressed/admin/shared/_sidebar.html.erb +18 -0
- data/app/views/not_pressed/admin/tags/index.html.erb +52 -0
- data/app/views/not_pressed/admin/themes/index.html.erb +60 -0
- data/app/views/not_pressed/admin/themes/show.html.erb +66 -0
- data/app/views/not_pressed/blog/_post_card.html.erb +24 -0
- data/app/views/not_pressed/blog/feed.rss.builder +22 -0
- data/app/views/not_pressed/blog/index.html.erb +56 -0
- data/app/views/not_pressed/blog/show.html.erb +41 -0
- data/app/views/not_pressed/form_mailer/submission_notification.text.erb +8 -0
- data/app/views/not_pressed/pages/show.html.erb +4 -0
- data/app/views/themes/starter/layouts/not_pressed/default.html.erb +36 -0
- data/app/views/themes/starter/layouts/not_pressed/full_width.html.erb +36 -0
- data/app/views/themes/starter/layouts/not_pressed/sidebar.html.erb +41 -0
- data/config/routes.rb +81 -0
- data/db/migrate/20260310000001_create_not_pressed_pages.rb +20 -0
- data/db/migrate/20260310000002_create_not_pressed_content_blocks.rb +17 -0
- data/db/migrate/20260310000003_create_not_pressed_media_attachments.rb +14 -0
- data/db/migrate/20260310000004_add_content_type_to_not_pressed_pages.rb +8 -0
- data/db/migrate/20260310000005_add_publishing_fields_to_not_pressed_pages.rb +8 -0
- data/db/migrate/20260310000006_create_not_pressed_page_versions.rb +16 -0
- data/db/migrate/20260310000007_add_settings_fields_to_not_pressed_pages.rb +11 -0
- data/db/migrate/20260311000001_create_not_pressed_forms.rb +42 -0
- data/db/migrate/20260311000002_add_seo_fields_to_not_pressed_pages.rb +10 -0
- data/db/migrate/20260311000003_add_code_injection_to_not_pressed_pages.rb +8 -0
- data/db/migrate/20260311000004_create_not_pressed_settings.rb +14 -0
- data/db/migrate/20260312000001_create_not_pressed_categories.rb +16 -0
- data/db/migrate/20260312000002_create_not_pressed_tags.rb +15 -0
- data/db/migrate/20260312000003_create_not_pressed_taggings.rb +14 -0
- data/db/migrate/20260312000004_add_category_id_to_not_pressed_pages.rb +7 -0
- data/lib/generators/not_pressed/install/install_generator.rb +52 -0
- data/lib/generators/not_pressed/install/templates/initializer.rb.tt +89 -0
- data/lib/generators/not_pressed/install/templates/seeds.rb.tt +131 -0
- data/lib/generators/not_pressed/plugin/plugin_generator.rb +37 -0
- data/lib/generators/not_pressed/plugin/templates/plugin.rb.tt +23 -0
- data/lib/not_pressed/admin/authentication.rb +48 -0
- data/lib/not_pressed/admin/menu_registry.rb +100 -0
- data/lib/not_pressed/configuration.rb +77 -0
- data/lib/not_pressed/content_type.rb +23 -0
- data/lib/not_pressed/content_type_builder.rb +51 -0
- data/lib/not_pressed/content_type_registry.rb +45 -0
- data/lib/not_pressed/engine.rb +132 -0
- data/lib/not_pressed/hooks.rb +166 -0
- data/lib/not_pressed/navigation/builder.rb +148 -0
- data/lib/not_pressed/navigation/menu.rb +54 -0
- data/lib/not_pressed/navigation/menu_item.rb +33 -0
- data/lib/not_pressed/navigation/node.rb +45 -0
- data/lib/not_pressed/navigation/partial_parser.rb +96 -0
- data/lib/not_pressed/navigation/route_inspector.rb +98 -0
- data/lib/not_pressed/navigation.rb +6 -0
- data/lib/not_pressed/plugin.rb +354 -0
- data/lib/not_pressed/plugin_importer.rb +133 -0
- data/lib/not_pressed/plugin_manager.rb +196 -0
- data/lib/not_pressed/plugin_packager.rb +129 -0
- data/lib/not_pressed/rendering/block_renderer.rb +222 -0
- data/lib/not_pressed/rendering/renderer_registry.rb +154 -0
- data/lib/not_pressed/rendering.rb +8 -0
- data/lib/not_pressed/seo/sitemap_builder.rb +61 -0
- data/lib/not_pressed/theme.rb +191 -0
- data/lib/not_pressed/theme_importer.rb +133 -0
- data/lib/not_pressed/theme_packager.rb +180 -0
- data/lib/not_pressed/theme_registry.rb +123 -0
- data/lib/not_pressed/version.rb +5 -0
- data/lib/not_pressed.rb +65 -0
- metadata +258 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d2c847504c7b5020949675becc86c826e4e775dd38cccc5812f1ab80197b07c7
|
|
4
|
+
data.tar.gz: 82315be6116a3a33d7c21b7b4c8ed155bd6cf0b71dcd044f64c7824614db6514
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 728c1a3cfe4ec9cb7025194b0dd944b03ea80db01287c7f30fc13e7dadde234a825c32d765e477ba51a7caaab9289929bc8170b1e397ba79c5bc03d4c31c6204
|
|
7
|
+
data.tar.gz: c1873a495fec9800e6c3ce260874e719c206a8a451710f59241e13e2273602e0e76cfeaf1ce35d3c55b425f91f9bee91a7755dcdf266a0eb2bf56a169c414214
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
The NotPressed License (NPL-1.0)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NotPressed Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
1. The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
2. PLUGIN DISTRIBUTION CLAUSE: Any plugin, extension, theme, or add-on that
|
|
16
|
+
integrates with the Software's plugin interface and is distributed publicly
|
|
17
|
+
(i.e., made available to third parties outside the developer's own
|
|
18
|
+
organization) must be made available free of charge. Public plugins may be
|
|
19
|
+
open source or closed source, but may not require payment, subscription,
|
|
20
|
+
licensing fees, or any other form of monetary compensation for their use.
|
|
21
|
+
|
|
22
|
+
This clause does NOT restrict:
|
|
23
|
+
(a) Private plugins developed for internal use within an organization;
|
|
24
|
+
(b) Custom development services (building, modifying, or configuring the
|
|
25
|
+
Software or plugins for clients);
|
|
26
|
+
(c) Selling, bundling, or redistributing the Software itself as part of a
|
|
27
|
+
product, service, or hosted offering;
|
|
28
|
+
(d) Accepting voluntary donations or sponsorships for plugin development.
|
|
29
|
+
|
|
30
|
+
3. TRADEMARK CLAUSE: The name "NotPressed", the NotPressed logo, and
|
|
31
|
+
associated branding may not be used in derivative works, forks, or
|
|
32
|
+
products in a manner that suggests official endorsement or affiliation
|
|
33
|
+
without prior written permission from the copyright holders.
|
|
34
|
+
|
|
35
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
36
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
37
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
38
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
39
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
40
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
41
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# not\_pressed-core
|
|
2
|
+
|
|
3
|
+
The core engine behind [NotPressed](https://github.com/notpressed/not_pressed) — a drop-in, fully open-source CMS for Rails 8. Auto-discovers your app's navigation, provides a block-based content editor, and supports plugins and themes — all without the WordPress tax.
|
|
4
|
+
|
|
5
|
+
**Not WordPress. Not pressed for cash. Not pressed for time.**
|
|
6
|
+
|
|
7
|
+
## Why not\_pressed-core?
|
|
8
|
+
|
|
9
|
+
Most CMS options force you to either adopt a monolithic platform (WordPress, Strapi) or bolt on a headless CMS that doesn't understand your Rails app. not\_pressed-core takes a different approach: it's a Rails engine that lives *inside* your existing application, works with your routes and views, and stays out of your way.
|
|
10
|
+
|
|
11
|
+
- **3 commands to a working CMS** — install the gem, run the generator, migrate
|
|
12
|
+
- **Works with your app** — auto-discovers routes, partials, and navigation from your existing codebase
|
|
13
|
+
- **Zero external JS** — built entirely on Hotwire, Turbo, and Stimulus
|
|
14
|
+
- **Batteries included** — pages, blog, media library, block editor, forms, SEO, themes, plugin system
|
|
15
|
+
- **Extensible** — plugin architecture, custom content types, theme system, import/export
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Ruby >= 3.2
|
|
20
|
+
- Rails >= 8.0
|
|
21
|
+
- Active Storage (for media library)
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Add not\_pressed-core to your Gemfile:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
gem "not_pressed-core"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Run the install generator:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bundle install
|
|
35
|
+
rails generate not_pressed:install
|
|
36
|
+
rails db:migrate
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Optionally load sample content:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
rails runner "load 'db/seeds/not_pressed.rb'"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Visit `/not_pressed` to access the admin panel.
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
### Block-Based Page Editor
|
|
50
|
+
|
|
51
|
+
Build pages from ordered, typed content blocks with a drag-and-drop editor:
|
|
52
|
+
|
|
53
|
+
| Block Type | Description |
|
|
54
|
+
|------------|-------------|
|
|
55
|
+
| `text` | Rich text paragraph |
|
|
56
|
+
| `heading` | Section heading (h1-h6) |
|
|
57
|
+
| `image` | Image with caption and alt text |
|
|
58
|
+
| `video` | Embedded video |
|
|
59
|
+
| `code` | Syntax-highlighted code block |
|
|
60
|
+
| `quote` | Blockquote with attribution |
|
|
61
|
+
| `divider` | Visual separator |
|
|
62
|
+
| `html` | Raw HTML injection |
|
|
63
|
+
| `form` | Embedded form (from form builder) |
|
|
64
|
+
| `gallery` | Image grid with lightbox viewer |
|
|
65
|
+
| `callout` | Highlighted callout box (plugin) |
|
|
66
|
+
|
|
67
|
+
Custom block types can be added via the plugin system.
|
|
68
|
+
|
|
69
|
+
### Page Management
|
|
70
|
+
|
|
71
|
+
- Hierarchical page tree with drag-to-reorder
|
|
72
|
+
- Draft / published / scheduled / archived workflow
|
|
73
|
+
- Page duplication, version history
|
|
74
|
+
- SEO fields, layout selection, visibility controls
|
|
75
|
+
- Live preview with split/toggle pane
|
|
76
|
+
|
|
77
|
+
### Blog
|
|
78
|
+
|
|
79
|
+
Built-in blog with categories, tags, and RSS:
|
|
80
|
+
|
|
81
|
+
- Blog posts are pages with the `blog_post` content type
|
|
82
|
+
- Category and tag management in admin
|
|
83
|
+
- Public blog listing with pagination
|
|
84
|
+
- Category and tag filtering
|
|
85
|
+
- Year/month archives
|
|
86
|
+
- RSS 2.0 feed at `/blog/feed.rss`
|
|
87
|
+
|
|
88
|
+
### Media Library
|
|
89
|
+
|
|
90
|
+
Centralized media management backed by Active Storage:
|
|
91
|
+
|
|
92
|
+
- Upload, browse, search, and filter
|
|
93
|
+
- Automatic metadata extraction (dimensions, file size, content type)
|
|
94
|
+
- Alt text and title fields for accessibility and SEO
|
|
95
|
+
- Image variants and thumbnail generation
|
|
96
|
+
- Media picker modal for the block editor
|
|
97
|
+
|
|
98
|
+
### Forms
|
|
99
|
+
|
|
100
|
+
Drag-and-drop form builder with submissions:
|
|
101
|
+
|
|
102
|
+
- Field types: text, email, textarea, select, checkbox, radio, number, date, phone, url
|
|
103
|
+
- Submission management with CSV export
|
|
104
|
+
- Email notifications on submission
|
|
105
|
+
- Embeddable via the `form` block type
|
|
106
|
+
|
|
107
|
+
### SEO & Discoverability
|
|
108
|
+
|
|
109
|
+
- Per-page meta title, description, Open Graph, Twitter Cards
|
|
110
|
+
- Canonical URLs and robots meta directives
|
|
111
|
+
- Auto-generated XML sitemap at `/sitemap.xml`
|
|
112
|
+
- Configurable `robots.txt`
|
|
113
|
+
- Per-page and global JS/CSS code injection
|
|
114
|
+
|
|
115
|
+
### Navigation
|
|
116
|
+
|
|
117
|
+
not\_pressed-core builds navigation automatically using three layers:
|
|
118
|
+
|
|
119
|
+
1. **Config DSL** — explicitly defined menus
|
|
120
|
+
2. **Route introspection** — scans your Rails routes for navigable pages
|
|
121
|
+
3. **Partial parsing** — detects existing nav/menu/header partials in your views
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
The install generator creates `config/initializers/not_pressed.rb`:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
NotPressed.configure do |config|
|
|
129
|
+
# Site name displayed in the admin header
|
|
130
|
+
config.site_name = "My Site"
|
|
131
|
+
|
|
132
|
+
# Admin panel mount path (default: "not_pressed")
|
|
133
|
+
config.admin_path = "admin"
|
|
134
|
+
|
|
135
|
+
# Records per page in admin listings
|
|
136
|
+
config.per_page = 25
|
|
137
|
+
|
|
138
|
+
# Authentication — Devise method, custom Proc, or nil (no auth)
|
|
139
|
+
config.admin_authentication = :authenticate_admin_user!
|
|
140
|
+
config.admin_current_user = :current_admin_user
|
|
141
|
+
config.admin_unauthorized_redirect = "/login"
|
|
142
|
+
|
|
143
|
+
# Available layouts for pages
|
|
144
|
+
config.available_layouts = %w[default sidebar full_width]
|
|
145
|
+
|
|
146
|
+
# Default theme
|
|
147
|
+
config.default_theme = "Starter"
|
|
148
|
+
|
|
149
|
+
# Auto-discover navigation from routes and view partials
|
|
150
|
+
config.auto_discover_navigation = true
|
|
151
|
+
config.cache_navigation = true
|
|
152
|
+
|
|
153
|
+
# SEO title separator
|
|
154
|
+
config.seo_title_separator = " | "
|
|
155
|
+
|
|
156
|
+
# Global code injection (head/body)
|
|
157
|
+
config.global_head_code = ""
|
|
158
|
+
config.global_body_code = ""
|
|
159
|
+
|
|
160
|
+
# Navigation DSL
|
|
161
|
+
config.menus do |menu|
|
|
162
|
+
menu.add "Home", "/"
|
|
163
|
+
menu.add "Blog", "/blog"
|
|
164
|
+
menu.add "About", "/about"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Authentication
|
|
170
|
+
|
|
171
|
+
not\_pressed-core doesn't ship its own auth — it plugs into yours:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
# Devise
|
|
175
|
+
config.admin_authentication = :authenticate_admin_user!
|
|
176
|
+
|
|
177
|
+
# Custom logic
|
|
178
|
+
config.admin_authentication = ->(controller) {
|
|
179
|
+
controller.redirect_to "/login" unless controller.session[:admin]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# No auth (development only!)
|
|
183
|
+
config.admin_authentication = nil
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Extending NotPressed
|
|
187
|
+
|
|
188
|
+
### Custom Content Types
|
|
189
|
+
|
|
190
|
+
Define custom content types with the builder DSL:
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
NotPressed.define_content_type(:event) do
|
|
194
|
+
label "Event"
|
|
195
|
+
icon "calendar"
|
|
196
|
+
description "An event with date and location"
|
|
197
|
+
allowed_blocks :text, :heading, :image
|
|
198
|
+
has_slug true
|
|
199
|
+
has_parent false
|
|
200
|
+
|
|
201
|
+
field :event_date, :datetime, label: "Event Date", required: true
|
|
202
|
+
field :venue, :string, label: "Venue"
|
|
203
|
+
field :ticket_url, :url, label: "Ticket URL"
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Plugins
|
|
208
|
+
|
|
209
|
+
WordPress-style plugin system with hooks, filters, custom blocks, and admin extensions:
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
class MyPlugin < NotPressed::Plugin
|
|
213
|
+
name "my_plugin"
|
|
214
|
+
version "1.0.0"
|
|
215
|
+
description "Does something useful"
|
|
216
|
+
|
|
217
|
+
on(:after_page_save) { |page| Rails.logger.info "Saved: #{page.title}" }
|
|
218
|
+
|
|
219
|
+
block_type :callout
|
|
220
|
+
|
|
221
|
+
settings do
|
|
222
|
+
field :api_key, :string, label: "API Key"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
admin_menu label: "My Plugin", icon: "puzzle-piece"
|
|
226
|
+
|
|
227
|
+
routes do
|
|
228
|
+
get "my-plugin", to: "my_plugin#index"
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Plugins can be exported as `.zip` archives and imported on other installations via the admin panel.
|
|
234
|
+
|
|
235
|
+
See [Plugin Development Guide](docs/guides/plugin-development.md) for full documentation.
|
|
236
|
+
|
|
237
|
+
### Themes
|
|
238
|
+
|
|
239
|
+
Swappable themes with layouts, color schemes, and template overrides:
|
|
240
|
+
|
|
241
|
+
```ruby
|
|
242
|
+
class MyTheme < NotPressed::Theme
|
|
243
|
+
name "my_theme"
|
|
244
|
+
version "1.0.0"
|
|
245
|
+
|
|
246
|
+
layout :default, label: "Default", primary: true
|
|
247
|
+
layout :sidebar, label: "With Sidebar"
|
|
248
|
+
|
|
249
|
+
stylesheet "not_pressed/themes/my_theme"
|
|
250
|
+
|
|
251
|
+
color_scheme do
|
|
252
|
+
color :primary, "#3366cc", label: "Primary"
|
|
253
|
+
color :background, "#ffffff", label: "Background"
|
|
254
|
+
color :text, "#333333", label: "Text"
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Themes support color customization from the admin panel, template overrides via view path prepending, and can be exported/imported as `.zip` archives.
|
|
260
|
+
|
|
261
|
+
See [Theme Development Guide](docs/guides/theme-development.md) for full documentation.
|
|
262
|
+
|
|
263
|
+
## Development
|
|
264
|
+
|
|
265
|
+
After checking out the repo:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
bundle install
|
|
269
|
+
cd spec/dummy && rails db:create db:migrate && cd ../..
|
|
270
|
+
bundle exec rspec
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The test suite uses a Rails 8 dummy app in `spec/dummy/`.
|
|
274
|
+
|
|
275
|
+
### Documentation
|
|
276
|
+
|
|
277
|
+
- [Plugin Development Guide](docs/guides/plugin-development.md)
|
|
278
|
+
- [Theme Development Guide](docs/guides/theme-development.md)
|
|
279
|
+
- [Hooks & Filters Reference](docs/guides/hooks-reference.md)
|
|
280
|
+
- [CSS Variables Reference](docs/guides/css-variables-reference.md)
|
|
281
|
+
- [API Reference](docs/api-reference.md)
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
|
|
285
|
+
not\_pressed-core is released under the [NPL-1.0 License](LICENSE.txt).
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NotPressed Gallery Lightbox
|
|
3
|
+
*
|
|
4
|
+
* Auto-initializes on elements with data-controller="lightbox"
|
|
5
|
+
* or class "np-content-gallery". Provides click-to-open, keyboard
|
|
6
|
+
* navigation (left/right arrows, Escape), and overlay click to close.
|
|
7
|
+
*/
|
|
8
|
+
(function () {
|
|
9
|
+
"use strict";
|
|
10
|
+
|
|
11
|
+
function initLightbox(gallery) {
|
|
12
|
+
var images = gallery.querySelectorAll(".np-gallery-item img");
|
|
13
|
+
if (!images.length) return;
|
|
14
|
+
|
|
15
|
+
var currentIndex = 0;
|
|
16
|
+
var overlay = null;
|
|
17
|
+
var lightboxImg = null;
|
|
18
|
+
|
|
19
|
+
function createOverlay() {
|
|
20
|
+
overlay = document.createElement("div");
|
|
21
|
+
overlay.className = "np-lightbox-overlay";
|
|
22
|
+
overlay.setAttribute("hidden", "");
|
|
23
|
+
|
|
24
|
+
lightboxImg = document.createElement("img");
|
|
25
|
+
lightboxImg.className = "np-lightbox-image";
|
|
26
|
+
|
|
27
|
+
var closeBtn = document.createElement("button");
|
|
28
|
+
closeBtn.className = "np-lightbox-close";
|
|
29
|
+
closeBtn.textContent = "\u00d7";
|
|
30
|
+
closeBtn.setAttribute("aria-label", "Close lightbox");
|
|
31
|
+
|
|
32
|
+
var prevBtn = document.createElement("button");
|
|
33
|
+
prevBtn.className = "np-lightbox-nav np-lightbox-nav--prev";
|
|
34
|
+
prevBtn.textContent = "\u2039";
|
|
35
|
+
prevBtn.setAttribute("aria-label", "Previous image");
|
|
36
|
+
|
|
37
|
+
var nextBtn = document.createElement("button");
|
|
38
|
+
nextBtn.className = "np-lightbox-nav np-lightbox-nav--next";
|
|
39
|
+
nextBtn.textContent = "\u203a";
|
|
40
|
+
nextBtn.setAttribute("aria-label", "Next image");
|
|
41
|
+
|
|
42
|
+
overlay.appendChild(closeBtn);
|
|
43
|
+
overlay.appendChild(prevBtn);
|
|
44
|
+
overlay.appendChild(lightboxImg);
|
|
45
|
+
overlay.appendChild(nextBtn);
|
|
46
|
+
document.body.appendChild(overlay);
|
|
47
|
+
|
|
48
|
+
closeBtn.addEventListener("click", close);
|
|
49
|
+
prevBtn.addEventListener("click", prev);
|
|
50
|
+
nextBtn.addEventListener("click", next);
|
|
51
|
+
overlay.addEventListener("click", function (e) {
|
|
52
|
+
if (e.target === overlay) close();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function show(index) {
|
|
57
|
+
if (!overlay) createOverlay();
|
|
58
|
+
currentIndex = index;
|
|
59
|
+
lightboxImg.src = images[currentIndex].src;
|
|
60
|
+
lightboxImg.alt = images[currentIndex].alt || "";
|
|
61
|
+
overlay.removeAttribute("hidden");
|
|
62
|
+
document.addEventListener("keydown", onKeydown);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function close() {
|
|
66
|
+
if (overlay) {
|
|
67
|
+
overlay.setAttribute("hidden", "");
|
|
68
|
+
document.removeEventListener("keydown", onKeydown);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function prev() {
|
|
73
|
+
currentIndex = (currentIndex - 1 + images.length) % images.length;
|
|
74
|
+
lightboxImg.src = images[currentIndex].src;
|
|
75
|
+
lightboxImg.alt = images[currentIndex].alt || "";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function next() {
|
|
79
|
+
currentIndex = (currentIndex + 1) % images.length;
|
|
80
|
+
lightboxImg.src = images[currentIndex].src;
|
|
81
|
+
lightboxImg.alt = images[currentIndex].alt || "";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function onKeydown(e) {
|
|
85
|
+
if (e.key === "Escape") close();
|
|
86
|
+
else if (e.key === "ArrowLeft") prev();
|
|
87
|
+
else if (e.key === "ArrowRight") next();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
images.forEach(function (img, idx) {
|
|
91
|
+
img.style.cursor = "pointer";
|
|
92
|
+
img.addEventListener("click", function () {
|
|
93
|
+
show(idx);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function init() {
|
|
99
|
+
var galleries = document.querySelectorAll(
|
|
100
|
+
'.np-content-gallery, [data-controller="lightbox"]'
|
|
101
|
+
);
|
|
102
|
+
galleries.forEach(initLightbox);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (document.readyState === "loading") {
|
|
106
|
+
document.addEventListener("DOMContentLoaded", init);
|
|
107
|
+
} else {
|
|
108
|
+
init();
|
|
109
|
+
}
|
|
110
|
+
})();
|