Module: Alchemy

Defined in:
app/models/alchemy/tag.rb,
app/models/alchemy/node.rb,
app/models/alchemy/page.rb,
app/models/alchemy/site.rb,
app/jobs/alchemy/base_job.rb,
app/models/alchemy/current.rb,
app/models/alchemy/element.rb,
app/models/alchemy/message.rb,
app/models/alchemy/picture.rb,
app/models/alchemy/language.rb,
app/models/alchemy/attachment.rb,
app/models/alchemy/ingredient.rb,
app/models/alchemy/page_mutex.rb,
app/helpers/alchemy/url_helper.rb,
app/models/alchemy/base_record.rb,
app/models/alchemy/folded_page.rb,
app/models/alchemy/picture/url.rb,
app/models/alchemy/site/layout.rb,
app/services/alchemy/copy_page.rb,
app/helpers/alchemy/base_helper.rb,
app/mailers/alchemy/base_mailer.rb,
app/models/alchemy/page_version.rb,
app/helpers/alchemy/pages_helper.rb,
app/models/alchemy/eager_loading.rb,
app/models/alchemy/page/url_path.rb,
app/models/alchemy/picture_thumb.rb,
app/components/alchemy/admin/icon.rb,
app/jobs/alchemy/publish_page_job.rb,
app/models/alchemy/attachment/url.rb,
app/models/alchemy/page/publisher.rb,
app/models/alchemy/element_to_page.rb,
app/models/alchemy/picture_variant.rb,
app/helpers/alchemy/elements_helper.rb,
app/mailers/alchemy/messages_mailer.rb,
app/models/alchemy/ingredients/file.rb,
app/models/alchemy/ingredients/html.rb,
app/models/alchemy/ingredients/link.rb,
app/models/alchemy/ingredients/node.rb,
app/models/alchemy/ingredients/page.rb,
app/models/alchemy/ingredients/text.rb,
app/models/alchemy/page/page_naming.rb,
app/models/alchemy/page/page_scopes.rb,
app/models/concerns/alchemy/dom_ids.rb,
app/components/alchemy/admin/message.rb,
app/models/alchemy/ingredients/audio.rb,
app/models/alchemy/ingredients/video.rb,
app/models/alchemy/page/page_layouts.rb,
app/models/alchemy/page/page_natures.rb,
app/models/alchemy/picture_thumb/uid.rb,
app/services/alchemy/delete_elements.rb,
app/services/alchemy/tag_validations.rb,
app/decorators/alchemy/element_editor.rb,
app/helpers/alchemy/admin/base_helper.rb,
app/helpers/alchemy/admin/form_helper.rb,
app/helpers/alchemy/admin/tags_helper.rb,
app/models/alchemy/element/presenters.rb,
app/models/alchemy/ingredients/select.rb,
app/models/alchemy/page/page_elements.rb,
app/helpers/alchemy/admin/pages_helper.rb,
app/models/alchemy/element/definitions.rb,
app/models/alchemy/elements_repository.rb,
app/models/alchemy/ingredients/boolean.rb,
app/models/alchemy/ingredients/picture.rb,
app/models/alchemy/picture_description.rb,
app/services/alchemy/duplicate_element.rb,
app/controllers/alchemy/base_controller.rb,
app/models/alchemy/ingredient_validator.rb,
app/models/alchemy/ingredients/datetime.rb,
app/models/alchemy/ingredients/headline.rb,
app/models/alchemy/ingredients/richtext.rb,
app/models/alchemy/picture/calculations.rb,
app/models/alchemy/picture/preprocessor.rb,
app/models/alchemy/picture_thumb/create.rb,
app/serializers/alchemy/base_serializer.rb,
app/serializers/alchemy/node_serializer.rb,
app/serializers/alchemy/page_serializer.rb,
app/serializers/alchemy/site_serializer.rb,
app/components/alchemy/admin/list_filter.rb,
app/components/alchemy/admin/node_select.rb,
app/components/alchemy/admin/page_select.rb,
app/controllers/alchemy/pages_controller.rb,
app/decorators/alchemy/ingredient_editor.rb,
app/models/alchemy/page/fixed_attributes.rb,
app/helpers/alchemy/admin/elements_helper.rb,
app/helpers/alchemy/admin/pictures_helper.rb,
app/helpers/alchemy/elements_block_helper.rb,
app/models/alchemy/image_cropper_settings.rb,
app/components/alchemy/admin/resource/cell.rb,
app/models/alchemy/picture/transformations.rb,
app/models/alchemy/picture_thumb/signature.rb,
app/models/concerns/alchemy/touch_elements.rb,
app/serializers/alchemy/element_serializer.rb,
app/serializers/alchemy/picture_serializer.rb,
app/components/alchemy/admin/resource/table.rb,
app/components/alchemy/admin/toolbar_button.rb,
app/controllers/alchemy/api/base_controller.rb,
app/controllers/alchemy/messages_controller.rb,
app/helpers/alchemy/admin/navigation_helper.rb,
app/models/alchemy/picture_thumb/file_store.rb,
app/serializers/alchemy/language_serializer.rb,
app/components/alchemy/admin/resource/action.rb,
app/components/alchemy/admin/resource/header.rb,
app/components/alchemy/ingredients/base_view.rb,
app/components/alchemy/ingredients/file_view.rb,
app/components/alchemy/ingredients/html_view.rb,
app/components/alchemy/ingredients/link_view.rb,
app/components/alchemy/ingredients/node_view.rb,
app/components/alchemy/ingredients/page_view.rb,
app/components/alchemy/ingredients/text_view.rb,
app/controllers/alchemy/api/nodes_controller.rb,
app/controllers/alchemy/api/pages_controller.rb,
app/helpers/alchemy/admin/attachments_helper.rb,
app/helpers/alchemy/admin/ingredients_helper.rb,
app/serializers/alchemy/page_node_serializer.rb,
app/serializers/alchemy/page_tree_serializer.rb,
app/components/alchemy/admin/link_dialog/tabs.rb,
app/components/alchemy/ingredients/audio_view.rb,
app/components/alchemy/ingredients/video_view.rb,
app/controllers/alchemy/admin/base_controller.rb,
app/controllers/alchemy/admin/tags_controller.rb,
app/serializers/alchemy/attachment_serializer.rb,
app/serializers/alchemy/ingredient_serializer.rb,
app/components/alchemy/admin/attachment_select.rb,
app/components/alchemy/admin/tags_autocomplete.rb,
app/components/alchemy/ingredients/select_view.rb,
app/controllers/alchemy/admin/nodes_controller.rb,
app/controllers/alchemy/admin/pages_controller.rb,
app/controllers/alchemy/admin/sites_controller.rb,
app/controllers/alchemy/attachments_controller.rb,
app/models/alchemy/element/element_ingredients.rb,
app/models/concerns/alchemy/picture_thumbnails.rb,
app/components/alchemy/ingredients/boolean_view.rb,
app/components/alchemy/ingredients/picture_view.rb,
app/controllers/alchemy/api/elements_controller.rb,
app/controllers/concerns/alchemy/site_redirects.rb,
app/components/alchemy/ingredients/datetime_view.rb,
app/components/alchemy/ingredients/headline_view.rb,
app/components/alchemy/ingredients/richtext_view.rb,
app/components/alchemy/admin/link_dialog/base_tab.rb,
app/components/alchemy/admin/link_dialog/file_tab.rb,
app/controllers/alchemy/admin/elements_controller.rb,
app/controllers/alchemy/admin/pictures_controller.rb,
app/controllers/alchemy/admin/clipboard_controller.rb,
app/controllers/alchemy/admin/dashboard_controller.rb,
app/controllers/alchemy/admin/languages_controller.rb,
app/controllers/alchemy/admin/resources_controller.rb,
app/controllers/alchemy/api/attachments_controller.rb,
app/controllers/alchemy/api/ingredients_controller.rb,
app/controllers/concerns/alchemy/admin/crop_action.rb,
app/components/alchemy/admin/link_dialog/anchor_tab.rb,
app/controllers/alchemy/admin/styleguide_controller.rb,
app/controllers/alchemy/admin/attachments_controller.rb,
app/controllers/alchemy/admin/ingredients_controller.rb,
app/controllers/alchemy/admin/layoutpages_controller.rb,
app/components/alchemy/admin/link_dialog/external_tab.rb,
app/components/alchemy/admin/link_dialog/internal_tab.rb,
app/controllers/concerns/alchemy/admin/archive_overlay.rb,
app/controllers/concerns/alchemy/legacy_page_redirects.rb,
app/controllers/concerns/alchemy/admin/current_language.rb,
app/controllers/alchemy/admin/legacy_page_urls_controller.rb,
app/controllers/concerns/alchemy/admin/uploader_responses.rb,
app/controllers/alchemy/admin/picture_descriptions_controller.rb,
lib/alchemy.rb,
lib/alchemy/i18n.rb,
lib/alchemy/hints.rb,
lib/alchemy/paths.rb,
lib/alchemy/shell.rb,
lib/alchemy/config.rb,
lib/alchemy/engine.rb,
lib/alchemy/errors.rb,
lib/alchemy/logger.rb,
lib/alchemy/seeder.rb,
lib/alchemy/modules.rb,
lib/alchemy/tinymce.rb,
lib/alchemy/version.rb,
lib/alchemy/resource.rb,
lib/alchemy/taggable.rb,
lib/alchemy/upgrader.rb,
lib/alchemy/filetypes.rb,
lib/alchemy/tasks/tidy.rb,
lib/alchemy/deprecation.rb,
lib/alchemy/page_layout.rb,
lib/alchemy/permissions.rb,
lib/alchemy/tasks/usage.rb,
lib/alchemy/admin/locale.rb,
lib/alchemy/test_support.rb,
lib/alchemy/forms/builder.rb,
lib/alchemy/install/tasks.rb,
lib/alchemy/auth_accessors.rb,
lib/alchemy/error_tracking.rb,
lib/alchemy/on_page_layout.rb,
lib/alchemy/elements_finder.rb,
lib/alchemy/resource_filter.rb,
lib/generators/alchemy/base.rb,
lib/alchemy/name_conversions.rb,
lib/alchemy/resources_helper.rb,
lib/alchemy/admin/preview_url.rb,
lib/alchemy/controller_actions.rb,
lib/alchemy/element_definition.rb,
lib/alchemy/routing_constraints.rb,
lib/alchemy/searchable_resource.rb,
lib/alchemy/configuration_methods.rb,
lib/alchemy/propshaft/tinymce_asset.rb,
lib/alchemy/error_tracking/error_logger.rb,
lib/alchemy/test_support/config_stubbing.rb,
lib/alchemy/test_support/capybara_helpers.rb,
lib/alchemy/cache_digests/template_tracker.rb,
lib/alchemy/dragonfly/processors/thumbnail.rb,
lib/alchemy/dev_support/live_reload_watcher.rb,
lib/alchemy/on_page_layout/callbacks_runner.rb,
lib/alchemy/dragonfly/processors/auto_orient.rb,
lib/alchemy/dragonfly/processors/crop_resize.rb,
lib/alchemy/test_support/integration_helpers.rb,
lib/generators/alchemy/menus/menus_generator.rb,
lib/generators/alchemy/views/views_generator.rb,
lib/generators/alchemy/module/module_generator.rb,
lib/generators/alchemy/install/install_generator.rb,
lib/generators/alchemy/elements/elements_generator.rb,
lib/generators/alchemy/ingredient/ingredient_generator.rb,
lib/generators/alchemy/page_layouts/page_layouts_generator.rb,
lib/generators/alchemy/site_layouts/site_layouts_generator.rb

Overview

Provides authentication accessors.

Alchemy has some defaults for user model name and login logout path names:

Alchemy.user_class_name defaults to ‘User’ Alchemy.user_class_primary_key defaults to :id Alchemy.current_user_method defaults to ‘current_user’+ Alchemy.signup_path defaults to ‘/signup’+ Alchemy.login_path defaults to ‘/login’+ Alchemy.logout_path defaults to ‘/logout’+ Alchemy.logout_method defaults to ‘delete’+ Alchemy.unauthorized_path defaults to ‘/’+

Anyway, you can tell Alchemy about your authentication model configuration:

1. Your user class name - @see: Alchemy.user_class
2. Your users table primary key - @see: Alchemy.user_class_primary_key
3. A method on your ApplicationController to get current user -
   @see: Alchemy.current_user_method
4. The path to the signup form - @see: Alchemy.signup_path
5. The path to the login form - @see: Alchemy.login_path
6. The path to the logout method - @see: Alchemy.logout_path
7. The http verb for the logout method - @see: Alchemy.logout_method
8. The path to the page showing the user she's unauthorized - @see: Alchemy.unauthorized_path

Example

# config/initializers/alchemy.rb
Alchemy.user_class_name = 'Admin'
Alchemy.user_class_primary_key = :user_id
Alchemy.current_user_method = 'current_admin'
Alchemy. = '/auth/signup'
Alchemy. = '/auth/login'
Alchemy.logout_path = '/auth/logout'
Alchemy.logout_method = 'get'
Alchemy.unauthorized_path = '/home'

If you don’t have your own user model or don’t want to provide one, add the ‘alchemy-devise` gem into your App’s Gemfile.

Adding your own CanCan abilities

If your app or your engine has own CanCan abilities you must register them:

Alchemy.register_ability MyCustom::Ability

Defined Under Namespace

Modules: AbilityHelper, Admin, BaseHelper, CacheDigests, ConfigurationMethods, ControllerActions, DomIds, Dragonfly, ElementsBlockHelper, ElementsHelper, ErrorTracking, Filetypes, Forms, Generators, Hints, I18n, Ingredients, Install, LegacyPageRedirects, Logger, Modules, NameConversions, OnPageLayout, PagesHelper, PictureThumbnails, Propshaft, ResourcesHelper, SearchableResource, Shell, SiteRedirects, Taggable, Tasks, TestSupport, Tinymce, TouchElements, UrlHelper Classes: Attachment, AttachmentSerializer, AttachmentsController, BaseController, BaseJob, BaseMailer, BaseRecord, BaseSerializer, Config, CopyPage, Current, DefaultLanguageNotFoundError, DefaultSiteNotFoundError, DeleteElements, DuplicateElement, EagerLoading, Element, ElementDefinition, ElementDefinitionError, ElementEditor, ElementSerializer, ElementToPage, ElementsFinder, ElementsRepository, Engine, FoldedPage, ImageCropperSettings, Ingredient, IngredientEditor, IngredientSerializer, IngredientValidator, Language, LanguageSerializer, LegacyPageUrl, LiveReloadWatcher, Message, MessagesController, MessagesMailer, MissingActiveRecordAssociation, MissingImageFileError, NoCurrentUserFoundError, Node, NodeSerializer, NotMountedError, Page, PageLayout, PageMutex, PageNodeSerializer, PageSerializer, PageTreeSerializer, PageVersion, PagesController, Permissions, Picture, PictureDescription, PictureInUseError, PictureSerializer, PictureThumb, PictureVariant, PublishPageJob, Resource, ResourceFilter, RoutingConstraints, Seeder, Site, SiteSerializer, Tag, TagValidations, Tidy, TinymceError, UnsupportedPageVersion, UpdateServiceUnavailable, Upgrader, WrongImageFormatError

Constant Summary collapse

YAML_PERMITTED_CLASSES =
%w[Symbol Date Regexp]
VERSION =
"8.0.0.a"
Deprecation =
ActiveSupport::Deprecation.new("8.0", "Alchemy")
@@admin_path =

Defaults

"admin"
@@admin_constraints =
{}
@@user_class_name =

Defaults

"User"
@@user_class_primary_key =
:id
@@current_user_method =
"current_user"
@@signup_path =
"/signup"
@@login_path =
"/login"
@@logout_path =
"/logout"
@@logout_method =
"delete"
@@unauthorized_path =
"/"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Attribute Details

#importmapObject

JS Importmap instance



139
140
141
# File 'lib/alchemy.rb', line 139

def importmap
  @importmap
end

Class Method Details

.admin_importmapsSet<Hash>

Additional importmaps to be included in the Alchemy admin UI

Be sure to also pin modules with Alchemy.importmap.

Example

# config/alchemy/importmap.rb
Alchemy.importmap.pin "alchemy_solidus", to: "alchemy_solidus.js", preload: true
Alchemy.importmap.pin_all_from Alchemy::Solidus::Engine.root.join("app/javascript/alchemy_solidus"),
  under: "alchemy_solidus",
  preload: true

# lib/alchemy/solidus/engine.rb
initializer "alchemy_solidus.assets", before: "alchemy.importmap" do |app|
  Alchemy.admin_importmaps.add({
    importmap_path: root.join("config/importmap.rb"),
    source_paths: [
      root.join("app/javascript")
    ],
    name: "alchemy_solidus"
  })
  app.config.assets.precompile << "alchemy_solidus/manifest.js"
end

Returns:

  • (Set<Hash>)


90
91
92
93
94
95
96
97
98
99
# File 'lib/alchemy.rb', line 90

def self.admin_importmaps
  @_admin_importmaps ||= Set.new([{
    importmap_path: Engine.root.join("config/importmap.rb"),
    source_paths: [
      Engine.root.join("app/javascript"),
      Engine.root.join("vendor/javascript")
    ],
    name: "alchemy_admin"
  }])
end

.admin_js_importsObject

Additional JS modules to be imported in the Alchemy admin UI

Be sure to also pin the modules with Alchemy.importmap.

Example

Alchemy.importmap.pin "flatpickr/de",
  to: "https://ga.jspm.io/npm:[email protected]/dist/l10n/de.js"

Alchemy.admin_js_imports << "flatpickr/de"


57
58
59
# File 'lib/alchemy.rb', line 57

def self.admin_js_imports
  @_admin_js_imports ||= Set.new
end

.admin_js_imports=(sources) ⇒ Object



61
62
63
# File 'lib/alchemy.rb', line 61

def self.admin_js_imports=(sources)
  @_admin_js_imports = Set[sources]
end

.gem_versionObject



10
11
12
# File 'lib/alchemy/version.rb', line 10

def self.gem_version
  Gem::Version.new(VERSION)
end

Configure tabs in the link dialog

With this configuration that tabs in the link dialog can be extended without overwriting or defacing the Admin Interface.

Example

# components/acme/link_tab.rb
module Acme
  class LinkTab < ::Alchemy::Admin::LinkDialog::BaseTab
    def title
      "Awesome Tab Title"
    end

    def name
      :unique_name
    end

    def fields
       [ title_input, target_select ]
    end
  end
end

# config/initializers/alchemy.rb
Alchemy.link_dialog_tabs << Acme::LinkTab


169
170
171
172
173
174
175
176
# File 'lib/alchemy.rb', line 169

def self.link_dialog_tabs
  @_link_dialog_tabs ||= Set.new([
    Alchemy::Admin::LinkDialog::InternalTab,
    Alchemy::Admin::LinkDialog::AnchorTab,
    Alchemy::Admin::LinkDialog::ExternalTab,
    Alchemy::Admin::LinkDialog::FileTab
  ])
end

.preview_sourcesObject

Define page preview sources

A preview source is a Ruby class returning an URL that is used as source for the preview frame in the admin UI.

Example

# lib/acme/preview_source.rb
class Acme::PreviewSource < Alchemy::Admin::PreviewUrl
  def url_for(page)
    if page.site.name == "Next"
      "https://user:#{ENV['PREVIEW_HTTP_PASS']}@next.acme.com"
    else
      "https://www.acme.com"
    end
  end
end

# config/initializers/alchemy.rb
require "acme/preview_source"
Alchemy.preview_sources << Acme::PreviewSource

# config/locales/de.yml
de:
  activemodel:
    models:
      acme/preview_source: Acme Vorschau


38
39
40
# File 'lib/alchemy.rb', line 38

def self.preview_sources
  @_preview_sources ||= Set.new << Alchemy::Admin::PreviewUrl
end

.preview_sources=(sources) ⇒ Object



42
43
44
# File 'lib/alchemy.rb', line 42

def self.preview_sources=(sources)
  @_preview_sources = Array(sources)
end

.publish_targetsObject

Define page publish targets

A publish target is a ActiveJob that gets performed whenever a user clicks the publish page button.

Use this to trigger deployment hooks of external services in an asychronous way.

Example

# app/jobs/publish_job.rb
class PublishJob < ApplicationJob
  def perform(page)
    RestClient.post(ENV['BUILD_HOOK_URL'])
  end
end

# config/initializers/alchemy.rb
Alchemy.publish_targets << PublishJob


121
122
123
# File 'lib/alchemy.rb', line 121

def self.publish_targets
  @_publish_targets ||= Set.new
end

.register_ability(klass) ⇒ Object

Register a CanCan Ability class



124
125
126
127
# File 'lib/alchemy/auth_accessors.rb', line 124

def self.register_ability(klass)
  @abilities ||= []
  @abilities << klass
end

.registered_abilitiesObject

All CanCan Ability classes registered to Alchemy



131
132
133
# File 'lib/alchemy/auth_accessors.rb', line 131

def self.registered_abilities
  @abilities ||= []
end

.t(msg, **kwargs) ⇒ Object

Alchemy shortcut translation method

Instead of having to call:

Alchemy::I18n.translate(:hello)

You can use this shortcut method:

Alchemy.t(:hello)


15
16
17
# File 'lib/alchemy/i18n.rb', line 15

def t(msg, **kwargs)
  Alchemy::I18n.translate(msg, **kwargs)
end

.table_name_prefixObject



4
5
6
# File 'app/models/alchemy/base_record.rb', line 4

def self.table_name_prefix
  "alchemy_"
end

.user_classObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/alchemy/auth_accessors.rb', line 94

def self.user_class
  @@user_class ||= begin
    @@user_class_name.constantize
  rescue NameError => e
    if /#{Regexp.escape(@@user_class_name)}/.match?(e.message)
      Rails.logger.warn <<~MSG
        #{e.message}
        #{e.backtrace.join("\n")}

        AlchemyCMS cannot find any user class!

        Please add a user class and tell Alchemy about it:

            # config/initializers/alchemy.rb
            Alchemy.user_class_name = 'MyUser'

        Or add the `alchemy-devise` gem to your Gemfile:

            bundle add alchemy-devise

      MSG
      nil
    else
      raise e
    end
  end
end

.user_class_nameObject

Prefix with

when getting to avoid constant name conflicts



82
83
84
85
86
87
88
# File 'lib/alchemy/auth_accessors.rb', line 82

def self.user_class_name
  if !@@user_class_name.is_a?(String)
    raise TypeError, "Alchemy.user_class_name must be a String, not a Class."
  end

  "::#{@@user_class_name}"
end

.user_class_name=(user_class_name) ⇒ Object



90
91
92
# File 'lib/alchemy/auth_accessors.rb', line 90

def self.user_class_name=(user_class_name)
  @@user_class_name = user_class_name
end

.versionObject



6
7
8
# File 'lib/alchemy/version.rb', line 6

def self.version
  VERSION
end