Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/controllers/spina/admin/layout_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class LayoutController < AdminController
before_action :set_account
before_action :set_locale
before_action :set_breadcrumb
before_action :set_parts_context
before_action :get_layout_parts

admin_section :content
Expand Down Expand Up @@ -42,5 +43,9 @@ def set_account
def set_locale
@locale = params[:locale] || I18n.default_locale
end

def set_parts_context
@parts_context = :layout
end
end
end
47 changes: 32 additions & 15 deletions app/helpers/spina/admin/pages_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,29 @@ def asset_available?(path)
end
end

def build_parts(partable, parts)
def build_parts(partable, parts, context: parts_context_for(partable))
I18n.with_locale(@locale) do
parts.map do |part|
part_attributes = current_theme.parts.find { |p| p[:name].to_s == part.to_s }
part_attributes = find_part_definition(part, context: context, view_template: current_view_template)
partable.part(part_attributes)
end
end
end

def find_part_definition(name, context: parts_context, view_template: current_view_template)
Spina::Current.theme.part_definitions_for(context, view_template: view_template).find do |part|
part[:name].to_s == name.to_s
end
end

def current_view_template
@page&.view_template
end

def parts_context
@parts_context ||= :page
end

def parts_partial_namespace(part_type)
part_type.tableize.sub(/\Aspina\/parts\//, "")
end
Expand All @@ -27,21 +41,24 @@ def option_label(part, value)

private

def check_propshaft_asset(path)
if Rails.configuration.assets.compile
Rails.application.assets.load_path.find(path).present? rescue false
else
Rails.application.assets.asset_for(path).present? rescue false
end
end
def parts_context_for(partable)
partable.is_a?(Spina::Account) ? :layout : parts_context
end

def check_sprockets_asset(path)
if Rails.configuration.assets.compile
Rails.application.precompiled_assets.include?(path)
else
Rails.application.assets_manifest.assets[path].present?
end
def check_propshaft_asset(path)
if Rails.configuration.assets.compile
Rails.application.assets.load_path.find(path).present? rescue false
else
Rails.application.assets.asset_for(path).present? rescue false
end
end

def check_sprockets_asset(path)
if Rails.configuration.assets.compile
Rails.application.precompiled_assets.include?(path)
else
Rails.application.assets_manifest.assets[path].present?
end
end
end
end
2 changes: 1 addition & 1 deletion app/views/spina/admin/parts/repeaters/_fields.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<%= f.hidden_field :title %>
<%= f.hidden_field :name %>

<% parts = current_theme.parts.find{|p|p[:name].to_s == f.object.name.to_s}&.dig(:parts) || [] %>
<% parts = find_part_definition(f.object.name)&.dig(:parts) || [] %>

<%= f.fields_for :parts, build_parts(f.object, parts) do |ff| %>
<%= ff.hidden_field :title %>
Expand Down
9 changes: 8 additions & 1 deletion docs/v2/2_themes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,11 @@

The installer generates a few initializers that contain necessary configuration for Spina.

In the initializers folder there's a new folder named themes. Inside you will find a configuration file named default.rb. This file contains all of your theme-specific settings. You can define multiple parts, view templates and custom pages.
In the initializers folder there's a new folder named themes. Inside you will find a configuration file named default.rb. This file contains your theme's global settings: custom pages, navigations, embeds, and so on.

Page and layout part definitions live in `app/templates/spina/default/`:

- `layout.rb` — global layout parts (`Spina.define_layout_parts`)
- `homepage.rb`, `show.rb`, etc. — page type definitions (`Spina.define_template`)

See [Parts](themes/1_parts.md), [View templates](themes/2_view_templates.md), and [Layout parts](themes/3_layout_parts.md) for details.
25 changes: 25 additions & 0 deletions docs/v2/getting_started/4_upgrading.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Upgrading

# Migrating theme configuration to page template files

Defining `theme.parts` and `theme.view_templates` directly in your theme initializer is deprecated and will be removed in a future major version of Spina.

The new style moves part definitions into `app/templates/spina/your_theme/`:

- `layout.rb` for global layout parts
- one file per page type (e.g. `homepage.rb`, `show.rb`)

To migrate automatically:

```bash
bin/rails spina:theme:migrate_templates[default]
```

This will:

1. Generate page template files under `app/templates/spina/default/`
2. Generate `layout.rb` if your theme has layout parts (including sectioned/tabbed layout parts)

3. Replace your theme initializer with a slim version
4. Back up the original to `config/initializers/themes/default.rb.bak`

If you have both the old configuration and template files present, Spina will raise an error at boot. Remove `theme.parts` and `theme.view_templates` from your theme initializer, or run the migrator.

# Upgrading from v2.0 to v2.1

v2.1 is a big upgrade for Spina's interface. All JavaScript, CSS and views have been refactored. Gone are jQuery, Turbolinks, custom stylesheets and Haml.
Expand Down
16 changes: 7 additions & 9 deletions docs/v2/rendering_content/3_rich_text.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ This part returns HTML. You can use the following helper to render it as HTML:

## Theme configuration

```
config.parts = [
# ...
{
name: "main_content",
title: "Main content",
part_type: "Spina::Parts::Text"
}
]
Define text parts in your page template file:

```ruby
# app/templates/spina/default/show.rb
Spina.define_template :show do
part :main_content, :text, title: "Main content"
end
```

## View template example
Expand Down
22 changes: 10 additions & 12 deletions docs/v2/rendering_content/8_repeating_content.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,16 @@ Nesting content is a very powerful feature of Spina. In your theme config you ca

## Theme configuration

```
config.parts = [
# ...
{ name: "title", title: "Title", part_type: "Spina::Parts::Line" },
{ name: "image", title: "Image", part_type: "Spina::Parts::Image" },
{
name: "portfolio",
title: "Portfolio",
parts: %w(title image),
part_type: "Spina::Parts::Repeater"
}
]
Define repeating content in your page template file:

```ruby
# app/templates/spina/default/show.rb
Spina.define_template :show do
repeater :portfolio do
part :title, :line
part :image, :image
end
end
```

## View template example
Expand Down
100 changes: 48 additions & 52 deletions docs/v2/themes/1_parts.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,89 +12,85 @@ A page in Spina has many parts. By default these parts can be one of the followi

These are the building blocks of your view templates. You can have an unlimited number of parts in a page. We prefer to keep the number of parts to a minimum so that managing your pages isn't too complex.

Spina uses an initializer to create the basic building blocks of your page. There are three steps to add a new building block or part to your app:
Parts are defined in page template files under `app/templates/spina/your_theme/`. There are three steps to add a new part to your app:

- Set up a new part in the initializer
- Set the new initializer into a view template
- Define the part in a page template file
- Add it to the view

**Create a new page part**
## Theme configuration

When you install Spina, you will see the following in config/initializers/themes/default.rb
Your theme initializer only contains global theme settings:

```ruby
# config/initializers/themes/default.rb
Spina::Theme.register do |theme|
theme.name = 'default'
theme.title = 'Default theme'

theme.parts = [
{name: 'text', title: "Body", hint: "Your main content", part_type: "Spina::Parts::Text"}
]

theme.view_templates = [
{name: 'homepage', title: 'Homepage', parts: %w(text)},
{name: 'show', title: 'Page', parts: %w(text)}
]

theme.name = "default"
theme.title = "Default theme"

theme.custom_pages = [
{name: 'homepage', title: "Homepage", deletable: false, view_template: "homepage"},
{name: "homepage", title: "Homepage", deletable: false, view_template: "homepage"}
]

theme.navigations = [
{name: 'main', label: 'Main navigation'}
{name: "main", label: "Main navigation"}
]

theme.layout_parts = []
theme.resources = []
theme.plugins = []

theme.embeds = []
end

```

Right now, the default theme is applying a title to the page, with a simple text div below it. Go to /admin on your app and have a look. Edit the textbox and go to preview the page.
Page templates are loaded automatically from `app/templates/spina/default/`.

Let's say I wanted to add another text box below this called portfolio. First I would add another hash to the parts array like so:
## Create a new page part

When you install Spina, you will see page template files like this:

```ruby
theme.parts = [{
name: 'content',
title: 'Content',
part_type: 'Spina::Parts::Text'
}, {
name: 'portfolio', # added this part
title: 'Portfolio',
part_type: 'Spina::Parts::Text'
}]
# app/templates/spina/default/show.rb
Spina.define_template :show do
title "Page"
part :text, :text, title: "Body", hint: "Your main content"
end
```

**Add it to the view template**
Built-in part types can be referenced as symbols (`:line`, `:text`, `:image`, and so on). Custom or extension parts use the full class name as a string:

Now, we need to update the view_templates config. These view templates provide customization for the different views you might want. For example, you may have a 'blog' view or an 'about' view which add different parts. For this example we will add the portfolio part into the 'Default' view template.
```ruby
part :case_study, "MyApp::Parts::CaseStudy"
```

Let's say you wanted to add another text box below the body called portfolio. Update the page template:

```ruby
theme.view_templates = [{
name: 'homepage',
title: 'Homepage',
parts: %w(content)
}, {
name: 'show',
title: 'Default',
description: 'A simple page',
usage: 'Use for your content',
parts: %w(content portfolio) # added 'portfolio'
}]
# app/templates/spina/default/show.rb
Spina.define_template :show do
title "Page"
part :text, :text, title: "Body", hint: "Your main content"
part :portfolio, :text, title: "Portfolio"
end
```

**Add it to the view**
## Add it to the view

Finally, let's go to views/default/pages/show.html.erb and add the following:
Finally, go to `app/views/default/pages/show.html.erb` and add:

```erb
<h1><%= current_page.title %></h1>

<%= content.html :text %>
<%= content.html :portfolio %> # added this line
<%= content.html :portfolio %>
```

Refresh the page form in the admin section. You should see another text box below the content box.

## Migrating from the old configuration style

Previously, all parts were defined in the theme initializer using `theme.parts` and `theme.view_templates`. That style still works, but is deprecated and will be removed in a future major version of Spina.

To migrate an existing theme automatically:

```bash
bin/rails spina:theme:migrate_templates[default]
```

We have successfully added another textbox! Refresh the page form in the admin section. You should see another text box below the content box.
This generates page template files, creates `layout.rb` if needed, and replaces your theme initializer with a slim version. Your original file is backed up to `config/initializers/themes/default.rb.bak`.
56 changes: 55 additions & 1 deletion docs/v2/themes/2_view_templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,58 @@

Each theme typically has a few different view templates which make up your website. By default Spina generates a homepage and show template.

The views for these templates are stored in app/views/default/pages.
View templates are defined as Ruby files in `app/templates/spina/your_theme/`:

```ruby
# app/templates/spina/default/homepage.rb
Spina.define_template :homepage do
title "Homepage"
description "The front page of your website"

part :headline, :line
part :body, :text
part :hero_image, :image
end
```

The views for these templates are stored in `app/views/default/pages/`.

## Repeating content

Use a `repeater` block for nested content:

```ruby
Spina.define_template :show do
title "Page"

repeater :portfolio do
part :title, :line
part :image, :image
part :description, :text
end
end
```

## Template metadata

You can set additional options on a page template:

```ruby
Spina.define_template :blogpost do
title "Blogpost"
description "Article template"
usage "Use for blog articles"
exclude_from %w[main]
layout "article"
end
```

## Custom template path

By default, Spina loads templates from `app/templates/spina/{theme.name}/`. You can override this in your theme initializer:

```ruby
theme.load_templates_from "app/templates/spina/default"
```

In development, Spina watches `config/initializers/themes/` and `app/templates/` (recursively) so new or changed template files reload automatically. Custom `load_templates_from` directories outside `app/templates/` are also watched after the theme registers.
Loading
Loading