In this tutorial you create your first content app. A content app is a special app type that is well suited for managing custom content items such as products. The items in this example are antique cars but you can use any items you wish. 

 

Create a module

Your content app needs to be deployed as a Magnolia module. Choose from these options depending on your skill level. 

Option 1: Clone the project in Git

Choose this option if you know how to work with a Magnolia project and Git. You get a project that you can edit it in your IDE and customize to your needs.

  1. Clone the app-tutorial repository.
git clone https://git.magnolia-cms.com/scm/documentation/app-tutorial.git
  1. fake
  2. Import the project into your IDE. Here's what the project looks like in IntelliJ IDEA:
  3. Build the project into a JAR and deploy it to Magnolia instance. Or run the project in your IDE.

Option 2: Download the module JAR

Choose this option if you are new to Magnolia. Download the module JAR and follow the standard module installation instructions. You get the complete content app and can learn how it works.

  1. Download magnolia-app-tutorial.jar from Nexus.
  2. Copy the JAR into <CATALINA_HOME>/webapps/<contextPath>/WEB-INF/lib folder. Typically this is <CATALINA_HOME>/webapps/magnoliaAuthor/WEB-INF/lib.
  3. Restart your Magnolia instance and run the Web update. This will install the App Tutorial module.

Option 3: Run a Groovy Script

If you already have a module, and want to quickly create a new App, you can use the Groovy App:

  1. Open the Groovy app.
  2. Edit the script /createAppScript.
  3. Update the parameters you can see between line 9 and 14:
    1. app_display_name, here you can specify the name of your app

    2. parent_module, this is where the app will be created (enter your module name)

    3. app_group, the group on the app launcher in which your app should display.

    4. app_icon, the icon of your app

    5. app_repository, here you can keep the default repository

    6. app_folder_support, if you want to be able to create folders within your app

When the app is created, do not forget to export the configuration. 

Module contents

The module contains the following source files:

  • Module descriptor: magnolia-module-app-tutorial.xml
  • App configuration products.yaml
  • App Launcher layout bootstrap: config.modules.ui-admincentral.config.appLauncherLayout.groups.edit.apps.products.xml
  • Sample products (cars): products.cars.xml
  • Images: dam.cars.xml
  • Custom node type app-tutorial-nodetypes.xml
  • User interface text in English: app-products-messages_en.properties
  • User interface text in Spanish: app-products-messages_es.properties

App configuration

The app is configured in YAML in products.yaml. Equivalent JCR configuration examples are provided below.

App descriptor

App descriptor configuration describes an app. The descriptor assigns the app a name, icon and implementation class. The name of the app content node must be unique as it is used to refer to the app across the system. This means you cannot name your own app pages since a Pages app already exists.

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
# subapps definition
Node nameValue

 modules

 

 my-module

 

 apps

 

 products

 

 appClass

info.magnolia.ui.contentapp.ContentApp

 class

info.magnolia.ui.contentapp.ContentAppDescriptor

App launcher layout

(warning) The app launcher can only be configured in the JCR.

In the app launcher layout we add the app to the Edit group.

Node name

 modules

 ui-admincentral

 config

 appLauncherLayout

 groups

 edit

 apps

  pages

  assets

  contacts

 products

Go to the app launcher and verify that you can see the Products app icon.

If you move the app to another group, log out and back in to see the change.

Browser subapp

The browser subapp allows you to view and organize items. It is part of every content app.

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
  browser:
    subAppClass: info.magnolia.ui.contentapp.browser.BrowserSubApp
    class: info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor
Node nameValue

 modules

 

 app-tutorial

 

 apps

 

 products

 

 subApps

 

 browser

 

 class

info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor

 subAppClass

info.magnolia.ui.contentapp.browser.BrowserSubApp

Content connector

A content connector tells the app where the content resides. In this case we store products in the repository so we use a JCR content connector. The configuration identifies the workspace and a path. It also configures the node type the app operates on. 

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
  browser:
    subAppClass: info.magnolia.ui.contentapp.browser.BrowserSubApp
    class: info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor
    contentConnector:
      includeProperties: false
      workspace: products
      rootPath: /
      defaultOrder: jcrName
      nodeTypes:
        - name: mgnl:product
          icon: icon-items
        - name: mgnl:folder
          icon: icon-folder
Node nameValue
 subApps
 

 browser

 

 contentConnector

 

 nodeTypes

 

 product

 

 icon

icon-items

 name

mgnl:product

 folders

 

 icon

icon-folder

 name

mgnl:folder

 defaultOrder

jcrName

 includeProperties

false

 rootPath

/

 workspace

products

Workbench

Workbench is a view that displays a list of content items in a workspace. It is part of the content app framework, typically defined in the browser subapp. The workbench contains at list of content views. Typical view types are tree, list and thumbnail. The workbench also provides a search box; results from search are displayed in the search view.

In this app, the workbench displays two node types: mgnl:folder and mgnl:product.

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
  browser:
    subAppClass: info.magnolia.ui.contentapp.browser.BrowserSubApp
    class: info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor
  # contentConnector definition
    workbench:
      dropConstraintClass: info.magnolia.ui.workbench.tree.drop.AlwaysTrueDropConstraint
      editable: false
Node nameValue

 browser

 

 workbench

 

 dropConstraintClass

info.magnolia.ui.workbench.tree.drop.AlwaysTrueDropConstraint

 editable

false

Content views

A content view defines how the content is displayed to the user. Under workbench, we have four views: tree, list, thumbnail and search. The tree view defines columns: Name, Status and Modification date. The other views extend the tree view, displaying the same columns.

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
  browser:
    subAppClass: info.magnolia.ui.contentapp.browser.BrowserSubApp
    class: info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor
  # contentConnector definition
    workbench:
      dropConstraintClass: info.magnolia.ui.workbench.tree.drop.AlwaysTrueDropConstraint
      editable: false

      contentViews:
        - name: tree
          class: info.magnolia.ui.workbench.tree.TreePresenterDefinition
          columns: &amp;myColumns
            - name: name
              editable: true
              sortable: true
              propertyName: jcrName
              class: info.magnolia.ui.workbench.column.definition.PropertyColumnDefinition
            - name: status
              width: 45
              displayInChooseDialog: false
              formatterClass: info.magnolia.ui.workbench.column.StatusColumnFormatter
              class: info.magnolia.ui.workbench.column.definition.StatusColumnDefinition
            - name: moddate
              width: 160
              sortable: true
              displayInChooseDialog: false
              formatterClass: info.magnolia.ui.workbench.column.DateColumnFormatter
              propertyName: mgnl:lastModified
              class: info.magnolia.ui.workbench.column.definition.MetaDataColumnDefinition
        - name: list
          class: info.magnolia.ui.workbench.list.ListPresenterDefinition
          columns: *myColumns
        - name: thumbnail
          class: info.magnolia.ui.workbench.thumbnail.ThumbnailPresenterDefinition
        - name: search
          class: info.magnolia.ui.workbench.search.SearchPresenterDefinition
          columns: *myColumns
Node nameValue
 workbench
 

 contentViews

 

 tree

 

 columns

 

 name

 

 class

info.magnolia.ui.workbench.column.definition.PropertyColumnDefinition

 editable

true

 label

Product name

 propertyName

jcrName

 sortable

true

 status

 

 class

info.magnolia.ui.workbench.column.definition.StatusColumnDefinition

 displayInChooseDialog

false

 formatterClass

info.magnolia.ui.workbench.column.StatusColumnFormatter

 label

Status

 width

45

 moddate

 

 class

info.magnolia.ui.workbench.column.definition.MetaDataColumnDefinition

 displayInChooseDialog

false

 formatterClass

info.magnolia.ui.workbench.column.DateColumnFormatter

 label

Modification date

 propertyName

mgnl:lastModified

 sortable

true

 width

160

 class

info.magnolia.ui.workbench.tree.TreePresenterDefinition

 list

 

 columns

 

 extends

../../tree/columns

 class

info.magnolia.ui.workbench.list.ListPresenterDefinition

 thumbnail

 

 class

info.magnolia.ui.workbench.thumbnail.ThumbnailPresenterDefinition

 search

 

 columns

 

 extends

../../tree/columns

 class

info.magnolia.ui.workbench.search.SearchPresenterDefinition

Here's the tree view:

Test the other views too. Search is case sensitive by default.

Actions

Actions allow you to add, edit and delete folders and products. Each action adheres to an action definition.

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
  browser:
    subAppClass: info.magnolia.ui.contentapp.browser.BrowserSubApp
    class: info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor
  # contentConnector definition
  # workbench definition
    actions:
      addProduct:
        subAppId: detail
        icon: icon-add-item
        nodeType: mgnl:product
        appName: products
        class: info.magnolia.ui.contentapp.detail.action.CreateItemActionDefinition
        availability:
          root: true
          nodeTypes:
            - mgnl:folder
      addFolder:
        icon: icon-add-folder
        class: info.magnolia.ui.framework.action.AddFolderActionDefinition
        availability:
          root: true
      editFolder:
        icon: icon-edit
        dialogName: ui-framework:folder
        class: info.magnolia.ui.framework.action.OpenEditDialogActionDefinition
      deleteFolder:
        icon: icon-delete
        class: info.magnolia.ui.framework.action.DeleteItemActionDefinition
      editProduct:
        subAppId: detail
        icon: icon-edit
        appName: products
        class: info.magnolia.ui.contentapp.detail.action.EditItemActionDefinition
        availability:
          nodeTypes:
            - mgnl:product
      deleteProduct:
        icon: icon-delete
        class: info.magnolia.ui.framework.action.DeleteItemActionDefinition
Node nameValue
 browser
 

 actions

 

 addProduct

 

 availability

 

 nodeTypes

 

 folder

mgnl:folder

 root

true

 appName

products 

 class

info.magnolia.ui.contentapp.detail.action.CreateItemActionDefinition

 icon

icon-add-item

 nodeType

mgnl:product

 subAppId

detail

 addFolder

 

 availability

 

 root

true

 class

info.magnolia.ui.framework.action.AddFolderActionDefinition

 icon

icon-add-folder

 editFolder

 

 class

info.magnolia.ui.framework.action.OpenEditDialogActionDefinition

 dialogName

ui-framework:folder

 icon

icon-edit

 deleteFolder

 

 class

info.magnolia.ui.framework.action.DeleteItemActionDefinition

 icon

icon-delete

 editProduct

 

 availability

 

 nodeTypes

 

 product

mgnl:product

 appName

products

 class

info.magnolia.ui.contentapp.detail.action.EditItemActionDefinition

 icon

 icon-edit

 subAppId

 detail

 deleteProduct

 

 class

info.magnolia.ui.framework.action.DeleteItemActionDefinition

 icon

 icon-delete

Action bar

An action bar makes actions available to users. The action bar is typically displayed on the right-hand side of the browser subapp. The action bar definition organizes actions into sections (green label) and groups (between horizontal lines). Availability rules determine which section is displayed to the user. For example, when the user selects a content item in the browser subapp, availability rules display only actions that are relevant to working with that item. A group contains actions that have something in common, such as actions for adding things.

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
  browser:
    subAppClass: info.magnolia.ui.contentapp.browser.BrowserSubApp
    class: info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor
  # contentConnector definition
  # workbench definition
  # actions definition
    actionbar:
      defaultAction: editProduct
      sections:
        - name: root
          groups:
            - name: addActions
              items:
                - name: addProduct
                - name: addFolder
          availability:
            nodes: false
            root: true
        - name: folder
          groups:
            - name: addActions
              items:
                - name: addProduct
                - name: addFolder
            - name: editActions
              items:
                - name: editFolder
                - name: deleteFolder
          availability:
            nodeTypes:
              - mgnl:folder
        - name: product
          groups:
            - name: editActions
              items:
                - name: editProduct
                - name: deleteProduct
          availability:
            nodeTypes:
              - mgnl:product

Node nameValue

 browser

 

 actionbar

 

 sections

 

 root

 

 groups

 

 addActions

 

 items

 

 addProduct

 

 addFolder

 

 availability

 

 nodes

false

 root

true

 folder

 

 groups

 

 addActions

 

 items

 

 addProduct

 

 addFolder

 

 editActions

 

 items

 

 editFolder

 

 deleteFolder

 

 availability

 

 nodeTypes

 

 folder

mgnl:folder

 product

 

 groups

 

 editActions

 

 items

 

 editProduct

 

 deleteProduct

 

 availability

 

 nodeTypes

 

 product

mgnl:product

 defaultAction

editProduct

Image provider

Image provider is a component that renders images used in apps. It generates the portrait image at the bottom of the action bar and the thumbnails for the thumbnail view. 

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
  browser:
    subAppClass: info.magnolia.ui.contentapp.browser.BrowserSubApp
    class: info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor
  # contentConnector definition
  # workbench definition
  # actions definition
  # actionbar definition
    imageProvider:
       class: info.magnolia.dam.app.ui.imageprovider.DamLinkImageProviderDefinition
       damLinkPropertyName: image
Node nameValue

 browser

 

 imageProvider

 

 class

info.magnolia.dam.app.ui.imageprovider.DamLinkImageProviderDefinition

 damLinkPropertyName

image

Select an item in the browser subapp to test the preview. The preview image is displayed at the bottom of the action bar.

Detail subapp

A detail subapp allows you to edit a product. It is configured with a subapp descriptor just like a browser subapp but the classes are different.

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
# browser subapp definition
  detail:
    subAppClass: info.magnolia.ui.contentapp.detail.DetailSubApp
    class: info.magnolia.ui.contentapp.detail.DetailSubAppDescriptor
Node nameValue

 subApps

 

 detail

 

 class

 info.magnolia.ui.contentapp.detail.DetailSubAppDescriptor

 subAppClass

 info.magnolia.ui.contentapp.detail.DetailSubApp

Content connector

A JCR content connector is also needed in the detail subapp. Here the definition is simpler. You just need to name the workspace. 

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
# browser subapp definition
  detail:
    subAppClass: info.magnolia.ui.contentapp.detail.DetailSubApp
    class: info.magnolia.ui.contentapp.detail.DetailSubAppDescriptor
    contentConnector:
      workspace: products
Node nameValue
 subApps
 

 detail

 

 contentConnector

 

 workspace

products

The rootPath property is omitted because the default value is / which is the same as in browser subapp content connector. If you define these properties they must have the same value in both subapps. However, since / is the default value you could omit the property in both subapps.

We highly recommend using the same rootPath in both the detail and browser sub-apps, to benefit from our standard set of actions provided out of the box.

Editor

Editor is a component that edits a content item. The editor typically contains a form. In a content app, you should create an editor definition under the detail subapp. Define the node types the editor edits, a form for editing them, and actions for saving the edit.

Node types
/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
# browser subapp definition
  detail:
    subAppClass: info.magnolia.ui.contentapp.detail.DetailSubApp
    class: info.magnolia.ui.contentapp.detail.DetailSubAppDescriptor
  # contentConnector definition
    editor:
      nodeType:
        icon: icon-items
        name: mgnl:product 
Node nameValue

 detail

 

 editor

 

 nodeType

 

 icon

icon-items

 name

mgnl:product

Form

Your app can edit any kind of content. What you plan to edit determines what fields you need on the form. In this tutorial the items are products so we create a form with three fields: product name, photo and photo credit.

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
# browser subapp definition
  detail:
    subAppClass: info.magnolia.ui.contentapp.detail.DetailSubApp
    class: info.magnolia.ui.contentapp.detail.DetailSubAppDescriptor
  # contentConnector configuration
    editor:
    # nodeType definition
      form:
        tabs:
          - name: product
            fields:
              - name: jcrName
                class: info.magnolia.ui.form.field.definition.TextFieldDefinition
              - name: title
                class: info.magnolia.ui.form.field.definition.TextFieldDefinition
              - name: image
                class: info.magnolia.ui.form.field.definition.LinkFieldDefinition
                targetWorkspace: dam
                appName: assets
                label: Select image
                identifierToPathConverter:
                  class: info.magnolia.dam.app.assets.field.translator.AssetCompositeIdKeyTranslator
                contentPreviewDefinition:
                  contentPreviewClass: info.magnolia.dam.app.ui.field.DamFilePreviewComponent
              - name: description
                class: info.magnolia.ui.form.field.definition.RichTextFieldDefinition 

Node name

Value

 editor

 

 form

 

 tabs

 

 product

 

 fields

 

 jcrName

 

 class

info.magnolia.ui.form.field.definition.TextFieldDefinition

 name

jcrName

 title

 

 class

info.magnolia.ui.form.field.definition.TextFieldDefinition

 name

title

 image

 

 identifierToPathConverter

 

 class

info.magnolia.dam.app.assets.field.translator.AssetCompositeIdKeyTranslator

 contentPreviewDefinition

 

 contentPreviewClass

info.magnolia.dam.app.ui.field.DamFilePreviewComponent

 appName

assets

 class

info.magnolia.ui.form.field.definition.LinkFieldDefinition

 label

Select image

 targetWorkspace

dam

 description

 

 class

info.magnolia.ui.form.field.definition.RichTextFieldDefinition

Commit and cancel actions

Commit and cancel are generic reusable actions that save the form data. They are defined under the detail subapp. The corresponding dialog buttons are in the editor (see above).

/app-tutorial/apps/products.yaml
appClass: info.magnolia.ui.contentapp.ContentApp
class: info.magnolia.ui.contentapp.ContentAppDescriptor
subApps:
# browser subapp definition
  detail:
    subAppClass: info.magnolia.ui.contentapp.detail.DetailSubApp
    class: info.magnolia.ui.contentapp.detail.DetailSubAppDescriptor
  # contentConnector definition
    editor:
    # nodeType definition
    # form definition
      actions:
        - name: commit
        - name: cancel
Node nameValue

 detail

 

 actions

 

 commit

 

 cancel

 

Create a few items. Verify that you can edit existing items too.

Now do it yourself

Here are ways to create your own content app:

Option 1: Copy an app

  1. Copy the configuration nodes of an existing content app into a new app. You can copy one of Magnolia's native apps such as Contacts.
  2. Export the copied configuration nodes into XML.
  3. Open the XML in a text editor. Search and replace the item names. For example, if your app manages apples instead of oranges, replace apple with orange.
  4. Import the XML back to Magnolia.
  5. Register a new workspace in the module descriptor and create a new node type.

Option 2: Extend an app

This method works well if one of Magnolia's native apps does almost what you need. You don't need to change much. Extend the native app to get all its functionality and add something special. For example, configure a special Pages app that displays only one website branch. If editors mostly work in that branch the app saves them from having to navigate there.

  1. Extend an existing app to inherit configuration. For example, see how the JCR Browser app extends the Configuration app by inheriting its subapps.
  2. Define exceptions in your own app.

See also  3.3.5 Create an app in Magnolia Academy.

Enhancements

Here are ideas for enhancing your content app and making it ready for production use:

Add import and export actions

Add export and import actions so you can export products into XML. See how the actions are implement in the Contacts app. Copy the action configuration to your app. The Magnolia UI framework provides generic export and import actions that you can use out of the box.

Import and export actions in the Contacts app
/modules/contacts/apps/contacts/subApps/browser/actions/export
/modules/contacts/apps/contacts/subApps/browser/actions/import
/modules/contacts/apps/contacts/subApps/browser/actionbar/sections/root/groups/importExportActions
/modules/contacts/apps/contacts/subApps/browser/actionbar/sections/contact/groups/importExportActions
/modules/contacts/apps/contacts/subApps/browser/actionbar/sections/folder/groups/importExportActions

Add publishing actions

To add publishing actions:

  1. Add activate and deactivate actions so that you can publish products to the public instance. Copy the actions from the Contacts app.
  2. Add the activate  and  deactivate  actions in the action bar. You can copy these also from the Contacts app.
  3. Add a subscription to the  products workspace so that public instances receive the content.

(warning) Don't forget to install your module also on the public instance! You may not need the app on the public instance but you will need the products workspace so that products can be activated.

Add deletion approval

Prevent editors from deleting products with a single click. The best practice is to mark an item for deletion and require the editor to publish the change:

  1. Display a confirmation message, asking if the user really wants to delete the item.
  2. Mark the item as deleted.
  3. Activate the change. This finally deletes the item.

See how the Contacts app handles this and replicate the actions in your app. See also how the  info.magnolia.ui.api.availability.IsNotDeletedRule action availability rule is used in the activate action to make sure that the activatable item is not marked for deletion. 

Display products on the website

You may want to display product images on the website or in a shop. Add a URI2RepositoryMapping for the products workspace. The mapping tells Magnolia to serve content from the products workspace when the request URL contains the /products prefix.

To add a URI2RepositoryMapping:

  1. Go to Configuration > /server/URI2RepositoryMapping/mappings.
  2. Map the /products URI prefix to the products workspace.

    Node nameValue

     server

     

     URI2RepositoryMapping

     

     mappings

     

     products

     

     URIPrefix

    /products

     handlePrefix

     

     repository

    products

  3. Activate the products node and its properties to the public instance.
  4. Optional: Grant the anonymous role permission to read the products workspace. This allows visitors to view the images on the public instance.
     

 


Photo credits:

#trackbackRdf ($trackbackUtils.getContentIdentifier($page) $page.title $trackbackUtils.getPingUrl($page))