Site Plugins

What are site plugins?

A site plugin can contain one or more extensions for Crafter CMS in a single package. These extensions can:

  • Extend Crafter Studio (authoring)

    • Add Studio authoring widgets that drive the Sidebar and other UI elements

    • Add new Form Engine extensions including data sources and components

    • Add server-side code and services that drive the Studio UI extensions

  • Extend Crafter Engine and the site/web application (delivery)

    • Add new content types along with their Groovy controllers and FreeMarker templates

    • Add REST APIs and/or server-side code

    • Add 3rd party integrations to your web app

How Do I Make My Own Site Plugin?

Requirements

You’ll need the following for creating your plugin:

  • A plugin descriptor file, craftercms-plugin.yaml

  • Your plugin files

The craftercms-plugin.yaml file contains information about your plugin, such as the license, the versions of Crafter CMS supported, and other configurations and metadata.

See Crafter CMS Plugin Descriptor for more information on what’s inside the plugin descriptor.

Your plugin files/folders could be JavaScript files, XML files, Groovy scripts, images, CSS files, and more depending on the plugin type you’re creating.

Directory Structure

A site plugin consist of a group of files that are copied to the site repository when installed. To create your own site plugin, your files/folders needs to go in the corresponding type of plugin folder, following the structure below:

  • craftercms-plugin.yaml: the plugin descriptor, see Crafter CMS Plugin Descriptor for details

  • .crafter

    • screenshots

      • default.png : the default representative image of the plugin placed under the default path .crafter/screenshots/

  • authoring: contains all files related to Crafter Studio extensions

    • content-types

      • component: contains configuration files for components, see below for an example

      • page: contains configuration files for pages

    • js: contains files for Studio UI plugins, see Crafter Studio Site Plugins for details

    • scripts

      • classes: contains Groovy classes

      • rest: contains REST Groovy scripts

  • delivery: contains all files related to Crafter Engine extensions

    • templates: contains Freemarker templates

    • static-assets: contains binary files

    • scripts

      • classes: contains Groovy classes

      • components: contains Groovy scripts for components

      • controllers: contains Groovy controllers

      • filters: contains Groovy filters

      • pages: contains Groovy scripts for pages

      • rest: contains Groovy REST scripts

An easy way to develop new plugins is to start with an empty site and when all the files are ready copy them to a new repository following the given structure. However all references should be updated to match the final destination of the file:

Location in the plugin repository

Location in the site repository

authoring/content-types/component/*

/config/studio/content-types/component/<plugin id path>/*

authoring/content-types/page/*

/config/studio/content-types/page/<plugin id path>/*

authoring/js/*

/config/studio/plugins/js/<plugin id path>/*

authoring/scripts/classes/*

/config/studio/plugins/scripts/classes/<plugin id path>/*

authoring/scripts/rest/*

/config/studio/plugins/scripts/rest/<plugin id path>/*

delivery/templates/*

/templates/<plugin id path>/*

delivery/static-assets/*

/static-assets/<plugin id path>/*

delivery/scripts/classes/*

/scripts/classes/<plugin id path>/*

delivery/scripts/components/*

/scripts/components/<plugin id path>/*

delivery/scripts/controllers/*

/scripts/controllers/<plugin id path>/*

delivery/scripts/filters/*

/scripts/filters/<plugin id path>/*

delivery/scripts/pages/*

/scripts/pages/<plugin id path>/*

delivery/scripts/rest/*

/scripts/rest/<plugin id path>/*

Create your plugin

To create a plugin, a descriptor file craftercms-plugin.yaml is required. Below is an example site plugin descriptor file.

Example craftercms-plugin.yaml file for a site plugin
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# This file describes a plugin for use in Crafter CMS

# The version of the format for this file
descriptorVersion: 2

# Describe the plugin
plugin:
  type: site
  id: org.craftercms.plugin.test
  name: Site Plugin Example
  tags:
    - test
  version:
    major: 3
    minor: 0
    patch: 1
  description: A simple example for site plugins
  website:
    name: Site Plugin Example
    url: https://github.com/craftercms/site-plugins-example
  media:
    screenshots:
      - title: Crafter CMS
        description: Crafter CMS Example Plugin
        url: "https://raw.githubusercontent.com/craftercms/site-plugin-example/master/.crafter/screenshots/default.png"
  developer:
    company:
      name: Crafter Software
      email: info@craftersoftware.com
      url: https://craftersoftware.com
  build:
    id: 0220b902bff5cd22749e8ac46ec80eed314c3d67
    date: 2021-03-18T00:00:00Z
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
  crafterCmsVersions:
    - major: 4
      minor: 0
      patch: 0
  crafterCmsEditions:
    - community
    - enterprise

Here are some things to note in the descriptor file:

  • plugin.type should be set to site for site plugins

  • plugin.id is a unique Id that is meaningful/recognizable to people who will be using the site plugin

  • plugin.name is the name displayed in the Crafter CMS Marketplace. Pick a unique name for your plugin. You can check in the Crafter CMS Marketplace if the name you picked does not exist yet. It’s also a best practice to provide a name for your plugin that is meaningful or recognizable to users. The name can be multiple words such as Site Plugin Example

  • plugin.version is a version number for the site plugin

  • plugin.description should contain a short description of the plugin and is displayed underneath the plugin name in the Crafter CMS Marketplace

  • plugin.website.url can be a page for more information on your site plugin or for announcing updates, reporting bugs, etc. from your user community.

  • plugin.media.url is the path to look for a representative image of the site plugin.

  • plugin.license is the license supported by the plugin

  • plugin.crafterCmsVersions contains the Crafter CMS version/s that the plugin is compatible with (look in the Release Notes section for the versions available), and you’ll need to keep this up to date



The next requirement for creating your site plugin are the plugin files. Depending on the plugin type you are creating, this could be a JavaScript file, Freemarker template files, Groovy file, XML file, etc. The plugin file/s should then be placed in a directory structure as described above depending on the site plugin created. For example, say your plugin is a component content type, your plugin files should be placed under the directory authoring/content-types/component

Example directory structure for a component content type site plugin
authoring/
  content-types/
    component/
      <your_component_name>/
        config.xml
        controller.groovy
        form-definition.xml

Crafter CMS uses a default path for Crafter CMS to look for a default representative image of a plugin, the url ../.crafter/screenshots/. Here’s a sample plugin files/directory with a default image to represent the plugin:

Example directory structure for a component content type site plugin with a default representative image
.crafter/
  screenshots/
    default.png
authoring/
  content-types/
    component/
      <your_component_name>/
        config.xml
        controller.groovy
        form-definition.xml


Publishing Your Site Plugin

To publish a plugin in the Crafter CMS Marketplace you can follow the instructions in Create Plugins for the Crafter Marketplace



Installing a Site Plugin

Plugins may be installed a couple of ways depending on where the plugins are located:



Install a plugin from the Crafter CMS Marketplace

Once a site plugin is published to the Crafter CMS Marketplace it can be installed using the Crafter Studio user interface or the REST API:

Note

To access the Plugin Management tool or use the install plugin REST API your user needs to have the following permissions:

  • list_plugins

  • install_plugins

For more information on installing plugins from the Crafter CMS Marketplace using Crafter Studio, see Plugin Management



Install a plugin in development from a Studio local folder

For developers who want to test out their plugins before submitting to the Crafter CMS Marketplace, Crafter CMS provides a CLI command copy-plugin for installing a plugin from a Studio local folder into a site using the crafter-cli.

Let’s take a look at an example to show how to install a plugin using the Crafter CMS cli copy-plugin command. We’ll use a site named mysite where we will be installing the plugin, and the plugin we want to install located in /Users/myuser/plugins/sidebar-plugin

To install the plugin sidebar-plugin to our site mysite, we’ll run the copy-plugin command like below:

➜  ./crafter-cli copy-plugin -e local -s editorial --path /users/myuser/plugins/sidebar-plugin
OK

Remember that the connection to Crafter CMS needs to be setup via the add-environment command before using any of the crafter-cli commands.

See copy-plugin for more information on the copy-plugin command.

Example Creating a Site Plugin

Let’s take a look at an example of creating a component content type plugin named My Component

First, we’ll configure the descriptor file craftercms-plugin.yaml file for our plugin

Descriptor file for the example component content type plugin
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# This file describes a plugin for use in Crafter CMS

# The version of the format for this file
descriptorVersion: 2

# Describe the plugin
plugin:
  type: site
  id: org.craftercms.plugin.mycomponent
  name: My Component Site Plugin Example
  tags:
    - test
  version:
    major: 4
    minor: 0
    patch: 0
  description: My simple component content type site plugin
  website:
    name: Component Content Type Site Plugin Example
    url: https://craftersoftware.com
  media:
    screenshots:
      - title: Crafter CMS
        description: Crafter CMS Example Component Plugin
        url: "https://raw.githubusercontent.com/craftercms/site-plugin-example/master/.crafter/screenshots/default.png"
  developer:
    company:
      name: Crafter Software
      email: info@craftersoftware.com
      url: https://craftersoftware.com
  build:
    id: 0220b902bff5cd22749e8ac46ec80eed314c3d67
    date: 2021-03-18T00:00:00Z
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
  crafterCmsVersions:
    - major: 4
      minor: 0
      patch: 0
  crafterCmsEditions:
    - community
    - enterprise

We’ll then create the directory structure for a component content type plugin authoring/content-types/component/*, to place our plugin files in,

Directory structure for component content type site plugin My Component
authoring/
  content-types/
    component/
      mycomponent/
        config.xml
        controller.groovy
        form-definition.xml

Here are the plugin files:

authoring/content-types/component/mycomponent/config.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<content-type name="/component/mycomponent" is-wcm-type="true">
<label>My Component</label>
<form>/component/plugins/org/craftercms/mycomponent/mycomponent</form>
<form-path>simple</form-path>
<model-instance-path>NOT-USED-BY-SIMPLE-FORM-ENGINE</model-instance-path>
<file-extension>xml</file-extension>
<content-as-folder>false</content-as-folder>
<previewable>false</previewable>
<quickCreate>false</quickCreate>
<quickCreatePath></quickCreatePath>
<noThumbnail>true</noThumbnail>
<image-thumbnail></image-thumbnail>
</content-type>

authoring/content-types/component/mycomponent/form-definition.xml
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<form>
    <title>Test</title>
    <description></description>
    <objectType>component</objectType>
    <content-type>/component/plugins/org/craftercms/plugin/mycomponent/mycomponent</content-type>
    <imageThumbnail>undefined</imageThumbnail>
    <quickCreate>false</quickCreate>
    <quickCreatePath></quickCreatePath>
    <properties>        <property>
            <name>display-template</name>
            <label>Display Template</label>
            <value>/templates/plugins/org/craftercms/plugin/mycomponent/mycomponent.ftl</value>
            <type>template</type>
        </property>
        <property>
            <name>no-template-required</name>
            <label>No Template Required</label>
            <value></value>
            <type>boolean</type>
        </property>
        <property>
            <name>merge-strategy</name>
            <label>Merge Strategy</label>
            <value>inherit-levels</value>
            <type>string</type>
        </property>
    </properties>
    <sections>        <section>
            <title>Test Properties</title>
            <description></description>
            <defaultOpen>true</defaultOpen>
            <fields>
                <field>
                    <type>auto-filename</type>
                    <id>file-name</id>
                    <iceId></iceId>
                    <title>Component ID</title>
                    <description></description>
                    <defaultValue></defaultValue>
                    <help></help>
                    <properties>
                        <property>
                            <name>size</name>
                            <value>50</value>
                            <type>int</type>
                        </property>
                        <property>
                            <name>maxlength</name>
                            <value>50</value>
                            <type>int</type>
                        </property>
                        <property>
                            <name>readonly</name>
                            <value></value>
                            <type>boolean</type>
                        </property>
                    </properties>
                    <constraints>
                    </constraints>
                </field>
                <field>
                    <type>input</type>
                    <id>internal-name</id>
                    <iceId></iceId>
                    <title>Internal Name</title>
                    <description></description>
                    <defaultValue></defaultValue>
                    <help></help>
                    <properties>
                        <property>
                            <name>size</name>
                            <value>50</value>
                            <type>int</type>
                        </property>
                        <property>
                            <name>maxlength</name>
                            <value>50</value>
                            <type>int</type>
                        </property>
                    </properties>
                    <constraints>
                        <constraint>
                            <name>required</name>
                            <value><![CDATA[true]]></value>
                            <type>boolean</type>
                        </constraint>
                    </constraints>
                </field>
                <field>
                    <type>input</type>
                    <id>someValue_s</id>
                    <iceId></iceId>
                    <title>Some Value</title>
                    <description></description>
                    <defaultValue></defaultValue>
                    <help></help>
                    <properties>
                        <property>
                            <name>size</name>
                            <value>50</value>
                            <type>int</type>
                        </property>
                        <property>
                            <name>maxlength</name>
                            <value>50</value>
                            <type>int</type>
                        </property>
                        <property>
                            <name>readonly</name>
                            <value></value>
                            <type>boolean</type>
                        </property>
                        <property>
                            <name>tokenize</name>
                            <value>false</value>
                            <type>boolean</type>
                        </property>
                    </properties>
                    <constraints>
                        <constraint>
                            <name>required</name>
                            <value><![CDATA[]]></value>
                            <type>boolean</type>
                        </constraint>
                        <constraint>
                            <name>pattern</name>
                            <value><![CDATA[]]></value>
                            <type>string</type>
                        </constraint>
                    </constraints>
                </field>
            </fields>
        </section>
    </sections>
    <datasources>    </datasources>
</form>
authoring/content-types/component/mycomponent/controller.groovy
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import scripts.libs.CommonLifecycleApi;

def contentLifecycleParams =[:];
contentLifecycleParams.site = site;
contentLifecycleParams.path = path;
contentLifecycleParams.user = user;
contentLifecycleParams.contentType = contentType;
contentLifecycleParams.contentLifecycleOperation = contentLifecycleOperation;
contentLifecycleParams.contentLoader = contentLoader;
contentLifecycleParams.applicationContext = applicationContext;

def controller = new CommonLifecycleApi(contentLifecycleParams);
controller.execute();

The plugin is now ready to be tested. We’ll install our plugin located under /users/myuser/component-plugin using the crafter-cli command copy-plugin to test it out to a site named editorial

➜ ./crafter-cli copy-plugin -e local -s editorial --path /users/myuser/component-plugin
OK

After installing our plugin, we can now verify that our component plugin is available in siteConfig Content Types

Example component content type plugin now available in site editorial

Site Plugin Example

Crafter CMS provides a site plugin example available here: https://github.com/craftercms/site-plugin-example

This plugin demonstrates a Site plugin with authoring and delivery code. It extends Crafter Studio with an API, a UI for content authors, and extends the delivery Site (in Crafter Engine) with an MVC (Model View Controller) with a content type (model), a FreeMarker template (view)

It is published in the Crafter CMS Marketplace and can be installed to your site using Plugin Management in siteConfig through the Studio UI. See Plugin Management for more information on installing site plugins from the Crafter CMS Marketplace.

Site Plugin Example in Plugin Management

Some More Examples

Here are some more site plugin examples to help you create your own plugins:

Authoring Examples

For more authoring examples of site plugins, see Crafter Studio Site Plugins