Part II - Complex content types and security set up
Part I - My first content type is a prerequisite to Part II.
This is the second part of the tutorial about how to use Magnolia content types.
In this part, you will define relationships between different content
types, try out the
multiple property and learn to handle complex use
You will see how you can store data in different languages and how to
set up security for a production environment.
In order to promote their tour guides, the travel agency Magnolia Travels decides to provide information about which office each tour guide works out of and what vehicles are available for tourists. For example, the tour office in Ho Chi Minh City currently owns five Yamaha Nuovo 135 bikes.
In the first part of the tutorial, we created a content type for tour guides. In this part, we will define two additional content types in the same domain:
A tour office has one or more tour guides. In addition, each tour office has one or more tour vehicles available.
In the next section, we will define content types and app descriptions for the tour vehicles and the tour offices. Later on, we will define the relationships between different content types and extend the content types.
Add the content type definitions and app descriptors for the tour
vehicles and the tour offices to the light module
The new content types require a
name property; we will use the
created name property to define the name property.
A tour vehicle has a name, vehicle type, number of seats and description.
datasource: workspace: tourvehicles namespaces: mt: https://www.magnolia-travel.com/jcr/1.0/mt autoCreate: true model: nodeType: mt:tourVehicle properties: - name: vehicleType - name: numberOfSeats type: Double - name: description type: richText
!content-type:tourVehicle name: tourVehicles-app # subApps: detail: editor: form: tabs: default: fields: - name: vehicleType fieldType: select label: Vehicle type options: - name: bicycle value: bicycle - name: motorbike value: motorbike - name: van value: van - name: boat value: boat - name: other value: other
A tour office has a name, country, city and address. It may link to tour vehicles and to tour guides.
datasource: workspace: touroffices rootPath: / namespaces: mt: https://www.magnolia-travel.com/jcr/1.0/mt autoCreate: true model: nodeType: mt:tourOffice properties: - name: city - name: address - name: country # - name: tourGuides # - name: allVehicles
The fields for the linked tour guides and vehicles are not properly defined yet, we will do this in the following sections.
Your light module should now have the following structure:
content-type-examples/ ├── apps/ │ ├── tourGuides-app.yaml │ ├── tourOffices-app.yaml │ └── tourVehicles-app.yaml ├── contentTypes/ │ ├── tourGuide.yaml │ ├── tourOffice.yaml │ └── tourVehicle.yaml └── i18n └── content-type-examples-backend_en.properties
Since you have created new apps, you also need new user interface
labels. Update your messages file
to use the following version:
# APPS # # app "tourVehicles-app" # tourVehicles-app = Tour vehicles tourVehicles-app.default.label = Tour vehicle tourVehicles-app.default.name.label = Name tourVehicles-app.default.description.label = Description tourVehicles-app.default.numberOfSeats.label = Number of seats tourVehicles-app.default.vehicleType.label = Type tourVehicles-app.default.vehicleType.options.bicycle = Bicycle tourVehicles-app.default.vehicleType.options.motorbike = Motorbike tourVehicles-app.default.vehicleType.options.van = Van tourVehicles-app.default.vehicleType.options.boat = Boat tourVehicles-app.default.vehicleType.options.other = Other # browser tourVehicles-app.browser.label = Tour vehicles # actionbar tourVehicles-app.browser.actionbar.sections.root.label=Vehicles tourVehicles-app.browser.actionbar.sections.folder.label=Folder tourVehicles-app.browser.actionbar.sections.item.label=Vehicle # actions tourVehicles-app.browser.actions.addItem.label=Add vehicle tourVehicles-app.browser.actions.editItem.label=Edit vehicle # # app "tourOffices-app" # tourOffices-app = Tour offices tourOffices-app.browser.label = Tour offices tourOffices-app.default.label = Tour offices tourOffices-app.default.name.label = Name tourOffices-app.default.city.label = City tourOffices-app.default.country.label = Country tourOffices-app.default.address.label = Adress tourOffices-app.default.tourGuides.label = Guides tourOffices-app.default.allVehicles.label = All vehicles tourOffices-app.default.allVehicles.vehicleGroup.vehicle.label = Vehicle tourOffices-app.default.allVehicles.vehicleGroup.amount.label = Amount # actionbar tourOffices-app.browser.actionbar.sections.root.label=Office tourOffices-app.browser.actionbar.sections.folder.label=Folder tourOffices-app.browser.actionbar.sections.item.label=Office # actions tourOffices-app.browser.actions.addItem.label=Add office tourOffices-app.browser.actions.editItem.label=Edit office # # app "tourGuides-app" # tourGuides-app = Tour guides tourGuides-app.default.label = tourGuides-app.default.name.label = Name tourGuides-app.default.birthday.label = Birthday tourGuides-app.default.gender.label = Gender tourGuides-app.default.gender.options.female = Female tourGuides-app.default.gender.options.male = Male tourGuides-app.default.gender.options.other = Other tourGuides-app.default.shortBio.label = About tourGuides-app.browser.label = Tour guide # actionbar tourGuides-app.browser.actionbar.sections.root.label=Guides tourGuides-app.browser.actionbar.sections.folder.label=Folder tourGuides-app.browser.actionbar.sections.item.label=Guide # actions tourGuides-app.browser.actions.addItem.label=Add guide tourGuides-app.browser.actions.editItem.label=Edit guide
Magnolia adds the tiles for the new apps to the App launcher automatically when registering the apps. To make the app tiles appear in the App launcher, you must first restart your Magnolia session once by logging out and logging in again. For further details see App launcher layout.
You can define relations between different content types. An item of type A can have zero to many links to items of type B. This is called a 0-n relationship.
In this section we define a relation between tour offices and tour guides. An office may have one or many tour guides.
We will also use the
multiple property to make it possible to add more
than one tour guide to a given office.
An office may have more than one tour guide. Edit the
tourOffice content type:
model: nodeType: mt:tourOffice properties: - name: city - name: address - name: country - name: tourGuides type: reference:tourGuide multiple: true
Line 14: For the property
tourGuides, we define its
reference:tourGuide. This value is the name of an existing content type.
Line 15: The
multipleproperty with the value
truemakes it possible to add more than one tour guide.
tourOffices-app app with the app launcher tile and add a tour
guide to a tour office.
Submodels are useful whenever you need a group of fields more than once.
A content type model definition can contain a list of submodel
definitions within the
Submodels can be used only within the content type where they have been
A submodel definition has the same properties as a model, but a submodel cannot have additional submodels.
nodeType for a submodels is
We defined the content type for tour vehicles at the beginning of this part of the tutorial. A tour vehicle has the properties: name, vehicle type, number of seats and description.
Now we will extend the tour office to make it possible to add tour vehicles to it. We need to be able to specify the number of different vehicles. For example, the tour office in Ho Chi Minh City currently owns five Yamaha Nuovo 135 bikes.
To map this case, we add a submodel to the
tourOffice content type.
Here is a fragment which shows a submodel:
subModels: - name: vehicleGroup properties: - name: vehicle type: reference:tourVehicle - name: amount type: Double
There is one submodel named
The submodel has two properties:
vehicle– Its type is
reference:tourVehicle, which means that it is referring to the
amount– A number to indicate how many different vehicles are available.
After you define the submodel, you must add a property in the model with
This is the updated
model of the
tourOffice content type definition
with the submodel added:
model: nodeType: mt:tourOffice properties: - name: city - name: address - name: country - name: tourGuides type: reference:tourGuide multiple: true - name: allVehicles type: vehicleGroup multiple: true subModels: - name: vehicleGroup properties: - name: vehicle type: reference:tourVehicle - name: amount type: Double
Lines 19-25: The submodel definition.
Line 20: The submodel’s
Lines 16-18: The model property
typehas the value
vehicleGroup, which is the name of a submodel.
multipleenables you to choose more than one of the submodel
Now the tour offices app is ready to handle vehicles as described in the use case:
datasource: workspace: touroffices namespaces: mt: https://www.magnolia-travel.com/jcr/1.0/mt autoCreate: true model: nodeType: mt:tourOffice properties: - name: city - name: address - name: country - name: tourGuides type: reference:tourGuide multiple: true - name: allVehicles type: vehicleGroup multiple: true subModels: - name: vehicleGroup properties: - name: vehicle type: reference:tourVehicle - name: amount type: Double
We’ve looked at how to link from one content type to another. You can also link from a content type to an already existing app such as the Magnolia Assets subapp, which is not based on a content type definition.
To demonstrate this, we will extend the tour guide content type (and its
app) with a new
image property to assign an asset (an image) to a tour
To do so:
Add a new property to the model definition of the content type. Give it an arbitrary name. Here we use
model: nodeType: mt:tourVehicle properties: - name: vehicleType - name: numberOfSeats type: Double - name: description type: richText - name: image
Override the defaults of the field that were generated for the property:
!content-type:tourVehicle name: tourVehicles-app # subApps: detail: editor: form: tabs: default: fields: - name: image fieldType: link 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
fieldType and defining a
field in the app descriptor is a feasible way to link to any content
To link to an asset, instead of defining the link field in the app
descriptor (as showed above) - use
type: asset in the content type
definition. In this case there is no more need to override the field in
the app descriptor.
model: nodeType: mt:tourVehicle properties: - name: image type: asset
This is what the adapted
tourVehicles-app looks like:
The properties of a content type item can be stored in multiple different languages.
If localization and internationalization (i18n) are completely new to you, we suggest you read the Language section of this documentation.
Here is an overview of how you enable multilanguage content:
Enable multilanguage authoring.
i18n=truein the properties of the model definition.
Define locales in the site.
To try out this feature, change the
tourVehicle content type so that
we can store the different language content for the
Enable multilanguage authoring in
/server/i18n/authoring. This allows editors to enter the same content in multiple languages.
server: i18n: authoring: class: info.magnolia.multisite.i18n.MultiSiteI18nAuthoringSupport enabled: true
required, default is
Enables multilanguage content entry.
A class that implements I18NAuthoringSupport such as:
By default, the Magnolia instance is already
configured for the
You must enable localization of content type items for each property.
Here is an example showing only the
description property enabled for
datasource: # ... model: nodeType: mt:tourVehicle properties: - name: vehicleType - name: numberOfSeats type: Double - name: description type: richText i18n: true - name: image
If you are using the
If you have no site definition, configure a site where you define at
i18n node as shown below.
Define the languages that editors should be able to choose as locales in
the site definition under
cs locales in an example
test site definition.
sites: test: i18n: enabled: true class: info.magnolia.cms.i18n.DefaultI18nContentSupport fallbackLocale: en defaultLocale: cs locales: cz: country: CZ language: cs enabled: true de: country: DE language: de enabled: true
optional, default is
Enables support for localized content. Used to rewrite URIs and getting nodes based on the current language.
Class that implements I18nContentSupport such as:
optional, default is
Content is served for the fallback locale if content is not available for the current locale.
If no locale can be determined, the default locale will be set. If no default locale is defined, the fallback locale is used.
Locale ID that consists of language and country such as
Country code such as
Language code such as
optional, default is
Enables the locale.
In this section, we look at how to set up security for content types and their apps in a production environment. We cover both JCR access security and app security.
In a local development environment, you can use the
Superuser has the permissions required to manage the content types and
related apps that you built during this tutorial. In a production
environment, you should create users with specific roles and deactivate
the superuser account.
In this tutorial, we will create the
ct-examples-editor role to edit
the content type items on the author instance.
The content type items are stored in the JCR.
Add a role with read and write permissions for the JCR workspaces:
Log in to the Magnolia author instance as a user (such as
superuser) with permission to write on the
Click Add new role.
Provide values for the fields in the Role info tab:
Full name: Example role that can edit tourguides, touroffices and tourvehicles workspaces.
In the Access control lists tab, you see a list of all JCR workspaces visible to the current user.
Create an access control list (ACL) for the workspaces
tourvehicles. with the following settings:
Selected and subnodes
/ (in the last field)
Save the new role.
The system automatically creates another ACL for
Here is a screenshot from the JCR ACLs for the
In a production environment, you have different users editing content such as tour guides, tour offices, tour vehicles and others. These users do not have the superuser role.
magnolia-community-demo-webapp we are using emulates a production
environment. It already contains a user named
eric that has several
roles to edit different types of content.
To assign the new
ct-examples-editor role to
eric the editor:
Log in to the Magnolia author instance as a user (such as
superuser) with permission to write on the
Open the Security app > Users subapp.
ericand click Edit user.
Click the Roles tab. There is a Twin-column field.
ct-examples-editorrole in the left Other available roles column and move it using the
>>button to the right Granted roles column.
Click Save changes.
Log out and in again as the user
eric with the password
Eric cannot see the app launcher tiles for the apps
tourVehicles-app yet. You must enable the tour
apps for Eric; you will learn how do this in the App
In a production environment, you would also create a role to grant read
access for the new workspaces
tourvehicles. Read only access would be assigned to the special system
anonymous. This would make sure that anonymous website visitors
on the public instance would see content, once rendered, from those
How to create this role and how to render the content types item on a website are not within the scope of this tutorial.
Without any further configuration, by default, only the
is granted access to an app.
The app descriptor, which defines an app, has a property named
permissions. In this property, you can add a list of roles for which
you want to grant app
Add the following code snippet to the very bottom of the YAML files for
permissions: roles: [ct-examples-editor]
!content-type:tourOffice name: tourOffices-app # .- subApps: detail: editor: form: tabs: default: fields: - name: address fieldType: text rows: 5 # security permissions: roles: [ct-examples-editor]
Log out and in again as the user
eric (you assigned the role
ct-examples-editor to Eric earlier).
Eric the editor can now use all three apps:
tourVehicles-app. He can add, edit and delete
content items in the JCR workspaces:
You have built a complete set of content types including complex types with submodels. You have created the apps to manage the content items for these content types. You have defined relationships between content types and other, non content type-based apps. Finally, you have learned to properly adjust security settings.
The next logical step would be to set up your Magnolia system to display the tour guides, the tour vehicles and the tour offices on your public instances for your website visitors. This could be done in different ways.
- Some suggestions
Headless consumption of the content types
Define a REST endpoint (using the Delivery API) for each of the three content types.
Embed content type items as components into pages