trek 0.1.20 โ 0.1.22
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/.npmignore +3 -0
- data/Gemfile.lock +86 -82
- data/README.md +8 -0
- data/app/assets/stylesheets/trek/_colors.css +181 -0
- data/app/components/trek/button_component.rb +9 -3
- data/app/components/trek/dashboard/tile_component/tile_component.html.slim +1 -1
- data/app/components/trek/dashboard/tile_component.rb +2 -0
- data/app/components/trek/form/actions_component/actions_component.css +6 -0
- data/app/components/trek/form/image_field_component/image_field_component.css +8 -0
- data/app/components/trek/form/image_field_component/image_field_component.html.slim +20 -8
- data/app/components/trek/form/image_field_component/image_field_component.js +23 -1
- data/app/components/trek/form/image_field_component.rb +13 -0
- data/app/components/trek/form/switch_box_component.rb +1 -0
- data/app/components/trek/tabs_component/tabs_component.css +1 -0
- data/app/controllers/concerns/trek/model.rb +4 -1
- data/config/locales/trek.en.yml +3 -0
- data/config/locales/trek.fr.yml +1 -0
- data/docs/.vitepress/config.mjs +133 -0
- data/docs/.vitepress/theme/index.js +17 -0
- data/docs/.vitepress/theme/style.css +139 -0
- data/docs/customize.md +63 -7
- data/docs/getting-started.md +160 -0
- data/docs/index.md +39 -0
- data/docs/public/logo.svg +1 -0
- data/docs/reference/components.md +40 -0
- data/docs/reference/concerns/contentable.md +28 -0
- data/docs/reference/concerns/formattable.md +24 -0
- data/docs/reference/concerns/fragmentable.md +33 -0
- data/docs/reference/concerns/keyable.md +18 -0
- data/docs/reference/concerns/orderable.md +26 -0
- data/docs/reference/concerns/pageable.md +35 -0
- data/docs/reference/concerns/pathable.md +28 -0
- data/docs/reference/concerns/phonable.md +28 -0
- data/docs/reference/concerns/search-engine-optimizable.md +19 -0
- data/docs/reference/concerns/searchable.md +22 -0
- data/docs/reference/concerns/sectionable.md +24 -0
- data/docs/reference/concerns/sluggable.md +21 -0
- data/docs/reference/concerns/taggable.md +20 -0
- data/docs/reference/concerns/translatable.md +35 -0
- data/docs/reference/concerns/versionable.md +27 -0
- data/docs/reference/controllers.md +23 -0
- data/docs/reference/environment-variables.md +9 -0
- data/docs/reference/formatters.md +30 -0
- data/docs/reference/forms.md +21 -0
- data/docs/reference/generators/admin-user.md +17 -0
- data/docs/reference/generators/install.md +42 -0
- data/docs/reference/generators/scaffold.md +37 -0
- data/docs/reference/generators/taxonomies.md +21 -0
- data/docs/reference/icons.md +11 -0
- data/docs/reference/index.md +47 -0
- data/docs/reference/models/current.md +11 -0
- data/docs/reference/models/external-link.md +16 -0
- data/docs/reference/models/fragment.md +28 -0
- data/docs/reference/models/menu-node.md +30 -0
- data/docs/reference/models/page-path.md +19 -0
- data/docs/reference/models/page-version.md +30 -0
- data/docs/reference/models/page.md +48 -0
- data/docs/reference/models/tag.md +41 -0
- data/docs/reference/models/user.md +65 -0
- data/docs/reference/policies.md +30 -0
- data/docs/reference/uploaders.md +19 -0
- data/docs/what-is-trek.md +37 -0
- data/esbuild.config.js +10 -7
- data/lib/generators/trek/templates/initializers/shrine.rb +1 -0
- data/lib/trek/version.rb +1 -1
- data/netlify.toml +4 -0
- data/package.json +10 -5
- data/renovate.json +4 -0
- data/yarn.lock +2015 -235
- metadata +48 -2
data/docs/index.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
# https://vitepress.dev/reference/default-theme-home-page
|
|
3
|
+
layout: home
|
|
4
|
+
|
|
5
|
+
hero:
|
|
6
|
+
name: "Trek"
|
|
7
|
+
text: "Take the easy path"
|
|
8
|
+
tagline: "A modern content management & back-office system for Ruby on Rails"
|
|
9
|
+
image:
|
|
10
|
+
src: /logo.svg
|
|
11
|
+
alt: Trek
|
|
12
|
+
actions:
|
|
13
|
+
- theme: brand
|
|
14
|
+
text: What's Trek?
|
|
15
|
+
link: /what-is-trek
|
|
16
|
+
- theme: alt
|
|
17
|
+
text: Quick start
|
|
18
|
+
link: /getting-started
|
|
19
|
+
|
|
20
|
+
features:
|
|
21
|
+
- icon: ๐
|
|
22
|
+
title: Ready out of the box
|
|
23
|
+
details: One installer sets up authentication, authorization, file uploads, emails, i18n and a polished admin UI โ with sensible conventions you can override later.
|
|
24
|
+
- icon: ๐
|
|
25
|
+
title: Modern content editing
|
|
26
|
+
details: A Tiptap-based rich text editor, hierarchical pages with versioning and SEO-friendly paths, plus reusable content fragments.
|
|
27
|
+
- icon: ๐
|
|
28
|
+
title: Scaffold-driven development
|
|
29
|
+
details: One command generates the model, admin panel, views, policy, routes and locales for any resource โ just like Rails scaffolding, but for your back-office.
|
|
30
|
+
- icon: ๐
|
|
31
|
+
title: Multilingual by default
|
|
32
|
+
details: Content and admin UI translations powered by Mobility and i18n-tasks, with optional automatic translation via DeepL.
|
|
33
|
+
- icon: ๐
|
|
34
|
+
title: Roles & policies built in
|
|
35
|
+
details: Sorcery authentication and ActionPolicy authorization with admin, editor, user, reader and guest roles, enforced across every panel.
|
|
36
|
+
- icon: ๐งฉ
|
|
37
|
+
title: Hotwire-native UI
|
|
38
|
+
details: ViewComponents, Stimulus and Turbo under the hood, with light & dark themes, Radix colors and a customizable design system.
|
|
39
|
+
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" x="0" y="0" style="enable-background:new 0 0 512 512" version="1.1" viewBox="0 0 512 512"><path d="M0 0h512v512H0z" style="fill:#6ae5ac"/><path d="M201.9 93.5v91h-91c0 50.2 40.7 91 91 91v52c0 50.2 40.7 91 91 91v-325h-91zM292.9 184.5c0 50.2 40.7 91 91 91v-91h-91z" style="fill:#2d2d2d"/><path d="M292.9 418.5v-91h91c-.1 50.2-40.8 91-91 91z" style="fill:#fff"/></svg>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Components
|
|
2
|
+
|
|
3
|
+
Trek's admin UI is built with [ViewComponent](https://viewcomponent.org), [Stimulus](https://stimulus.hotwired.dev) and [Turbo](https://turbo.hotwired.dev), styled with PostCSS and [Radix colors](https://www.radix-ui.com/colors), with light & dark themes.
|
|
4
|
+
|
|
5
|
+
## UI components
|
|
6
|
+
|
|
7
|
+
The main building blocks, all in the `Trek::` namespace:
|
|
8
|
+
|
|
9
|
+
| Component | Purpose |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `LayoutComponent` | admin layout, with header, menu and panel slots |
|
|
12
|
+
| `HeaderComponent` | top navigation bar |
|
|
13
|
+
| `MenuComponent` | sidebar navigation, with polymorphic slots |
|
|
14
|
+
| `BrandComponent` | logo, title & subtitle area |
|
|
15
|
+
| `DialogComponent` | modal dialogs, with configurable title and buttons |
|
|
16
|
+
| `TabsComponent` | tabbed interfaces |
|
|
17
|
+
| `ButtonComponent` | buttons & button-styled links |
|
|
18
|
+
| `ListComponent` | record lists |
|
|
19
|
+
| `PropertiesComponent` | key-value property display |
|
|
20
|
+
| `PaginationComponent` | Kaminari-backed pagination |
|
|
21
|
+
| `ToasterComponent` / `ToastComponent` | flash notifications |
|
|
22
|
+
| `HeadingComponent` | headings |
|
|
23
|
+
| `GateComponent` | authorization-based visibility gate |
|
|
24
|
+
| `IconComponent` | SVG [icon](/reference/icons) renderer |
|
|
25
|
+
|
|
26
|
+
## Form components
|
|
27
|
+
|
|
28
|
+
Rendered by the [form builder](/reference/forms) helpers, under `Trek::Form::`:
|
|
29
|
+
|
|
30
|
+
`GroupComponent`, `FieldsetComponent`, `ActionsComponent` (configurable aspect & classnames), `ErrorsComponent`, `TextFieldComponent`, `ImageFieldComponent`, `SoundFieldComponent`, `SwitchBoxComponent`, `CollectionSelectComponent`, `GroupedCollectionSelectComponent`, `LinkSelectComponent` and `ContentEditorComponent`.
|
|
31
|
+
|
|
32
|
+
## Content editor
|
|
33
|
+
|
|
34
|
+
`Trek::Form::ContentEditorComponent` wraps the [Tiptap](https://tiptap.dev) editor and accepts three options โ `nodes`, `blocks` and `floating` (all enabled by default) โ to toggle the node toolbar, block formatting and floating menu:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
f.content_editor :intro, nodes: false, floating: false
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Content is stored as ProseMirror JSON and rendered with [`formatted_content`](/reference/concerns/contentable). Image uploads (Uppy), link insertion and prompts are served by the [panel controllers](/reference/controllers).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Trek::Contentable
|
|
2
|
+
|
|
3
|
+
Utilities for rich text stored as ProseMirror JSON in a `content` column โ the format produced by Trek's [content editor](/reference/components#content-editor).
|
|
4
|
+
|
|
5
|
+
## Methods
|
|
6
|
+
|
|
7
|
+
| Method | Returns |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `parsed_content` | the content as a hash (parsing JSON strings if needed) |
|
|
10
|
+
| `formatted_content` | safe HTML, rendered through the [ProseMirror pipeline](/reference/formatters) |
|
|
11
|
+
| `content_text` | plain text extracted from all text nodes |
|
|
12
|
+
| `prosemirror_content?` | whether `content` is a valid ProseMirror document |
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
class Page < ApplicationRecord
|
|
18
|
+
include Trek::Contentable
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
page.formatted_content # => "<p>Hello <strong>world</strong></p>"
|
|
22
|
+
page.content_text # => "Hello world"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- A `content` jsonb column
|
|
28
|
+
- Used by [Page](/reference/models/page), [PageVersion](/reference/models/page-version) and [Fragment](/reference/models/fragment)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Trek::Formattable
|
|
2
|
+
|
|
3
|
+
Applies formatters to attributes before validation โ out of the box, [`Trek::TypographyFormatter`](/reference/formatters#typographyformatter) and its French typography rules.
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
class Page < ApplicationRecord
|
|
9
|
+
include Trek::Formattable
|
|
10
|
+
format_attributes Trek::TypographyFormatter, :title, :description
|
|
11
|
+
end
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`format_attributes(formatter_class, *attr_names)` registers the attributes; a `before_validation` callback runs the formatter on each of them.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
page = Page.new(title: "Un titre: voici...")
|
|
20
|
+
page.valid?
|
|
21
|
+
page.title # => "Un titre : voiciโฆ"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Works on plain strings **and** on ProseMirror documents (only `text` nodes are touched; the structure is preserved).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Trek::Fragmentable
|
|
2
|
+
|
|
3
|
+
Composes a record from reusable [fragments](/reference/models/fragment), based on a configuration matrix mapping record keys to fragment ids.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
# page_fragments configuration
|
|
9
|
+
page_fragments:
|
|
10
|
+
matrix:
|
|
11
|
+
home: [1, 2, 3]
|
|
12
|
+
about: [4, 5]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Methods
|
|
16
|
+
|
|
17
|
+
| Method | Purpose |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `fragments` | the Fragment records configured for this record's `key` |
|
|
20
|
+
| `fragments_attributes=` | bulk-updates fragments from nested form attributes |
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
class Page < ApplicationRecord
|
|
26
|
+
include Trek::Fragmentable
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
page = Page.find_by(key: "home")
|
|
30
|
+
page.fragments # => [Fragment(1), Fragment(2), Fragment(3)]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This is what powers the fragment editing UI on page forms in the admin.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Trek::Keyable
|
|
2
|
+
|
|
3
|
+
Batch lookup of records by their `key` attribute, preserving the requested order.
|
|
4
|
+
|
|
5
|
+
## Methods
|
|
6
|
+
|
|
7
|
+
| Method | Returns |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `Model.take_by_keys(keys)` | the records matching `keys`, **in the same order**, with `nil` for missing keys |
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
Page.take_by_keys(%w[services about missing])
|
|
15
|
+
# => [#<Page key="services">, #<Page key="about">, nil]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Handy for rendering a fixed set of pages or fragments in a specific order in your views.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Trek::Orderable
|
|
2
|
+
|
|
3
|
+
Position tracking with [acts_as_list](https://github.com/brendon/acts_as_list), scoped by parent.
|
|
4
|
+
|
|
5
|
+
## What it adds
|
|
6
|
+
|
|
7
|
+
- `acts_as_list scope: :parent, top_of_list: 0`
|
|
8
|
+
- An `ordered` scope (position order)
|
|
9
|
+
- All the `acts_as_list` instance methods: `insert_at`, `move_to_top`, `move_to_bottom`, `move_higher`, `move_lower`โฆ
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- A `position` integer column (and a `parent_id` column for the scope)
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
class MenuNode < ApplicationRecord
|
|
19
|
+
include Trek::Orderable
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
MenuNode.where(parent: menu).ordered
|
|
23
|
+
node.move_to_top
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This is what powers drag & drop reordering (SortableJS) in the admin lists.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Trek::Pageable
|
|
2
|
+
|
|
3
|
+
The bridge between your content models (articles, productsโฆ) and their [Page](/reference/models/page) representation. The model owns the data; the page owns the title, slug, SEO and position.
|
|
4
|
+
|
|
5
|
+
## What it adds
|
|
6
|
+
|
|
7
|
+
- `has_one :page, as: :pageable, dependent: :destroy, autosave: true` with `accepts_nested_attributes_for :page`
|
|
8
|
+
- Delegates `title`, `image`, `image_url`, `current_image`, `current_image_url`, `to_s` to the page
|
|
9
|
+
- `before_create` guarantees the page exists
|
|
10
|
+
- `to_param` uses the page slug on the public site (and the id in the admin)
|
|
11
|
+
|
|
12
|
+
## Class macros
|
|
13
|
+
|
|
14
|
+
| Macro | Purpose |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `has_position_from_page` | delegates `position` to the page and adds a `by_position` scope |
|
|
17
|
+
| `has_root_scope` | adds a `root` scope finding resources under their index page |
|
|
18
|
+
| `find_with_page_slug(slug)` | class method โ resource lookup by page slug |
|
|
19
|
+
|
|
20
|
+
## Navigation helpers
|
|
21
|
+
|
|
22
|
+
`parent?`, `parent`, `children?`, `children` โ traverse the page hierarchy from the resource side.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
class Article < ApplicationRecord
|
|
28
|
+
include Trek::Pageable
|
|
29
|
+
has_position_from_page
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
article = Article.create(page_attributes: { title: "My Article" })
|
|
33
|
+
article.title # => "My Article" (delegated)
|
|
34
|
+
article.to_param # => "my-article"
|
|
35
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Trek::Pages::Pathable
|
|
2
|
+
|
|
3
|
+
Manages a page's URL paths across locales: the current path, the full history (for redirects), and automatic regeneration when pages move in the tree.
|
|
4
|
+
|
|
5
|
+
## What it adds
|
|
6
|
+
|
|
7
|
+
- `belongs_to :current_path` and `has_many :paths` โ [PagePath](/reference/models/page-path)
|
|
8
|
+
- `after_save_commit`, paths are (re)created for the page **and all its descendants**
|
|
9
|
+
|
|
10
|
+
## Methods
|
|
11
|
+
|
|
12
|
+
| Method | Returns |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `Page.find_at_full_path(path)` | the page at this path (raises `ActiveRecord::RecordNotFound`) |
|
|
15
|
+
| `full_path` | the current path string |
|
|
16
|
+
| `full_path_from_tree` | the path computed from ancestor slugs |
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
parent = Page.create(title: "Services")
|
|
22
|
+
child = Page.create(title: "Design", parent: parent)
|
|
23
|
+
|
|
24
|
+
child.full_path # => "services/design"
|
|
25
|
+
Page.find_at_full_path("services/design") # => child
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Old paths remain in `page.paths`, which lets your front-end controllers redirect outdated URLs to `full_path`.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Trek::Phonable
|
|
2
|
+
|
|
3
|
+
Phone number validation, normalization and formatting, backed by [Phonelib](https://github.com/daddyz/phonelib).
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
class Organization < ApplicationRecord
|
|
9
|
+
include Trek::Phonable
|
|
10
|
+
phonable_attributes :phone, :fax
|
|
11
|
+
end
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
For each declared attribute:
|
|
15
|
+
|
|
16
|
+
- a validation (`phone: { possible: true, allow_blank: true }`)
|
|
17
|
+
- normalization to E.164 international format on save
|
|
18
|
+
- `formatted_{attr}` โ national format (`"01 23 45 67 89"`)
|
|
19
|
+
- `{attr}_with_country_code` โ E.164 (`"+33123456789"`)
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
org = Organization.create(phone: "0123456789")
|
|
25
|
+
org.phone # => "+33123456789"
|
|
26
|
+
org.formatted_phone # => "01 23 45 67 89"
|
|
27
|
+
org.phone_with_country_code # => "+33123456789"
|
|
28
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Trek::Pages::SearchEngineOptimizable
|
|
2
|
+
|
|
3
|
+
SEO metadata with sensible fallbacks: explicit title/description/image when set, model defaults otherwise (including the parent page's image).
|
|
4
|
+
|
|
5
|
+
## Methods
|
|
6
|
+
|
|
7
|
+
| Method | Returns |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `seo_values` | `{ title:, description:, image:, slug: }` with fallbacks applied |
|
|
10
|
+
| `meta_tags` | `seo_values` without the slug โ ready for your `<head>` |
|
|
11
|
+
| `default_title` / `default_description` / `default_image` | the fallback values |
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```slim
|
|
16
|
+
/ app/views/layouts/application.html.slim
|
|
17
|
+
- @page.meta_tags.each do |name, content|
|
|
18
|
+
meta name=name content=content
|
|
19
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Trek::Searchable
|
|
2
|
+
|
|
3
|
+
Accent-insensitive full-text search on a record's page title, using PostgreSQL's `unaccent` extension.
|
|
4
|
+
|
|
5
|
+
## What it adds
|
|
6
|
+
|
|
7
|
+
- A `search(query)` scope โ joins to the associated page and matches the title with an unaccented `ILIKE`; returns everything when the query is blank
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
class Article < ApplicationRecord
|
|
13
|
+
include Trek::Pageable
|
|
14
|
+
include Trek::Searchable
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Article.search("รฉlรจve") # matches "eleve", "รlรจve"โฆ
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Controller side
|
|
21
|
+
|
|
22
|
+
The `Trek::Search` controller concern wires this into index actions: it filters `@objects` by `params[:search]` whenever the model responds to `search` โ that's what powers the search field in admin lists.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Trek::Sectionable
|
|
2
|
+
|
|
3
|
+
Organizes a record's content into ordered, typed sections (hero, features, testimonialsโฆ).
|
|
4
|
+
|
|
5
|
+
## What it adds
|
|
6
|
+
|
|
7
|
+
- `has_many :sections, -> { ordered }, as: :sectionable, dependent: :destroy`
|
|
8
|
+
- `sections_instances` โ the unwrapped section objects, in order
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
class Page < ApplicationRecord
|
|
14
|
+
include Trek::Sectionable
|
|
15
|
+
|
|
16
|
+
SECTIONS = [HeroSection, FeaturesSection].freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
page.sections_instances.each do |section|
|
|
20
|
+
render section_component_for(section)
|
|
21
|
+
end
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Sections are opt-in per page via the `sectioned` boolean column. The including class defines the allowed section types through the `SECTIONS` constant.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Trek::Sluggable
|
|
2
|
+
|
|
3
|
+
Auto-generates locale-aware URL slugs before validation, for every available locale.
|
|
4
|
+
|
|
5
|
+
## Behavior
|
|
6
|
+
|
|
7
|
+
- `before_validation`, the slug is regenerated per locale: from a route translation of the page `key` when one exists, otherwise by parameterizing the SEO title
|
|
8
|
+
- Validates slug presence (except for the root page) and uniqueness among siblings (`scope: :parent_id`)
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
- [Translatable](/reference/concerns/translatable) with a translated `slug`
|
|
13
|
+
- A `key` attribute, a `root?` predicate and `seo_values` (provided by [SearchEngineOptimizable](/reference/concerns/search-engine-optimizable))
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
page = Page.new(key: "services", title: "Our Services")
|
|
19
|
+
page.save
|
|
20
|
+
page.slug # => "our-services"
|
|
21
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Trek::Taggable
|
|
2
|
+
|
|
3
|
+
Tag associations for any model, via the [taxonomy models](/reference/models/tag) (installed by the [taxonomies generator](/reference/generators/taxonomies)).
|
|
4
|
+
|
|
5
|
+
## What it adds
|
|
6
|
+
|
|
7
|
+
- `has_many :taggings, as: :taggable, dependent: :destroy`
|
|
8
|
+
- `has_many :tags, through: :taggings`
|
|
9
|
+
- `tag_names` โ the tag names as an array of strings
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
class Article < ApplicationRecord
|
|
15
|
+
include Trek::Taggable
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
article.tags << Tag.find_or_create_by(name: "Ruby", key: "ruby")
|
|
19
|
+
article.tag_names # => ["Ruby"]
|
|
20
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Trek::Translatable
|
|
2
|
+
|
|
3
|
+
Multi-language attributes, backed by [Mobility](https://github.com/shioyama/mobility). Translated values are stored in `jsonb` columns keyed by locale.
|
|
4
|
+
|
|
5
|
+
## API
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
class Page < ApplicationRecord
|
|
9
|
+
include Trek::Translatable
|
|
10
|
+
translate_attributes :title, :description
|
|
11
|
+
end
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`translate_attributes(*attr_names)` declares the translatable attributes (it delegates to Mobility's `translates`).
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
page = Page.create(title: "Home")
|
|
20
|
+
I18n.with_locale(:fr) { page.update(title: "Accueil") }
|
|
21
|
+
|
|
22
|
+
page.title # => "Home" (current locale)
|
|
23
|
+
I18n.with_locale(:fr) { page.title } # => "Accueil"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Stored as:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{ "en": "Home", "fr": "Accueil" }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- A `jsonb` column per translated attribute (default `{}`)
|
|
35
|
+
- Used by [Page](/reference/models/page), [PagePath](/reference/models/page-path), [Fragment](/reference/models/fragment) and [MenuNode](/reference/models/menu-node)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Trek::Pages::Versionable
|
|
2
|
+
|
|
3
|
+
Content versioning for [pages](/reference/models/page): snapshot the current content and image as a [PageVersion](/reference/models/page-version), then keep editing while the snapshot stays live.
|
|
4
|
+
|
|
5
|
+
## What it adds
|
|
6
|
+
|
|
7
|
+
- `has_many :versions` (most recent first) with a `current` sub-scope, plus `accepts_nested_attributes_for :versions`
|
|
8
|
+
|
|
9
|
+
## Methods
|
|
10
|
+
|
|
11
|
+
| Method | Purpose |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `create_current_version_from_page!` | snapshots the page's `content` and `image` as the new current version |
|
|
14
|
+
| `current_version` | the live version (or `nil` when not versioned) |
|
|
15
|
+
| `current_content` / `current_sections` | content from the current version, falling back to the page itself |
|
|
16
|
+
| `current_image` / `current_image_url` | image from the current version, falling back to the page, then its parent |
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
Versioning is opt-in per page via the `versioned` boolean column:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
page.update(versioned: true)
|
|
24
|
+
page.create_current_version_from_page! # freeze what's live
|
|
25
|
+
page.update(content: new_draft) # keep editing safely
|
|
26
|
+
page.current_content # => still the frozen snapshot
|
|
27
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Controllers
|
|
2
|
+
|
|
3
|
+
- **`Trek::BaseController`** โ the base for all admin controllers: authentication, authorization, localization, the `admin` layout and Trek's [form builder](/reference/forms).
|
|
4
|
+
- **`Trek::ResourceController`** โ extends `BaseController` with the full CRUD (`index`, `show`, `new`, `create`, `edit`, `update`, `destroy`), wired to ActionPolicy (`authorized_scope`, `authorize!`) and ordering. Scaffolded controllers inherit from it and only need to declare their `model`:
|
|
5
|
+
|
|
6
|
+
```ruby
|
|
7
|
+
module Admin
|
|
8
|
+
class BooksController < Trek::ResourceController
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def model
|
|
12
|
+
Book
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- **Panel controllers** (`Trek::Panels::*`) โ support endpoints used by the admin UI: image uploads (Uppy), link insertion and content editor prompts.
|
|
19
|
+
|
|
20
|
+
## Mixins
|
|
21
|
+
|
|
22
|
+
- `include Trek::Pagination` โ paginates the index with Kaminari
|
|
23
|
+
- `include Trek::Search` โ filters the index by `params[:search]` when the model is [Searchable](/reference/concerns/searchable)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Environment variables
|
|
2
|
+
|
|
3
|
+
| Variable | Purpose |
|
|
4
|
+
| --- | --- |
|
|
5
|
+
| `ADMIN_EMAIL` / `ADMIN_PASSWORD` | credentials used by [`trek:admin:user`](/reference/generators/admin-user) |
|
|
6
|
+
| `POSTMARK_API_TOKEN` | Postmark email delivery (auto-injected into credentials by the [installer](/reference/generators/install)) |
|
|
7
|
+
| `DEEPL_AUTH_KEY` | automatic locale translation in the [scaffold generator](/reference/generators/scaffold) |
|
|
8
|
+
| `FEATURE_STORAGE` | `local` (development) or `s3` (production) |
|
|
9
|
+
| `S3_BUCKET`, `S3_REGION`, `S3_ENDPOINT`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `S3_BASE_URL` | S3 storage configuration for [uploads](/reference/uploaders) |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Formatters
|
|
2
|
+
|
|
3
|
+
## TypographyFormatter
|
|
4
|
+
|
|
5
|
+
`Trek::TypographyFormatter` applies French typography rules to strings **and** ProseMirror documents (only `text` nodes are modified):
|
|
6
|
+
|
|
7
|
+
- non-breaking spaces before `: ; ! ? % โฌ $ ยป` and after `ยซ`, dashes, `nยฐ`
|
|
8
|
+
- non-breaking spaces before units (km, kg, h, min, ยฐCโฆ) and between digit groups
|
|
9
|
+
- `...` โ `โฆ`, ASCII apostrophes โ typographic `'`
|
|
10
|
+
- removes double spaces and spaces before periods/commas
|
|
11
|
+
|
|
12
|
+
Wire it to attributes with [Formattable](/reference/concerns/formattable):
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
include Trek::Formattable
|
|
16
|
+
format_attributes Trek::TypographyFormatter, :title
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## ProseMirror โ HTML pipeline
|
|
20
|
+
|
|
21
|
+
`formatted_content` (from [Contentable](/reference/concerns/contentable)) renders ProseMirror JSON to safe HTML through the `prosemirror_to_html` gem, extended with:
|
|
22
|
+
|
|
23
|
+
| Piece | Role |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `Nodes::ImageBlock` | renders `image_block` nodes as figures (Shrine image, alt, caption, size) |
|
|
26
|
+
| `Nodes::PromptsBlock` | renders `prompts_block` nodes as call-to-action buttons |
|
|
27
|
+
| `RemoveEmptyParagraphsFormatter` | strips empty `<p></p>` tags |
|
|
28
|
+
| `GlobalIdToLinksFormatter` | resolves `gid://` references in links to real record URLs |
|
|
29
|
+
|
|
30
|
+
The pipeline is orchestrated by the `Prosemirror` model โ extend it with your own node renderers when you add custom blocks to the editor.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Forms
|
|
2
|
+
|
|
3
|
+
Trek ships `Trek::FormBuilder`, an extension of [`ViewComponent::Form`](https://github.com/pantographe/view_component-form)'s builder, automatically used in admin views. On top of the standard helpers, it provides:
|
|
4
|
+
|
|
5
|
+
| Helper | Renders |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `f.group` | a label + input wrapper with errors |
|
|
8
|
+
| `f.content_editor` | the Tiptap rich text editor |
|
|
9
|
+
| `f.image_field` | an image uploader with thumbnail preview (Uppy + Shrine) |
|
|
10
|
+
| `f.sound_field` | an audio file uploader |
|
|
11
|
+
| `f.switch_box` | a toggle switch |
|
|
12
|
+
| `f.link_select` | a link picker (internal pages & external links) |
|
|
13
|
+
|
|
14
|
+
Each helper renders the matching component from the [`Trek::Form` namespace](/reference/components#form-components).
|
|
15
|
+
|
|
16
|
+
```slim
|
|
17
|
+
= form_with model: [:admin, @book] do |f|
|
|
18
|
+
= f.group :title
|
|
19
|
+
= f.content_editor :intro, floating: false
|
|
20
|
+
= f.switch_box :published
|
|
21
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Admin user
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
rails g trek:admin:user
|
|
5
|
+
# or (soon)
|
|
6
|
+
trek admin:user
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Creates an admin user with the credentials defined by the `ADMIN_EMAIL` and `ADMIN_PASSWORD` ENV variables, typically set in `.env`:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
# .env
|
|
13
|
+
ADMIN_EMAIL=admin@example.com
|
|
14
|
+
ADMIN_PASSWORD=a-strong-password
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The created user gets the `admin` [role](/reference/models/user#roles), which makes it `privileged?` and grants it full access in the default [policies](/reference/policies).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Installer
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
rails g trek:install
|
|
5
|
+
# or (soon)
|
|
6
|
+
trek install
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Trek provides an install generator, that will:
|
|
10
|
+
|
|
11
|
+
- install all dependencies
|
|
12
|
+
- create the `User` model and include Trek's concerns
|
|
13
|
+
- create the `Page`, `PagePath` and `PageVersion` models and include Trek's concerns
|
|
14
|
+
- create the `Fragment` model and include Trek's concerns
|
|
15
|
+
- create the relevant policies
|
|
16
|
+
- generate and run the relevant migrations
|
|
17
|
+
|
|
18
|
+
## Sub-generators
|
|
19
|
+
|
|
20
|
+
The installer orchestrates dedicated sub-generators, all under the `trek:install:*` namespace. Each one can also be run individually, e.g. `rails g trek:install:healthcheck`.
|
|
21
|
+
|
|
22
|
+
| Area | What gets set up |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| Authentication | Sorcery, the `UserSession` form model, login/logout & password reset panels |
|
|
25
|
+
| Authorization | ActionPolicy and the base policies |
|
|
26
|
+
| Models | `User`, `Page`, `PagePath`, `PageVersion`, `Fragment`, `MenuNode`, `ExternalLink`, `Current` โ with migrations |
|
|
27
|
+
| Attachments | Shrine configuration and the uploaders |
|
|
28
|
+
| Emails | Postmark and the user mailer |
|
|
29
|
+
| I18n | rails-i18n, Mobility, i18n-tasks and the locale files |
|
|
30
|
+
| Admin panels | Pages, fragments, users, sessions, password resets, routes, dashboard |
|
|
31
|
+
| Pagination | Kaminari |
|
|
32
|
+
| Tooling | esbuild, PostCSS, EditorConfig, Procfiles, Makefile, health check endpoint |
|
|
33
|
+
| Linting | Standard, ESLint, Stylelint, Prettier, Slim Lint, Lefthook git hooks |
|
|
34
|
+
| Security | Brakeman, bundler-audit, ruby_audit |
|
|
35
|
+
| CI | GitLab CI configuration |
|
|
36
|
+
|
|
37
|
+
## ENV variables read by the installer
|
|
38
|
+
|
|
39
|
+
Define these **before** running the installer to have them injected automatically:
|
|
40
|
+
|
|
41
|
+
- `POSTMARK_API_TOKEN` โ injected into the Rails credentials for email delivery
|
|
42
|
+
- `DEEPL_AUTH_KEY` โ enables automatic locale translation in the [scaffold generator](/reference/generators/scaffold)
|