` with
~~~ erb
<%= render_elements only: 'blog_post' %>
~~~
to embed the **blog_post** elements into the page layout view.
Inside 'Sidebar' comment replace
~~~ erb
~~~
with
~~~ erb
<%= render_elements only: 'categories_block' %>
~~~
and replace the content of `
` with
~~~ erb
<%= render_elements only: 'featured' %>
~~~
Now you can run
~~~ bash
rails server
~~~
to start the server.
---
URL: "/v7/how_to_create_a_contact_form.html"
LLMS_URL: "/v7/how_to_create_a_contact_form.md"
---
# How To: Create a contact form
This guide covers the configuration setup and create process of a
regular contact form:
- Setup Alchemy mailer
- Customize the views
## Additional Rails and application setup
Alchemy relies on Rails' [ActionMailer](https://guides.rubyonrails.org/action_mailer_basics.html) to send emails.
You will need to configure ActionMailer to use an SMTP service like Gmail, Sendgrid, Mailgun, Mandrill, Amazon SES, etc.
## Mailer Settings
This is the default mailer configuration in `config/alchemy/config.yml`:
~~~ yaml
mailer:
page_layout_name: contact
forward_to_page: false
mail_success_page: thanks
mail_from: your.mail@your-domain.com
mail_to: your.mail@your-domain.com
fields: [salutation, firstname, lastname, address, zip, city, phone, email, message]
validate_fields: [lastname, email]
~~~
- **page_layout_name** `String` (Optional)
A `Alchemy::PageLayout` name. Used for rendering the contact form.
- **forward_to_page** `Boolean` (Optional)
If set to true the mailer redirects to the page defined with mail_success_page option.
- **mail_success_page** `String` (Optional)
A page urlname that should be displayed after successfully sending the mail.
- **mail_from** `String` (Optional)
The email address the mail is send from.
- **mail_to** `String` (Optional)
The email address the mail is send to.
- **fields** `Array`
Field names of your contact form. These fields become attributes on the `Alchemy::Message` Model
- **validate_fields** `Array`
Field names of your contact form that should be validated for presence.
::: tip NOTE
All optional setting should be manageable through the content management user. So creating an element that set these values is **highly recommended**.
:::
## Creating a contact form element
Describe a new Element with this options inside your `elements.yml` file:
~~~ yaml
- name: contactform
ingredients:
- role: mail_from
type: Text
validate:
- presence
- format: email
- role: mail_to
type: Text
validate:
- presence
- format: email
- role: subject
type: Text
validate:
- presence
- role: success_page
type: Page
validate:
- presence
~~~
::: tip NOTE
The fields `mail_to`, `mail_from`, `subject` and `success_page` are recommended. The `Alchemy::MessagesController` uses them to send your mails. That way your customer has full control over these values inside his contact form element.
:::
::: warning INFO
The validations are optional, but **highly recommended**.
:::
Create a page layout for your contact page in the `page_layouts.yml` file:
~~~ yaml
- name: contact
unique: true
cache: false
elements:
- pageheading
- heading
- contactform
autogenerate:
- contactform
~~~
::: warning INFO
Disabling the page caching is **very important**!
:::
## Example contact form
Use the `rails g alchemy:elements --skip` generator to create the view files.
### The contact form view
We are using the great `simple_form` gem in this example.
If this gem is not already installed, you have to add
~~~ bash
bundle add simple_form
~~~
Then open `app/views/alchemy/elements/_contactform.html.erb` in your text editor
and replace the content with:
~~~ erb
<%= simple_form_for(@message ||= Alchemy::Message.new) do |form| %>
<%= form.input :firstname %>
<%= form.input :lastname %>
<%= form.input :email %>
<%= form.input :message, as: 'text' %>
<%= form.hidden_field :contact_form_id, value: element.id %>
<%= form.button :submit %>
<% end %>
~~~
::: warning INFO
See the `hidden_field`? This is important, or the messages mailer can't do all the magic for you.
:::
If you use different or additional input symbols like 'company' , 'age' etc. make sure to adapt the fields in the mailer configuration (`config/alchemy/config.yml`).
::: tip
Please have a look at the [simple form documentation](https://github.com/plataformatec/simple_form#readme) for further infos about the various config options.
:::
## Translating validation messages
All validation messages are passed through `::I18n.t` so you can translate it in your language yml file.
### Example Translation
~~~ yaml
de:
activemodel:
attributes:
alchemy/message:
firstname: Vorname
lastname: Nachname
~~~
If you would like to use same vocabulary in different context: e.g in the `app/views/alchemy/elements/_contactform_view.html.erb`
~~~ erb
<%= form.input ... %>
<%= form.input :firstname , label: t(:sender, scope: 'my_forms.contactform') %>
<%= form.input ... %>
~~~
Your language yml should look like this: (`config/locales/de.yml`)
~~~ yaml
de:
my_forms:
contactform:
sender: Sender
~~~
Now you can reuse the label by using the `t` function while defining label.
---
URL: "/v7/how_to_create_custom_ingredients.html"
LLMS_URL: "/v7/how_to_create_custom_ingredients.md"
---
# Creating own Ingredients
Alchemys powerful content storage technology is build around ingredients. They are ActiveRecord models that have an editor and a presentation view partial.
Alchemy has lots of [build in ingredients](https://github.com/AlchemyCMS/alchemy_cms/tree/main/app/models/alchemy/ingredients). But you can create your own ingredient and even associate them with your existing models. This guide shows how.
* Use the generator to create a new ingredient
* Associate an ingredient to your model
## Using The Ingredient Generator
The ingredient generator is a wrapper around the Rails model generator. It generates the ingredient model for you.
~~~ bash
bin/rails g alchemy:ingredient MyIngredient
~~~
::: tip INFO
Ingredients are created under the `Alchemy::Ingredients` namespace.
:::
This is what the generated model looks like:
~~~ ruby
# app/models/alchemy/ingredients/my_ingredient.rb
module Alchemy
module Ingredients
class MyIngredient < Alchemy::Ingredient
# Set additional attributes that get stored in the `data` JSON column
# store_accessor :data, :some, :attribute
# Set a related_object alias for convenience
# related_object_alias :some_association_name, class_name: "Some::Klass"
end
end
end
~~~
Alchemy stores the main value in the `value` (a `Text`) column.
If you want to store additional values in another column, please add it as attribute to the `data` [`store_accessor`](https://api.rubyonrails.org/classes/ActiveRecord/Store.html). Rails will create accessor methods for you.
## The ingredient views
Every ingredient has to have two views:
* One for presenting
* One for editing
### The view partial
The view partial is used by the `render_elements` helper to present the ingredient to the user.
It is yours. Adjust it to your needs. You can access the value with the `ingredient` method of the `content` object instance.
~~~ erb
<%= my_ingredient_view.ingredient %>
~~~
### The editor partial
The editor partial is basically a set of form fields holding values of your ingredient. It is rendered inside the element editor view form object.
This is just what the generator creates for you:
~~~ erb
<%= content_tag :div,
class: my_ingredient_editor.css_classes,
data: my_ingredient_editor.data_attributes do %>
<%= element_form.fields_for(:ingredients, my_ingredient_editor.ingredient) do |f| %>
<%= ingredient_label(my_ingredient_editor) %>
<%= f.text_field :value %>
<% end %>
<% end %>
~~~
But this is yours. Feel free to adjust it to your needs.
Just make shure that you provide form fields that Alchemy can use to update your object in the database.
## Associations
You can associate every ActiveRecord based model with an ingredient. In this example we want to connect an existing `Person` model to an element, so we can associate it with an Alchemy page.
Just use the `related_object_alias` method to tell Alchemy the foreign key to use for the association.
### Set the foreign key
~~~ ruby
# app/models/alchemy/ingredients/person.rb
module Alchemy
module Ingredients
class Person < Alchemy::Ingredient
related_object_alias :person, class_name: "My::Person"
end
end
end
~~~
### Accessing your model instance
That's it. Everything else is handled by Alchemy. You can now access the associated `My::Person` model with the `person` method.
~~~ erb
<%= person_view.person.firstname %>
~~~
Now you can use your new ingredient in any element you want as shown in the [Elements guide](elements).
---
URL: "/v7/how_to_create_modules.html"
LLMS_URL: "/v7/how_to_create_modules.md"
---
# Creating custom Modules
Basicly there are two ways to set up a module:
* Use Alchemy as your Rails admin backend
* Create a new Engine
## Generator
Alchemy provides a rails generator for custom modules, that can be invoked like this
~~~ bash
bin/rails generate alchemy:module your_module_name
~~~
## Using Alchemy as your Rails admin backend
Hence every module is a subsection of a Rails application or a gem you can roll out your own admin backend if you like.
By using Alchemy's backend interface you get a whole bunch of things:
1. Authentication and Authorization
2. A highly and fine grained customizable resource manager (CRUD interface)
3. A nicely styled user interface
And it's easy as this:
~~~ ruby
module Admin
class YourResourcesController < Alchemy::Admin::ResourcesController
end
end
~~~
You can overwrite the default controller actions and views as you like.
An instance-variable named after the resource (i.e. `@your_resource` for `YourResource`) is defined for use in your views.
After you setting up your routes you have to take care for control access:
## Authorization
Rights and roles are set in a CanCan Ability model.
Create the file if it doesn't exist already. It has to be registered manually, by invoking `Alchemy.register_ability MyModuleAbility` in the module initializer.
~~~ ruby
# app/models/my_module_ability.rb
class MyModuleAbility
include CanCan::Ability
def initialize(user)
if !user.blank? && user.is_admin?
can :manage, YourResource
can :manage, :admin_your_resources
end
end
end
~~~
More information about authorization can be found in the docs of the [`cancancan` gem](https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities)
## Routing
### In an engine
~~~ ruby
# config/routes.rb
YourApp::Application.routes.draw do
...
mount YourAlchemyModule::Engine => '/'
mount Alchemy::Engine => '/'
end
~~~
### In your app
~~~ ruby
# config/routes.rb
YourApp::Application.routes.draw do
resources :your_resource
namespace :admin do
resources :your_resource
end
mount Alchemy::Engine => '/'
end
~~~
::: tip
Due to Alchemy's strong routes it is **strongly recommended** to mount your engine before Alchemy.
:::
## Registering the module
Last but not least the module needs to be registered to AlchemyCMS.
### When using your host app
~~~ ruby
# my_host_app/config/initializers/alchemy_modules.rb
Alchemy::Modules.register_module({
name: 'name_of_your_module',
order: 1, # The position in main navigation, if you have more than 1 module.
navigation: {
name: 'modules.products', # The name in the main navigation (translated via I18n).
controller: '/admin/products', # The controller that will be used.
action: 'index', # The controller action that will be used.
icon: 'module_icon', # Class of icon that will be rendered as navigation icon.
sub_navigation: [
{
name: 'modules.products', # The name for the subnavigation tab (translated via I18n).
controller: '/admin/products', # Controller that will be used.
action: 'index' # Controller action that will be used.
},
{
name: 'modules.variants', # The name for the subnavigation tab (translated via I18n).
controller: '/admin/variants', # Controller that will be used.
action: 'index' # Controller action that will be used.
}
]
}
})
# Register the module ability
Alchemy.register_ability MyModuleAbility
~~~
NOTE: As an alternative to the module icon you can pass: `image: '/alchemy/icon_of_your_module.png'`
### Using a Rails engine
~~~ ruby
# my_engine/config/initializers/alchemy.rb
Alchemy::Modules.register_module({
name: 'name_of_your_module',
engine_name: 'name_of_your_engine' # The engine_name set in your Rails::Engine class.
order: 1, # The position in main navigation, if you have more than 1 module.
navigation: {
name: 'modules.products', # The name in the main navigation (translated via I18n).
controller: '/admin/products', # The controller that will be used.
action: 'index', # The controller action that will be used.
icon: 'module_icon', # Class of icon that will be rendered as navigation icon.
sub_navigation: [
{
name: 'modules.products', # The name for the subnavigation tab (translated via I18n).
controller: '/admin/products', # Controller that will be used.
action: 'index' # Controller action that will be used.
},
{
name: 'modules.variants', # The name for the subnavigation tab (translated via I18n).
controller: '/admin/variants', # Controller that will be used.
action: 'index' # Controller action that will be used.
}
]
}
})
# Register the module ability
Alchemy.register_ability MyModuleAbility
~~~
## Translate your module names
All module names are passed through I18n within a `alchemy` namespace.
The name will be used as translation key.
You should namespace your module name (with i.e. `modules`), to prevent conflicts.
### Example
Given a module name `modules.products` your translation has to be:
~~~ yaml
# config/locales/de.yml
de:
alchemy:
modules:
products: Produkte
~~~
## Customizing the built in views
Alchemy makes use of Rails templates and partials to render your model's resource views. You can overwrite each of them in your app or engine.
Just place a file with the same name in your `app/views/admin/plural-name-of-your-resource/` folder and Rails will pick that up instead of the Alchemy one.
You can find the list of all templates and partials Alchemy provides [on GitHub](https://github.com/AlchemyCMS/alchemy_cms/tree/7.2-stable/app/views/alchemy/admin/resources).
### The resource form
Alchemy uses [a `_form` partial](https://github.com/AlchemyCMS/alchemy_cms/blob/7.2-stable/app/views/alchemy/admin/resources/_form.html.erb) to render the fields for your resource's edit view.
It looks like this
```erb
<%= alchemy_form_for resource_instance_variable, url: resource_path(resource_instance_variable, search_filter_params) do |f| %>
<% resource_handler.editable_attributes.each do |attribute| %>
<% if relation = attribute[:relation] %>
<%= f.association relation[:name].to_sym,
collection: relation[:collection],
label_method: relation[:attr_method],
include_blank: Alchemy.t(:blank, scope: 'resources.relation_select'),
input_html: {is: 'alchemy-select'} %>
<% elsif attribute[:type].in? %i[date time datetime] %>
<%= f.datepicker attribute[:name], resource_attribute_field_options(attribute) %>
<% elsif attribute[:enum].present? %>
<%= f.input attribute[:name],
collection: attribute[:enum],
include_blank: Alchemy.t(:blank, scope: 'resources.relation_select'),
input_html: {is: 'alchemy-select'} %>
<% else %>
<%= f.input attribute[:name], resource_attribute_field_options(attribute) %>
<% end %>
<% end %>
<% if f.object.respond_to?(:tag_list) %>
<%= render Alchemy::Admin::TagsAutocomplete.new do %>
<%= f.input :tag_list, input_html: { value: f.object.tag_list.join(",") } %>
<% end %>
<% end %>
<%= f.submit Alchemy.t(:save) %>
<% end %>
```
To customize the form place a file with the same content into your apps view folder and adjust it to your needs.
### The resource table
Alchemy uses [a `_table` partial](https://github.com/AlchemyCMS/alchemy_cms/blob/7.2-stable/app/views/alchemy/admin/resources/_table.html.erb) to render the table for your resource's index view.
The default table looks like this
```erb
<% if resources_instance_variable.any? %>
<% if local_assigns[:icon] %>
|
<% end %>
<% resource_handler.sorted_attributes.each do |attribute| %>
<%= sort_link [:resource_url_proxy, @query],
sortable_resource_header_column(attribute),
resource_handler.model.human_attribute_name(attribute[:name]),
default_order: attribute[:type].to_s =~ /date|time/ ? 'desc' : 'asc' %>
|
<% end %>
|
<%= render_resources(icon: local_assigns[:icon]) %>
<% elsif search_filter_params[:q].present? %>
<%= Alchemy.t('Nothing found') %>
<% end %>
<%= paginate resources_instance_variable, scope: resource_url_proxy, theme: 'alchemy' %>
```
You can customize it by adding a `_table.html.erb` partial into your resources view folder of your app or engine and adjust it accordingly.
### Examples
Alchemy dogfeeds it's own resource views to render it's admin interface. You can take them as example as starting point.
---
URL: "/v7/how_to_customize_tinymce.html"
LLMS_URL: "/v7/how_to_customize_tinymce.md"
---
# Customizing the Richtext Editor
This guide explains how to override the default settings of the TinyMCE richtext editor and customize it.
After reading this guide you will be able to:
* [Overriding default settings](#override-the-default-settings)
* [Customizing the toolbar](#custom-toolbar-buttons)
* [Changing the default stylesheet](#setting-a-custom-stylesheet)
## Override the default settings
To override the default settings you have to create a `tinymce.rb` initializer file into `config/initializers`.
Alchemys default settings for TinyMCE can be found in the [documentation](https://www.rubydoc.info/github/AlchemyCMS/alchemy_cms/Alchemy/Tinymce)
### A Minimal Editor
Let's reduce the options for formatting the text to a minimum.
Because Alchemy merges the default options and your custom options you just need to pass the options you want to override to `Alchemy::Tinymce.init`.
~~~ ruby
# config/initializers/tinymce.rb
Alchemy::Tinymce.init = {
toolbar: [
'bold italic underline',
'pastetext charmap code | undo redo | alchemy_link unlink | alignleft aligncenter alignright alignjustify'
]
}
~~~
Now only this three basic formatting options are available in the first toolbar to your editors.
In the second toolbar are now the align formatting options available.
::: warning NOTE
Overriding options always in the initializer applies to all richtext editors in all elements in Alchemy.
:::
## Custom toolbar buttons
You can customize the TinyMCE toolbar with a lot of buttons. Find them in the [TinyMCE documentation](http://www.tinymce.com/wiki.php/Controls)
### Adding a format select box
Let's add a format select to our editors so they can insert headlines into their texts.
In the TinyMCE documentation we found our configuration option:
~~~ js
tinymce.init({
...
block_formats: "Paragraph=p;Header 1=h1;Header 2=h2;Header 3=h3"
});
~~~
We just have to _convert_ this javacript object into a ruby hash.
And we need to insert the format select into the set of buttons.
~~~ ruby
# config/initializers/tinymce.rb
Alchemy::Tinymce.init = {
toolbar: [
'bold italic underline | strikethrough subscript superscript | numlist bullist indent outdent | removeformat | fullscreen',
'pastetext | formatselect | charmap code | undo redo | alchemy_link unlink'
],
block_formats: "Header 2=h2;Paragraph=p"
}
~~~
That's it. Now the editor can format a block of text as headline 2. Nice!
::: warning NOTE
Custom buttons always appear in all richtext editors in all elements in Alchemy.
:::
## Adding editor plugins
It is possible to extend the tinymce editor with plugins.
All you have to do is to download the tinymce plugin and copy the folder to
`vendor/assets/javascripts/tinymce/plugins`
and add the following content to `config/initializers/tinymce.rb`
~~~ ruby
Alchemy::Tinymce.init = {
plugins: Alchemy::Tinymce.plugins + ['colorpicker'],
toolbar: [
'bold italic underline | strikethrough subscript superscript | numlist bullist indent outdent | removeformat | alignleft aligncenter alignright | fullscreen',
'pastetext charmap hr | undo redo | alchemy_link unlink anchor | code | colorpicker'
]
}
~~~
Another example configuration could be:
~~~ ruby
Alchemy::Tinymce.plugins += ['textcolor']
Alchemy::Tinymce.init = {
toolbar: [
'bold italic underline | strikethrough subscript superscript | numlist bullist indent outdent | removeformat | fullscreen',
'pastetext | charmap code | undo redo | alchemy_link unlink | help | forecolor backcolor'
],
}
~~~
This example adds the textcolor plugin and adds its button to the toolbar.
## Setting a custom stylesheet
It is a good practice to set the stylesheet TinyMCE uses to display text in the editor area to the one you use to render the website.
::: tip INFO
Please read more on custom stylesheets in the [TinyMCE documentation](http://www.tinymce.com/wiki.php/Configuration:content_css)
:::
### Create the css file
Put a `tinymce_content.css` stylesheet into your `app/assets/stylesheets` folder.
### Setting the stylesheet
~~~ ruby
# config/initializers/tinymce.rb
Alchemy::Tinymce.init = {
content_css: "/assets/tinymce_content.css"
}
~~~
### Compile the asset for production
In production mode (on your server) Rails uses compiled assets to speed up the rendering of your page.
As default Rails only compiles the `application.css` stylesheet. But you can tell Rails to compile our custom TinyMCE stylesheet as well.
Open the `config/environments/production.rb` file in your editor and insert this line:
~~~ ruby
config.assets.precompile += %w( tinymce_content.css )
~~~
## Per element customization
You can also set TinyMCE configuration options per element.
~~~ yaml
# elements.yml
- name: minimal_text
ingredients:
- role: text
type: Richtext
settings:
tinymce:
toolbar:
- 'bold italic underline',
- 'pastetext charmap code | undo redo | alchemy_link unlink | alignleft aligncenter alignright alignjustify'
~~~
## Configuration Syntax
You can set any [TinyMCE configuration option](http://www.tinymce.com/wiki.php/Configuration). Just remember to _convert_ the javascript syntax into ruby.
### Convertions Tips
Javascript and Ruby has very similiar syntax. Especially if you use the new Ruby 1.9 Hash syntax. So converting is no big deal.
#### Object to Hash
~~~ js
{ "key" : "value" } => { key: "value" }
~~~
#### Array to Array
Arrays have the same syntax. So you can just take them as they are.
~~~ js
["1", "2", "3", "4"] => ["1", "2", "3", "4"]
~~~
#### Object with Array as value
~~~ js
{ "key" : ["value1", "value2"] } => { key: ["value1", "value2"] }
~~~
#### Object with Object as value
~~~ js
{ "key" : { "value" : ["value1", "value2"] } } => { key: { value: ["value1", "value2"] } }
~~~
---
URL: "/v7/how_to_deploy_on_heroku.html"
LLMS_URL: "/v7/how_to_deploy_on_heroku.md"
---
# Heroku deployment
[Heroku](https://heroku.com) is a cloud platform as a service. You can start with a free plan and run it for lifetime. In this guide you will learn how to deploy your existing Alchemy Site on Heroku. After finishing this guide you will know:
* How to prepare your local Alchemy installation for Heroku
* How to deploy code changes to Heroku
## Prerequisites
We assume you already have a running Alchemy application on your local machine. In the following steps we call it `my-project`. Please replace it name with your actual name. All following shell commands need to be executed within your applications folder from the command shell.
We also assume you already have an account on Heroku. [Otherwise signup now.](https://signup.heroku.com)
You need the [Heroku Toolbelt](https://devcenter.heroku.com/articles/getting-started-with-ruby#set-up) for your OS installed to get access to the Heroku Command Line utility. Login with the heroku command from your command shell as described in the devcenter.
The last thing we assume is you already have an Amazon AWS account. [Otherwise signup now.](https://aws.amazon.com)
## Amazon (AWS) setup
### Create S3 buckets
Heroku does not provide data storage on its filesystem (beside a small amount for the application code and some temporary data). So we have to store our pictures and attachments outside of Heroku. Amazon S3 comes for the rescue. We need two S3 buckets, one for Alchemy pictures and another for Alchemy attachments.
Please create two S3 buckets now and name them as follows:
* `my-project-alchemy-pictures`
* `my-project-alchemy-attachments`
Please make sure to save the keys (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) Amazon gives you and keep them safe.
## Heroku setup
### Create a new application
Create a [new Application from your Dashboard](https://dashboard.heroku.com/new).
### Install Memcache add-on
We use Memcache to store and deliver cropped pictures and the page cache. This is important because otherwise page publishing and image cropping will not work accordingly.
Lets add MemCachier to the application.
~~~ bash
heroku addons:create memcachier:dev
~~~
This installs MemCachier with a 25M cache store for free.
### Set environment variables
Run the following commands on your command shell to set some environment variables on your heroku account to provide the S3 key and bucket names. Make sure to replace `my-project` and `your-key` with the correct information.
~~~ bash
heroku config:set AWS_REGION=bucket-region
heroku config:set AWS_ACCESS_KEY_ID=your-key-id
heroku config:set AWS_SECRET_ACCESS_KEY=your-key
heroku config:set AWS_PICTURES_BUCKET=my-project-alchemy-pictures
heroku config:set AWS_ATTACHMENTS_BUCKET=my-project-alchemy-attachments
~~~
## Code adjustments
### Add gems
We need to add a few gems to the `Gemfile` of the Application.
~~~ ruby
gem 'thin'
group :production do
gem 'pg'
gem 'rails_12factor'
gem 'rack-cache', require: 'rack/cache'
gem "dragonfly-s3_data_store"
gem 'dalli'
gem 'kgio'
end
~~~
Now bundle the gems
~~~ bash
bundle install
~~~
::: tip
Heroku uses PostgreSQL. If you want to use a different database for your development environment you need to define `group: "development"` for the relevant database adapter gem.
:::
### Use thin webserver instead of webrick
Webrick is not the best choice for running rails applications in production. Lets tell Heroku we want to use thin instead. Create a new file in your applications root directory named `Procfile` and paste the following line.
~~~ ruby
web: bundle exec thin start -p $PORT -e $RACK_ENV
~~~
### Create initializer
Create a new initializer `config/initializers/alchemy_file_storage.rb` in your application code to overwrite the information how and where Alchemy pictures and attachments are stored. You can just paste the following code and adjust the region if necessary. Be aware to use the same region as you did when creating the buckets.
We set up the application to use Amazon S3 storage on production while using the filesystem in other environments.
~~~ ruby
if Rails.env.production?
require 'dragonfly'
require 'dragonfly/s3_data_store'
aws_defaults = {
access_key_id: Rails.configuration.aws_access_key_id,
secret_access_key: Rails.configuration.aws_secret_access_key,
region: Rails.configuration.aws_region,
storage_headers: {'x-amz-acl' => 'public-read'},
url_scheme: 'https'
}
Dragonfly.app(:alchemy_pictures).configure do
plugin :imagemagick
datastore :s3,
{ bucket_name: Rails.configuration.aws_pictures_bucket }.merge(aws_defaults)
end
Dragonfly.app(:alchemy_attachments).configure do
datastore :s3,
{ bucket_name: Rails.configuration.aws_attachments_bucket }.merge(aws_defaults)
end
end
~~~
### Configure rack-cache for production environment
~~~ ruby
# Enable Rack::Cache to put a simple HTTP cache in front of your application
# Add `rack-cache` to your Gemfile before enabling this.
# For large-scale production use, consider using a caching reverse proxy like
# NGINX, varnish or squid.
config.action_dispatch.rack_cache = true
# use the Dalli client for its cache-store
config.cache_store = :dalli_store
client = Dalli::Client.new((ENV["MEMCACHIER_SERVERS"] || "").split(","),
:username => ENV["MEMCACHIER_USERNAME"],
:password => ENV["MEMCACHIER_PASSWORD"],
:failover => true,
:socket_timeout => 1.5,
:socket_failure_delay => 0.2,
:value_max_bytes => 10485760)
config.action_dispatch.rack_cache = {
:metastore => client,
:entitystore => client
}
config.static_cache_control = "public, max-age=311040000"
~~~
### Make S3 informations accessible to Rails configuration
Paste the following lines to your `config/environments/production.rb` inside the configure block. The Rails application gets access to the S3 information provided via environment variables.
~~~ ruby
config.aws_region = ENV['AWS_REGION']
config.aws_access_key_id = ENV['AWS_ACCESS_KEY_ID']
config.aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
config.aws_pictures_bucket = ENV['AWS_PICTURES_BUCKET']
config.aws_attachments_bucket = ENV['AWS_ATTACHMENTS_BUCKET']
~~~
### Prepare your seeds.rb
In order to populate the remote database with basic information (which is needed for running Alchemy) please paste the following to your `db/seeds.rb`. You can skip this step if you already have it.
~~~ ruby
Alchemy::Seeder.seed!
~~~
## Git repository
### Create a new repo
If your application code is already checked-in to a git repository you can skip this. Otherwise initialize a new git repository now.
~~~ bash
git init
~~~
### Make sure you do not store certain files in your git repo
We do not want certain files in git. Please make sure you ignore at least following files/folders in your `.gitignore` file.
~~~ bash
/tmp/*
/log/*
/uploads
/public/assets
/public/pictures
/config/database.yml
.DS_Store
/db/*.sqlite3
~~~
### Commit your files to the git repository
~~~ bash
git add .
git commit -am "Initial commit"
~~~
::: tip
Never store any critical informations like database or ssh passwords in your git repository, even if you have a private one.
:::
### Add heroku as a git remote
You need to add heroku as a remote git repository. Each time you push the master branch to heroku the application will be deployed.
~~~ bash
heroku git:remote -a my-project
~~~
## Deployment
### First deployment
Lets deploy the application to heroku the first time! Its easy as pushing our repository to the heroku git remote.
~~~ bash
git push heroku master
~~~
### Migrate and seed the database
We need to migrate the database and populate it with Alchemy seed data.
~~~ bash
heroku run rake db:migrate
heroku run rake db:seed
~~~
### Verify the deployment
Open a browser and enter the domain Heroku gave you. Usually its a subdomain consisting of your project name and the domain herokuapp.com (e.g.: https://my-project.herokuapp.com)
::: tip
You can always get your domain from the settings on Heroku. E.g.: https://dashboard.heroku.com/apps/my-project/settings (replace my-project with the correct name)
:::
### Deploy the app later on
Each time you want to deploy to Heroku just push your git commits to the heroku remote:
~~~ bash
git push heroku master
~~~
---
URL: "/v7/how_to_extend_alchemy.html"
LLMS_URL: "/v7/how_to_extend_alchemy.md"
---
# Extending Alchemy
At some point you may come across the need of adding some behavior to already existing controllers or models, that Alchemy provides. Obviously you could just copy the files, insert them into your projects and edit them there, but this isn't a really clean solution.
* This section will give you a hint on how to extend existing Alchemy files without overriding everything.
## Tell Rails to load our extensions
Tell Rails to load our extensions by adding some lines into `config/application.rb`
~~~ ruby
# config/application.rb
config.to_prepare do
Dir.glob(Rails.root.join('app', '**', '*_extension.rb')) do |f|
Rails.configuration.cache_classes ? require(f) : load(f)
end
end
~~~
## Adding an extension
After you set up the loading for our extensions, we can actually start making some. Lets assume you want to add a before_action method to the `Alchemy::PagesController`. You go into your host app and add a file in `app/controllers/alchemy/` called `pages_controller_extension.rb`. It's important to add the `_extension` to the filename so Rails will load them.
Into this file you add the following code:
~~~ ruby
module AlchemyPagesControllerExtension
end
Alchemy::PagesController.prepend AlchemyPagesControllerExtension
~~~
Into this block you can add anything you want your controller to do. As already mentioned, we want to add a before_action to this controller for some show off. It could then look like this:
~~~ ruby
module AlchemyPagesControllerExtension
def prepended(base)
base.before_action :some_method, only: :show
end
end
~~~
The last step is adding some_method to your `ApplicationController`. This is done like you are used to in Rails.
---
URL: "/v7/ingredients.html"
LLMS_URL: "/v7/ingredients.md"
prev: "elements"
next: "render_images"
---
# Ingredients
## Overview
Ingredients store the actual content of your site. They are stored as instances of `Alchemy::Ingredient` subclasses on each `Alchemy::Element` instance.
Alchemy uses Rails [Single Table Inheritance](https://guides.rubyonrails.org/association_basics.html#single-table-inheritance-sti) to store the values in the same table.
Alchemy comes with a lot of predefined ingredients for the regular needs of a website project. Combine them like a chemestry kit into [elements](elements).
Ingredients are Rails models. It is pretty easy to [add your own ingredient class](how_to_create_custom_ingredients) as well.
## Definition
Ingredients are defined as `ingredients` on an [element definition](elements#defining-elements).
### Global ingredient settings
When defining ingredients, you need to provide a `role` and `type`. You can set hints and default values as well.
~~~ yaml
# config/alchemy/elements.yml
- name: article
ingredients:
- role: headline
type: Headline
hint: This is the headline
- role: color
type: Text
settings:
input_type: color
- role: copy
type: Richtext
default: Lorem ipsum dolor
as_element_title: true
~~~
* **role** `String` _required_
A lowercased **unique** (per element) role of the ingredient. Separate words needs to be underscored.
* **type** `String` _required_
An type this ingredient is of. Alchemy has lots of built in ingredients for [simple text](#text), [rich text](#richtext), [images](#picture), [booleans](#boolean) and more.
* **hint** `String|Symbol|Boolean`
A hint for the user in the admin frontend that describes what the ingredient is used for. The hint is translatable if you provide an I18n translation `Symbol` instead of a `String`. You may also set it to `true` to default to the I18n key `alchemy.ingredients_hints.your_ingredient_role`.
* **default** `String`
The default text to prefill newly created elements. You may also use a symbol to set it to the I18n key `alchemy.default_ingredients_texts.your_ingredient_role`
* **as_element_title** `Boolean`
For the displayed element title, the first ingredient is used. Use this setting to override this behaviour and show other ingredient as element title.
* **settings** `Hash`
A set of options to configure the ingredient. Each ingredient has its own set of options listed below.
### Individual ingredient settings
Each ingredient type can have its own type of settings.
To configure these settings you have to pass them into its `settings` key in the `elements.yml`.
~~~ yaml {5-6}
- name: my_element
ingredients:
- role: headline
type: Text
settings:
linkable: true
~~~
## Text
Stores plain text of 255 chars max.
Use this for a headline, or a product name. The editor is renderd as a single lined input field. The view output will be sanitized and HTML escaped.
### Settings
* **linkable** `Boolean`
If set to `true`, the user can add a link to the text.
* **input_type** `String`
Change the [input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types) of the form field displayed in the editor.
### Example
~~~ yaml
- name: button
type: Text
settings:
linkable: true
~~~
## Headline
Stores headline text of 255 chars max.
Use this for HTML headings. The editor is renderd as a single lined input field with level and optional size selects. The view output will be a heading element.
### Settings
* **anchor** `Boolean` or the `String` `from_value`
If set to `true`, the user can add a custom anchor to the headline.
If set to `from_value` the anchor will be created from the value.
The anchor will be added as `id` to the heading tag.
* **levels** `Array` of `Integer`s
Set the available [heading levels](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements) that the editor can choose from.
By default it is 1 to 6.
* **sizes** `Array` of `Integer`s
Optionally add a listy of sizes that the editor can choose a heading css size. Added as class (`.h2`) to the heading element. Use this of
### Example
~~~ yaml
- name: headline
type: Headline
settings:
levels: [2, 3]
sizes: [1, 2]
~~~
## Richtext
Used to store paragraphs of formatted text.
The editor is rendered as a textarea with embedded Tinymce Editor.
### Settings
You can customize the Tinymce editor of a single element instance.
* **tinymce** `Hash`
### Example
~~~ yaml
- name: text
type: Richtext
settings:
tinymce:
style_formats:
- title: 'Subheadline'
block: 'h3'
~~~
::: tip INFO
See the [Tinymce customization guide](how_to_customize_tinymce) for all available options
:::
## Picture
Store references to pictures the editor assigns from the library.
The editor partial is rendered as a picture editor with a lot of options (i.e. image cropper).
The view partial renders the assigned picture, resizes it and crops it if needed.
::: tip INFO
See the [rendering images](render_images) guide for further information on the powerful image rendering engine of Alchemy.
:::
### Settings
* **size** `String`
The size the image should be downsized to. I.e. 400x300
* **crop** `Boolean` (default: false)
Crop the image to given size. This also enables the build in cropper tool.
* **srcset** `Array
`
A list of sizes of this image uses as sources list. Best used with the `sizes` setting for implementing responsive images.
* **sizes** `Array`
A list of screen sizes the image sources should be used for. Best used with the `srcset` setting for implementing responsive images.
* **linkable** `Boolean`
If set to `false`, the user cannot add a link to the picture.
## Datetime
Use this to store a `DateTime` value. Renders a datepicker in the editor partial.
The view output is passed through Rails' I18n Library, so it is fully localizable.
### Settings
* **date_format** `String|Symbol`
Either a `String` with the format of [Rubys `strftime`](https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/Date.html#method-i-strftime) or a `Symbol` of a [date/time format](https://guides.rubyonrails.org/i18n.html#adding-date-time-formats) defined in your locale files.
## Html
Useful to store HTML code (i.e. a embed, or tracking code).
::: warning
The view renders the raw, not sanitized or escaped output.
**So be careful!**
:::
## Boolean
Stores a `Boolean` value in the database. Renders a checkbox in the editor partial.
## Select
Renders a select box in the editor partial and stores the value as `String`.
Useful for letting your user select from a limited set of choices.
### Settings
* **select_values** `Array`
A list of values your users can select from. Use [a two dimensional array](https://guides.rubyonrails.org/form_helpers.html#the-select-and-option-tags) for having value and text pairs.
### Example
~~~ yaml
- name: width
type: Select
settings:
select_values: ['200', '300', '400']
~~~
::: tip
If you need dynamic values (ie, a from a product catalogue), please [create a custom ingredient class](how_to_create_custom_ingredients) that provides the values.
:::
## Link
Stores a url in the database. Useful for linking things, where the editor should not set the linked text itself.
::: tip
If you want the linked text to be editable by the editor use the [**Text**](#text) with `linkable: true` option instead.
:::
## Number
Stores a number in the database. Useful for slider durations, price, lengths, etc. The number value gets translated via Rails' [`number_to_human`](https://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_human) helper.
### Settings
* **input_type** `String|Symbol`
The input type to render the editor with. Defaults to `number`. Can be set to `range` instead.
* **step** `Integer|Decimal`
The step on the editor input.
* **min** `Integer|Decimal`
The minimum number allowed on the editor input.
* **max** `Integer|Decimal`
The maximum number allowed on the editor input.
* **unit** `String`
The unit displayed on the editor input addon. Also used as `unit` for the `number_to_human` helper.
## Page
References an [Alchemy::Page](pages).
Renders a select box in the editor partial.
Useful for contact form follow up pages or other use cases where you want to reference another page.
### Example
~~~ yaml
- name: contact_form
ingredients:
- role: follow_up_page
type: Page
~~~
## Rendering ingredients within your element
Similar to [rendering elements within layouts](elements.html#render_elements_in_your_layout), ingredients are rendered on an element.
Page layouts use the `<%= render_elements %>` helper to load an elements partial, elements use the `<%= element_view_for %>` helper to expose a block that makes it easy to access the ingredients (instances of `Alchemy::Ingredient` subclasses) of the element.
For example. With an `article` element like this:
~~~ yaml
# config/alchemy/elements.yml
- name: article
ingredients:
- role: headline
type: Text
- role: color
type: Text
settings:
input_type: color
- role: copy
type: Richtext
~~~
The `_article.html.erb` template generated by `rails alchemy:elements --skip` would look like this:
~~~erb
<%- cache(article) do -%>
<%= element_view_for(article) do |el| -%>
<%= el.render :headline %>
<%= el.render :color %>
<%= el.render :copy %>
<%- end -%>
<%- end -%>
~~~
Note how `element_view_for` allows you to call `el.render` on ingredients within the block.
::: tip
`element_view_for` wraps the block in a `` with a set ID and class by default, but this is [customizeable](https://github.com/AlchemyCMS/alchemy_cms/blob/main/app/helpers/alchemy/elements_block_helper.rb#L137) if you pass in arguments:
`<%= element_view_for(article, tag: :span, id: 'custom_id', class: 'custom_class') do |el| -%>`
:::
Without using the `element_view_for` helper, you can still access ingredients:
`article.ingredient_by_role('headline')&.value`
But the `el.render` helper takes care of generating the appropriate DOM elements to display the ingredient based on its type. It is recommended you rely on these helpers unless you are comfortable with the structure of the ingredient model you are trying to render.
Just like `element_view_for`, you can pass `options` and `html_options` to `el.render`:
~~~erb
<%= el.render :headline, options = {}, html_options = {} %>
~~~
You can check out the [`ElementViewHelper#render` helper](https://github.com/AlchemyCMS/alchemy_cms/blob/b8cc62493693a070f7457081760708bf28c13e34/app/helpers/alchemy/elements_block_helper.rb#L31) for details.
::: warning NOTE
Options are not universally applied by all element types. For example, `ElementRichText` ignores `html_options` since it's already in HTML format and isn't wrapped in any special div. And you can't prevent the `ElementPicture` from generating an `
![]()
` tag.
:::
### Options
The `options = {}` hash is used for formatting the child elements' tags. You can pass `false` to not include tags inside the wrapper element. For more, see the source code [here](https://github.com/AlchemyCMS/alchemy_cms/blob/56e95a07446fb6832676acfc1e8c7047d1b80495/app/helpers/alchemy/elements_block_helper.rb#L128) and [here](https://github.com/AlchemyCMS/alchemy_cms/blob/main/app/helpers/alchemy/elements_helper.rb#L193)
### Html_options
The `html_option = {}` are:
* `:tag` - The HTML tag to be used for the wrapping element
* `:id` - The wrapper tag's DOM ID.
* `:class` - The wrapper tag's DOM class.
---
URL: "/v7/pages.html"
LLMS_URL: "/v7/pages.md"
prev: "about"
next: "elements"
---
# Pages
## Overview
In contrary to most CMSs pages in Alchemy **do not hold the actual content**. The actual content is stored in [ingredients](ingredients) inside of [elements](elements) pages are built of.
Pages have a unique friendly url and are organized as nested tree and represent the structure of your website.
Beside a language, a page has attributes for name, title, visibility, published and restriction status and all SEO relevant attributes like meta tags and meta descriptions.
Every page has a [definition](#defining-page-layouts) which defines additional properties like caching, uniqueness and it defines [elements](elements) that can be placed on that Page.
## Defining page types
Page types are defined in the `config/alchemy/page_layouts.yml` file.
Every page type needs at least a name. You don't need to set every option. It depends on what you need for pages with that type.
### Recommended settings
* **name** `String` *required*
The name of the page type used for views and inside the database. You can render a page by type with the `render_page_layout(name)` helper.
* **elements** `Array`
A list of element names that can be placed on this type of page i.e. `[text, picture]`.
[Elements](elements) are defined inside the `elements.yml` file.
* **autogenerate** `Array`
A list of element names that are autogenerated after creating a Page of that type.
* **unique** `Boolean` (Default `false`)
Pass `true` and the user can only choose this page type once inside a language tree.
* **cache** `Boolean` (Default `true`)
Pass `false` to disable the cache headers for this kind of pages. **Recommended for contact forms** and such likes.
* **layoutpage** `Boolean` (Default `false`)
Layout pages (or [global pages](#global-pages)) are outside the normal page tree and can be used to place "global" Elements like a header and footer.
* **taggable** `Boolean` (Default `false`)
Pass `true` to be able to assign tags within page settings.
### Optional settings
* **hide** `Boolean` (Default `false`)
Pass `true` to hide this page type from the user.
* **searchresults** `Boolean` (Default `false`)
Pass `true` to use this type of page for rendering the search results of the build in fulltext search.
* **feed** `Boolean` (Default `false`)
Pass `true` to enable a RSS feed of news elements from this page.
* **controller** `String`
Controller to use instead of the default `Alchemy::PagesController`
* **action** `String`
Controllers action to use instead of the default `Alchemy::PagesController#show`
### Example
Lets say you want to create a contact page with a headline element, a contactform and a text element on it.
This page should be unique, because you don't want to give your content manager the possibility to create more than one contact form.
This page must not be cached, because of validation messages and user specific form content.
We also want to autogenerate the headline and the contactform element after the page gets created.
~~~ yaml
# config/alchemy/page_layouts.yml
- name: contact
cache: false
unique: true
elements: [headline, contactform, text]
autogenerate: [headline, contactform]
~~~
::: warning NOTE
Please ensure to restart the Rails server after editing `page_layouts.yml`.
:::
## Page templates
Each page type (called `page_layout` in Alchemy) has a Rails view partial which is yielded on the Rails application layout (`app/views/layouts/application.html.erb`).
All page type partials live in the `app/views/alchemy/page_layouts` folder.
They are named after the pages `page_layout` you defined in the `page_layouts.yml` file.
::: warning NOTE
If no page type partial is found for a page, the `_standard.html.erb` partial gets rendered instead.
:::
### Template generator
There is no need to create these partials manually. AlchemyCMS comes with a Rails generator task which creates these partials for you.
So after defining the page layouts, you can generate all the corresponding partials for them.
~~~ bash
bin/rails g alchemy:page_layouts --skip
~~~
Using the example above, which defines a contact page type, the generator will create a partial named `_contact.html.erb`.
::: tip
You can pass `--template-engine` or `-e` as option to use one of `haml`, `slim` and `erb`. The default depends on your default template engine in your Rails host app.
:::
### Rendering elements
Alchemy does not place any HTML markup in your generated page layouts partial.
So:
~~~ erb
<%= render_elements %>
~~~
is all you will see. Feel free to customize the HTML so it fits your needs.
::: tip
The `render_elements` view helper has lots of options. Please have a look at the [`Alchemy::ElementsHelper` documentation](https://www.rubydoc.info/github/AlchemyCMS/alchemy_cms/Alchemy/ElementsHelper#render_elements-instance_method)
:::
## Global pages
Global pages (or layout pages) are pages that are not in the default page tree (your navigation). They will never get rendered on its own. Use them to store shared elements that should be rendered on multiple other pages (ie. footer, header, tracking codes etc) or somewhere directly on the application layout.
To define a global page set `layoutpage: true` in the page type definition of that page.
### Render an element from a global page
To render an element from a global page use the `from_page` option of the `render_elements` helper.
#### Example
~~~ erb
<%= render_elements only: 'news_teaser', from_page: Alchemy::Page.find_by(name: 'sidebar') %>
~~~
::: tip
You can pass a `page_layout` name as a `String`, an `Array` of page type names, or an instance of a certain `Alchemy::Page`.
:::
## Caching
If using the global caching option (defined in `config/alchemy/config.yml`) - which is enabled by default - your page requests will deliver cache headers. Most browsers, CDNs and proxys use these headers to cache the page.
::: tip
You can disable cache headers for certain pages by using the `cache: false` setting.
:::
### Russian doll caching
You can use Rails' "Russian-Doll-Caching" to cache page templates.
```erb
<% cache @page do %>
<%= render_elements %>
<% end %>
```
::: warning
Be sure to not cache page templates that have elements with forms on it, like contact or comment forms. Rails' csrf protection token is placed inside the `