Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the...

222
Sulu 2.0 Documentation Release 1.6 Sulu Team Sep 03, 2018

Transcript of Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the...

Page 1: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 DocumentationRelease 1.6

Sulu Team

Sep 03, 2018

Page 2: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework
Page 3: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Contents

1 What’s in our documentation? 31.1 The Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2 Cookbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501.3 Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051.4 Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1601.5 Developer Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204

i

Page 4: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

ii

Page 5: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Sulu makes Content Management awesome.

Here’s the perfect place to get started and find everything you need to know to code with Sulu. We got The Book,Cookbook, Reference, Bundles and Developer Guide.

Be sure to visit our website. There’s a lot of information about Sulu and how we do things.

Sulu aims to manage business content in a beautiful interface based on solid, extendable state of the art technol-ogy.

Contents 1

Page 6: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

2 Contents

Page 7: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

CHAPTER 1

What’s in our documentation?

1.1 The Book

When you work through the book you’ll build a website based on Sulu from the scratch. At first we explain some ofour very basic design decisions. Based on that we’ll get your project started.

Be sure to know a thing or two about Symfony. Sulu is based on Symfony and structured according to the Symfonystandards. If you don’t know how to work within the Symfony framework you might encounter some problems whenusing Sulu.

What’s in the book?

1.1.1 Introduction

We created Sulu because we believe some things could work different - in terms of content management - than theydo at the moment. In Sulu there’s a lot of discussion, reflection, experience and passion. We made a lot of decisionsand want to give you a short overview on why we chose to do things the way we do them.

When is Sulu the right choice?

Sulu isn’t made for everyone. There are some requirements where other Content Management Systems are better.

Sulu is the right choice when you can answer some of the following questions with yes.

• Do you want to work with the Symfony stack?

• You got a running app based on Symfony and want to add a content management layer?

• Do you need a clean, solid development setup?

• Default solutions won’t fit the requirements?

• You want to create a modern website based on modern technology?

• Do you need total freedom over your frontend code?

3

Page 8: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

4 Chapter 1. What’s in our documentation?

Page 9: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• Does your project require multiple languages?

• Should there be several portals?

• Usability and design of the backend are important?

Answering some of the following questions with yes, may be a hint that another technology may be a better pick.

• You want to choose from tons of ready to use templates?

• You want to create a simple blog?

• You have never used Symfony before?

• You want to host the website yourself and have no experience in server setup?

No matter what the outcome of our little survey is, we’d love to show you more about Sulu.

What is a Content Management Platform?

When it comes to Content Management a lot of terms are in the wild. CMS, CMF, CMP. . . What else could youimagine? We see Sulu more as a “Content Management Platform” as it isn’t made for websites only and we want tokeep it highly customizable.

A “Content Management Platform” is a set of components and modules that can be used to build a content administra-tion and publishing environment customized to the individual needs of the project. It’s used for data driven systems,web-apps or large scale web platforms. A “Content Management System” (CMS) is a more or less standardizedsoftware with a fixed set of functionalities mainly focused on the management of websites.

Sulu is a Content Management Platform which comes with all necessary modules to be used as a CMS. In this docu-mentation we will focus mainly on the usage as a Content Management System.

Sulu is capable of managing the content of websites and fully support the 4-step “Content Life Cycle” (http://en.wikipedia.org/wiki/Web_content_lifecycle):

1. Content creation/collection

2. Revision/approval

3. Publishing

4. Archiving

The standard publishing format is HTML but the platform is multi-channel, meaning other formats (such as e.g. XML)can be easily applied. Other strengths are the support of multi-languages and multi-site structures with shareablecontent.

Sulu was designed to create webpages with high performance in the current state of the Internet. This means ahigh focus on web standards (as described in the W3C standards http://www.w3.org/standards/) and search engineoptimization.

Although Sulu focuses on more complex websites it still provides an UI that is easy to understand, fast to learn andenjoyable to use. The software runs on modern browsers without the need of any plugins and will work on a standardstate-of-the-art PC or tablet.

We already covered some of Sulu’s strength. The next chapter will stake out the main focus.

What is Sulu’s focus?

While developing Sulu we had a special vision in mind. Besides “Content management made awesome” we wantedto create a technology that

• supports what a business needs.

1.1. The Book 5

Page 10: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• looks good and is easy to use.

• helps developers excel.

Business

Our world is going global. Businesses target several markets where several languages are spoken. Companies are splitinto brands with dozens of products. Complex structures arise. With Sulu we build up a data pool which feeds a set ofportals in a couple of languages.

Design

We put a lot of love and manpower into the design and user interface of our administration area. Editors are guidedintuitive through a clean set of organized screens. The live preview provides immediate and robust visual feedback.You can’t create ugly content, it will directly get back on you.

Technology

Developers should focus on building awesome applications. They should not think about where they put things andhow to structure them. They also shouldn’t think about on how to hack stuff. That’s why we chose Symfony as a base.A clean, modern framework that allows developers to adapt, extend and modify functionality where ever they want.

We learned what Sulu focus on. Next we’ll show what comes with Sulu in a basic installation.

How Sulu is structured?

This graphic shows how the core of Sulu is setup.

6 Chapter 1. What’s in our documentation?

Page 11: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Data

Symfony uses PHPCR, Doctrine and MySQL, MariaDB or Jackrabbit.

Basic Frameworks

Sulu is based on Symfony and the Symfony CMF.

Sulu

Everything else is Sulu. In the next step there is more information about the components.

Which components are packed into Sulu?

The standard installation of Sulu comes with a set of components (called „modules“) required for the content manage-ment process:

1.1. The Book 7

Page 12: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Contacts

The Contact module has two main purposes: The first (and in many cases most important) is managing the users thathave access to the administration backend of the website. The second is organizing the user-data collected through thewebsite (e.g. newsletter registrations etc.). In more complex environments this module can also be used to managecommunity members, online-shop customers or other contact based data.

Assets

The Assets module let you upload and organize any type of documents such as pictures, text documents (PDF, Word,Excel etc.), videos or audio clips. Once uploaded an asset can be used in as many webpages as required remainingits single source in the Assets module. This means if you would like to change a document that is used in severaldifferent webpages you would only have to replace it once in Assets. Pictures will be automatically transformed toweb-compatible formats and resized to the required formats of the templates while the original file will be stored aswell. All other document types remain in their original format.

Webspaces

A webspace is the place where the actual website structure and content will be created. Within a webspace, onesingle content-structure will be defined, but by using e.g. multiple languages and sub-domains, an unlimited numberof websites that share the same structure may be available. Furthermore, an unlimited number of webspaces can bemanaged in one Sulu installation.

Confused? Maybe this example helps:

ACME Inc. has a website www.acme.com that needs to be published in English, German and French. The easy wayto do this is to let the user choose their desired language and stay on the same domain displaying the required contentusing sub-domains, such as e.g. www.acme.com/de. For the user or a search engine, this would mean 1 website with3 languages sharing the same content structure.

Next, let’s assume that ACME Inc. wants to dedicate each language to its correspondent market by using top-level-domains. This would of course be more marketing oriented and search engine friendly. The English content would bepublished in www.acme.com, the German content in www.acme.de and the French content in www.acme.fr. Let’s goeven further and say that each website’s design should be a little different, maybe with a different header color. Theuser and the search engine would now have 3 separate websites, each with 1 language and individual design but allwith the same content-structure.

Any of these scenarios can be implemented with Sulu using one single webspace.

Settings

As the title implies this module gives you access to all internal adjustments of Sulu. One very important section is“Permissions”, where you can create user roles with access rights which then can be applied to a user in the Contactsmodule. This gives you complete control over the access rights of your website administrators. In addition, you canmanage meta information such as categories or tags that are used in other modules.

Now that you know all the components of Sulu, we’ll have a closer look at one of the paradigms we committedourselves to: Separation of concerns.

Separation of Concerns

Like most modern Content Management Systems, Sulu completely separates content from design. It urges templatebased content rendering. Since usability, web standards and SEO are of such great importance, templates take a big

8 Chapter 1. What’s in our documentation?

Page 13: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

role in the creation of a Sulu based web platform.

To ensure that templates make use of all the possibilities that the CMS provides, Sulu includes a set of tools anddevelopment guidelines. Many of these can be found in this documentation and we strongly recommend to carefullyread through and use them as much as possible. By doing so, your content will be much easier to manage, theperformance of your system will be optimized, and your website will be more easily accessible for users, searchengines and third party applications.

Here are some of the separations we did.

Template vs. Theme

As usual in Symfony, the structure of data is separated from its presentation. In Sulu, the structure of a page, a socalled template, is defined. The template describes how the administration interface of the page looks and how thedata is passed to the theme. The theme itself is - by default - a set of Twig templates.

We already heard some content specific terms on this page. On the next page we’ll have detailed look at the ContentArchitecture.

Content Architecture

Our content is heavily structured. As mentioned in How Sulu is structured?, we rely on the Symfony CMF on top ofPHPCR. On this page we’ll get into the Sulu Content Architecture.

Sulu Instance

At first, you create a Sulu Instance. A Sulu instance can be seen as a single installation or one pool of data. Its reallive mapping could be a company or an organization.

Webspace

On your instance you’ll define webspaces. Webspaces could represent your brands and corporations. A landing pagecould be a single webspace.

Languages

Once you created your webpsaces, you could define languages. Pages could then be translated or mapped to anotherlanguage as shadow- or ghost-pages.

Page

As already heard in Separation of Concerns pages are configured in templates. They are created in webspaces andrepresent an entry in a certain menu. These pages are contained within your webspace. Once you got your setup right,you can start exploring the Sulu default structure in the backend.

1.1. The Book 9

Page 14: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Content-Type

A template is built up of several content types. A blog post, for example, could consist of the following content types:

• Single line text (Title)

• Multi line text (Infobox)

• Richtext (Content)

• Date (Event-Date)

• List of Tags (Tags)

A detailed list of all the content types is covered in Creating a Page Template.

Assets

Assets are media files like images and downloads. They are shared through all the Websites on the instance.

Snippets

Snippets are very similar to assets. They are small pieces of content that could be shown on several pages in severalwebspaces.

Contacts

Contacts represent personal information. They are also used to manage Sulu Users itself.

Whatever you want

Sulu is very extensible. You got existing content you want to integrate through DBAL? Do it. An API that deliverscontent? Integrate it. Other Symfony bundles you’ve already coded. Integrate them.

A more detailed documentation could be found in the section About the Sulu Content Architecture.

Now you are really deep into the concepts of Sulu. So the next step is to get started. Getting Started

It is important to understand the underlying concepts of Sulu, but if you want to skip this part and start coding, headover to Getting Started.

1.1.2 Getting Started

Are you ready for a quick start with Sulu? Sit back, fasten your seat belts and. . . go!

Bootstrap a Project

We’ll bootstrap a new project based on the Sulu Minimal Edition with Composer:

composer create-project sulu/sulu-minimal my-project -n

10 Chapter 1. What’s in our documentation?

Page 15: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

This command will bootstrap a new project in the directory my-project.

Tip: Now is a good time to start versioning your project. If you use Git, initialize a new Git repository and submityour first commit:

cd my-projectgit initgit add .git commit -m "Initial commit"

Webspaces

The content management part of Sulu is built upon webspaces. Each of these webspaces configure a content tree. Eachcontent tree may contain translations for different locales.

The default webspace configuration is located in app/Resources/webspaces/example.com.xml. Renamethis file so that it matches the name of your project.

To get started, change the <name> and the <key> of the webspace to the name of your project. The name is ahuman-readable label that is shown in the administration interface. The key is the unique identifier of the webspace:

<?xml version="1.0" encoding="utf-8"?><webspace xmlns="http://schemas.sulu.io/webspace/webspace"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/webspace/webspace http://schemas.

→˓sulu.io/webspace/webspace-1.1.xsd">

<name>My Project</name><key>my-project</key>

<!-- ... --></webspace>

Caution: Changing the <key> of a webspace later on causes complications. We recommend to decide what keyto use before you build the database in the next step.

We’ll return to webspaces later in this book.

Setup the Database

Next we’ll setup a database for Sulu. You can use Sulu with the database backends supported by Doctrine DBAL.Some of those are currently still untested:

Platform SupportedMySQL yesPostgreSQL yesOracle untestedMicrosoft SQL Server untestedSAP Sybase SQL Anywhere untestedSQLite noDrizzle untested

1.1. The Book 11

Page 16: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Once you created a database, user and password, adapt the database_* keys of your app/config/parameters.yml file. Here is an example for using Sulu with MySQL:

parameters:database_driver: pdo_mysqldatabase_host: 127.0.0.1database_port: nulldatabase_name: hellosuludatabase_user: hellosuludatabase_password: averystrongpassworddatabase_version: 5.6

Tip: The parameter reference contains more information about each of the parameters in this file.

When you’re done with the configuration, populate the database with Sulu’s default data:

bin/adminconsole sulu:build dev

Caution: This command adds a user “admin” with password “admin” to your installation! If you don’t want toadd that user, pass the argument prod instead:

bin/adminconsole sulu:build prod

Optionally, you can store the content of your website (all tables starting with phpcr_) in Apache Jackrabbit. We’llget back to that later.

Start a Web Server

Now that the database is ready, we’ll fire up a server to try Sulu in the browser.

Sulu is made up of two separate applications for the administration interface and the website. Each application isoptimized for its purpose. The applications can be managed with the command line tools bin/adminconsole (forthe administration) and bin/website (for the website).

However, we will run one server for both applications, and our front controller will make sure the correct applicationis loaded.

bin/console server:start

You can access the administration interface via http://127.0.0.1:8000/admin. The default user and password is “admin”.

The web frontend can be found under http://127.0.0.1:8000.

Tip: If you want to learn more about using Sulu with a real web server, read Server Configuration.

Next Steps

Your Sulu website is ready now! Check out the administration, create pages and play around.

When you’re ready to learn more, continue with Creating a Page Template.

12 Chapter 1. What’s in our documentation?

Page 17: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.1.3 Creating a Page Template

In Sulu, each page has a page template. The page template controls two things:

• the structure of the page

• how that structure is rendered

The structure of a page consists of properties, each of which has a (content) type. The default page template, forexample, defines the following properties:

• title of type text_line

• url of type resource_locator

• article of type text_editor

When a content manager opens a page in the administration interface, they can change the values of these properties.At last, frontend designers can access these values and render them according to the desired design.

Each page template is defined by two files:

• an XML file that contains the page structure

• a Twig file that contains the HTML code

For example, the default template – named “default” – is defined by the files app/Resources/templates/pages/default.xml and app/Resources/views/templates/default.html.twig. The Sulu Min-imal Edition also contains a second template named “homepage”, which you can find in the same directories.

This guide focuses on the configuration of the page structure in the XML file. If you want to learn more about renderingthe pages in Twig, read Rendering Pages with Twig.

Choosing the Template of a Page

The template of a page can be selected in the admin interface:

Caution: A template is shown in the dropdown only if both the XML and the Twig file exist! If you can’t seeyour template, double-check the directories app/Resources/templates/pages and app/Resources/views/templates.

The name displayed in the dropdown is configured in the <meta> section of the XML:

1.1. The Book 13

Page 18: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<!-- app/Resources/templates/pages/default.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<meta><title lang="en">Default</title>

</meta>

<!-- ... --></template>

You can customize the text by changing this property.

Creating a Custom Template

In your projects, you will need several templates for different parts of your website. The easiest way is to copy andadjust the default template.

The first thing you need to adjust is the <key>. This is the unique identifier of the template:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd">

<key>event</key>

<!-- ... --></template>

Caution: Currently the <key> has to be identical to the filename of the template minus the .xml suffix.

The second thing you have to customize is the <view>. This element stores the Twig file that is used to render thetemplate:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<view>templates/event</view>

<!-- ... --></template>

14 Chapter 1. What’s in our documentation?

Page 19: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Note: Sulu automatically adds the .<format>.twig suffix to the view string, depending on the format requestedby the client (HTML, JSON, XML, . . . ).

Instead of the folder notation with the / you can use the Symfony’s naming convention without the file extension forTwig templates.

We’ll talk more about the Twig file itself in Rendering Pages with Twig. Let’s continue with adding properties to ourpage template.

Properties

Properties make up the structure of a page. They are defined in the element <properties>:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<properties><!-- ... -->

<property name="startDate" type="date"><meta>

<title lang="en">Start Date</title></meta>

</property>

<!-- ... --></properties>

</template>

A property has three essential attributes:

• a name that is unique within a template

• a type that defines what kind of content can be stored

• a title that is shown in the administration interface

Here is a table with the content types shipped in Sulu core:

1.1. The Book 15

Page 20: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Key Appearance in the administration Valuetext_line simple text input stringtext_area text area stringtext_editor text editor with formatting capabilities HTML stringcheckbox checkbox booleansingle_select list of radio buttons stringmultiple_select list of checkboxes array of stringscolor color picker stringdate date picker stringtime text input with time validation stringurl text input with URL validation stringemail text input with email validation stringpassword password input stringphone text input for a phone number stringinternal_links widget for selecting links to other pages resolved pages as defined in parameterssin-gle_internal_link

widget for selecting a single page resolved page as defined in parameters

smart_content widget for configuring a data source resolved pages as defined in parametersresource_locator widget for entering the URL of a page stringtag_list autocomplete input for entering and

adding tagsarray of strings

category_list autocomplete input for entering andadding tags

array of strings

media_selection widget for selecting media (images, doc-uments)

array containing arrays with urls for every format

con-tact_selection

widget for selecting contacts array containing array representations of the con-tact objects

teaser_selection widget for displaying content teasers array containing array representations of theteasers

snippet widget for selecting snippets array containing array representations of the snip-pets

Tip: Use the command sulu:content:types:dump to list all the content types available in your project:

bin/adminconsole sulu:content:types:dump

Many content types can be configured by passing parameters in the element <params>. For a single select, forexample, you need to set the possible choices:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<properties><!-- ... -->

<property name="eventType" type="single_select">(continues on next page)

16 Chapter 1. What’s in our documentation?

Page 21: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<meta><title lang="en">Event Type</title>

</meta><params>

<param name="values" type="collection"><param name="concert">

<meta><title lang="en">Concert</title>

</meta></param><param name="festival">

<meta><title lang="en">Festival</title>

</meta></param>

</param></params>

</property>

<!-- ... --></properties>

</template>

More detail about the content types and their parameters can be found in the Content Type Reference.

Mandatory/Optional Properties

Properties are optional by default. If a content manager must fill out a property, set the attribute mandatory to true:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<properties><!-- ... -->

<property name="startDate" type="date" mandatory="true"><!-- ... -->

</property>

<!-- ... --></properties>

</template>

Sections

Properties can be grouped together in sections. Sections are visible in the administration interface only and have noother effect on the data model:

A section is identified by its name. This name is used for the anchor tag in the administration interface.

1.1. The Book 17

Page 22: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

As for properties, the label of the section goes into its <meta> tag:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<section name="organizationalDetails"><meta>

<title lang="en">Organizational Details</title></meta>

<!-- ... --></section>

<!-- ... --></template>

The properties in the sections are nested in a separate element below the section:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<section name="organizationalDetails"><!-- ... -->

<properties><property name="startDate" type="date">

<meta><title lang="en">Start Date</title>

</meta></property><property name="endDate" type="date">

<meta><title lang="en">End Date</title>

</meta></property>

</properties>(continues on next page)

18 Chapter 1. What’s in our documentation?

Page 23: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

</section>

<!-- ... --></template>

Content Blocks

Similar to sections, content blocks contain a list of fields. In content blocks, however, the content managers themselvescan add fields of different types and order them as they want:

Content blocks are defined with the <block> element. Like properties, they have a name that is used to access theircontent in Twig. The label of the content block is – you guessed it – set in the <meta> element:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"> (continues on next page)

1.1. The Book 19

Page 24: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<!-- ... -->

<block name="eventDetails"><meta>

<title lang="en">Event Details</title></meta>

<!-- ... --></block>

<!-- ... --></template>

The content managers can choose the type of each individual block from a dropdown. Attention, we’re not talkingabout content types! The users of the administration interface don’t even know what the quite technical concept of acontent type is.

Instead, you should think about your own types that make sense in your case. In this particular example, we want toprovide the following types to our users:

• “Text” for formatted text

• “Image Gallery” for a gallery of images

• “Quote” for a quote from an artist

We’ll define these types in the <types> element and set the default type in the default-type attribute:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<block name="eventDetails" default-type="text"><!-- ... -->

<types><type name="text">

<meta><title lang="en">Text</title>

</meta>

<!-- ... --></type><type name="imageGallery">

<meta><title lang="en">Image Gallery</title>

</meta>

<!-- ... --></type><type name="quote">

<meta><title lang="en">Quote</title>

</meta>(continues on next page)

20 Chapter 1. What’s in our documentation?

Page 25: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<!-- ... --></type>

</types></block>

<!-- ... --></template>

Each of our types can be mapped to one or multiple properties. These properties are shown in the administrationinterface when the content manager selects the type:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<block name="eventDetails" default-type="text"><!-- ... -->

<types><!-- ... -->

<type name="quote"><!-- ... -->

<properties><property name="text" type="text_area"/><property name="author" type="contact"/>

</properties></type>

</types></block>

<!-- ... --></template>

Note: The challenge here is to mentally separate block types from content types. You define block types yourself inthe <types> element and set the default selection in default-type. Only from the <property>, we referencea content type.

Aligning Fields on the Grid

Sulu’s administration interface uses a basic twelve-column grid for the properties. By default, each property is all thetwelve columns wide. If you reduce that width, properties automatically float next to each other if they fit within thetwelve columns:

The width of a property is configured in the colspan attribute:

1.1. The Book 21

Page 26: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<section name="organizationalDetails"><!-- ... -->

<properties><property name="startDate" type="date" colspan="6">

<!-- ... --></property><property name="endDate" type="date" colspan="6">

<!-- ... --></property>

</properties></section>

<!-- ... --></template>

Help Text

You can display a help text with additional information in properties and sections. Put the help text into the<info_text> element in the <meta> section:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<properties><!-- ... -->

<property name="endDate" type="date"><meta>

<!-- ... -->

<info_text lang="en">If the same as the start date, the event is treated asone-day event.

</info_text>(continues on next page)

22 Chapter 1. What’s in our documentation?

Page 27: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

</meta></property>

<!-- ... --></properties>

</template>

Including Other Templates

If you want to reuse a portion of a template in a different template, you can move the portion to a separate file andinclude it using XInclude.

Warning: XInclude currently does not work on Windows.

To enable XInclude, we’ll first add the namespace xmlns:xi="http://www.w3.org/2001/XInclude" toour document:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xi="http://www.w3.org/2001/XInclude"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.1.xsd">

<!-- ... -->

</template>

Now we can include the fragment in the template with the <xi:include> element:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xi="http://www.w3.org/2001/XInclude"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.1.xsd">

<!-- ... -->

<xi:include href="fragments/event-properties.xml"/>

<!-- ... --></template>

Note: The href contains a relative path to the included file.

The fragment itself must contain a <template> or a <properties> element as root. In this example, we’ll use a<properties> container:

1.1. The Book 23

Page 28: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<!-- app/Resources/templates/pages/fragments/event-properties.xml --><?xml version="1.0" ?><properties xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://

→˓schemas.sulu.io/template/template-1.1.xsd">

<property name="startDate" type="date" mandatory="true"><!-- ... -->

</property>

<!-- ... --></properties>

Including a Fragment of a Template

If you want to pick single properties or sections of another template, use an XPointer. XPointers are similar to CSSselectors and match a specific part of an XML document.

As example, imagine that you have a generic “Event” template and a more specific “Concert” template that reuses theproperties of the “Event” template. Let’s look at the “Event” template first:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xi="http://www.w3.org/2001/XInclude"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.1.xsd">

<!-- ... -->

<properties><!-- ... -->

<property name="startDate" type="date" mandatory="true"><!-- ... -->

</property>

<!-- ... --></properties>

</template>

Nothing new here. To include these properties in the “Concert” template, pass an XPointer that selects these elementsin the xpointer attribute of the <xi:include> tag:

<!-- app/Resources/templates/pages/concert.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xi="http://www.w3.org/2001/XInclude"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.1.xsd">

<!-- ... -->

(continues on next page)

24 Chapter 1. What’s in our documentation?

Page 29: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<properties><!-- ... -->

<xi:include href="event.xml"xpointer="xmlns(sulu=http://schemas.sulu.io/template/template)

xpointer(/sulu:properties/sulu:property)"/>

<!-- ... --></properties>

</template>

The XPointer starts with the root element <properties> in the sulu namespace and selects all <property>children.

If you want to select an individual property with a specific name, that’s possible:

<!-- app/Resources/templates/pages/concert.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xi="http://www.w3.org/2001/XInclude"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.1.xsd">

<!-- ... -->

<properties><!-- ... -->

<xi:include href="event.xml"xpointer="xmlns(sulu=http://schemas.sulu.io/template/template)

xpointer(/sulu:properties/sulu:property[@name='startDate'])"/>

<!-- ... --></properties>

</template>

This XPointer starts with the root element <properties> in the sulu namespace and selects all <property>children with the attribute name set to “startDate”.

You can also match multiple elements of different types. Use the wildcard * for that:

<!-- app/Resources/templates/pages/concert.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xi="http://www.w3.org/2001/XInclude"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.1.xsd">

<!-- ... -->

<properties><!-- ... -->

<xi:include href="event.xml"xpointer="xmlns(sulu=http://schemas.sulu.io/template/template)

(continues on next page)

1.1. The Book 25

Page 30: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

xpointer(/sulu:properties/*)"/>

<!-- ... --></properties>

</template>

Caching

Eventually you will start tweaking your pages for performance. Caching pages on the client is one of the easiestperformance improvements you can do.

You can configure a different caching strategy for each template. Add a <cacheLifetime> element with thenumber of seconds that your page should be cached on the client:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<cacheLifetime type="seconds">2400</view>

<!-- ... --></template>

The cache lifetime will be sent to the client in the max-age field of the Cache-Control header. After the specifiedtime, the cache will be invalidated on the client. The next time the page is requested, the client will send a new requestto your server to update its cache.

Caution: When you use client-side caching, be aware that there is no way to invalidate the client-side cache ondemand. Prepare for having to wait for the given cache lifetime until all clients receive an updated version of yourwebsite. To shorten this time, it’s generally a good idea not to set the cache lifetime too high.

There is a second type that you can use to specify the cache lifetime: expression. With that type, you can passthe lifetime as cron expression. For example, if you know that your homepage changes its content each day at 8:00AM, set the value to 0 8 * * *:

<!-- app/Resources/templates/pages/event.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><!-- ... -->

<cacheLifetime type="expression">0 8 * * *</view>

<!-- ... --></template>

26 Chapter 1. What’s in our documentation?

Page 31: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Search

That a property is indexed in the search the property need to be tagged.

<property name="title" type="text_line"><meta>

<title lang="en">Title</title></meta><tag name="sulu.search.field" role="title" />

</property>

<property name="images" type="media_selection"><meta>

<title lang="en">Images</title></meta><tag name="sulu.search.field" role="image" index="false" />

</property>

The tag can have specific attributes:

• role: The role for the property

• type: Type how the data need to be stored

• index: Is indexed need only to be set to deactivate index

Roles:

• title: The main title of the document

• description: The main description of the document

• image: The main image of the document

Types:

• string: For simple fields

• array: For multiple fields such as the category_list content type

• tags: Special typ for tag_list content type

• date: For indexing the date content type

• json: For indexing raw data in the search

Next Steps

We learned a lot about configuring the structure of a page template. Continue with Rendering Pages with Twig to learnmore about rendering this structure as HTML.

1.1.4 Rendering Pages with Twig

Twig is an awesome option for rendering HTML. It got some nice features like blocks and inheritance. That’s why weuse and love Twig.

1.1. The Book 27

Page 32: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Build the HTML template

We recommend to build the HTML templates in a first draft with plain HTML and some dummy texts. That meansabsolutely no placeholders for template engines. This ensures that your HTML is working across all browsers (at leastif you test it correctly), and it is easier to test, since there are guaranteed no errors caused by the some wrong templatevariables.

Please make also sure that your HTML is valid, and use HTML tags in a semantic way, so that your website willachieve the best results in terms of search engine optimization.

Which Twig-Template is used?

In Creating a Page Template we learned how to define a template.

1 <?xml version="1.0" ?>2 <template xmlns="http://schemas.sulu.io/template/template"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd">5

6 <key>default</key>7

8 <view>templates/default</view>9 <controller>SuluWebsiteBundle:Default:index</controller>

10 ...11 </template>

In the page template the view could be set. Internally Sulu appends the format of the request to findthe correct template to render the response. As an example sulu uses for a html request the templateapp/Resources/views/templates/default.html.twig or app/Resources/views/templates/default.xml.twig for a xml request.With this feature you are able to define different output format for a single page.

Rendering the Content

If you don’t use your custom controller and modify the output the Sulu Controller renders, Sulu passes some defaultvariables to Twig.

Content

In the content everything you defined in your template is saved. If you got a title you could easily obtain it from thecontent-var.

<h1>{{ content.title }}</title>

Extension

In the extension var Sulu writes content from Sulu extensions. Typically stuff that is defined in separate Tabs in theSulu content section. At the moment there is the SEO and the excerpt extension, that could be used. This extensionsare available on every page no matter which template you chose.

Here is an example how it could look like in the backend. Notice the Excerpt & Categories tab next to the SEO tab.

You could include the SEO meta tags like this:

28 Chapter 1. What’s in our documentation?

Page 33: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

{%- include 'SuluWebsiteBundle:Extension:seo.html.twig' with {seo: extension.seo,content: content,urls: urls,shadowBaseLocale: shadowBaseLocale,defaultLocale: request.defaultLocale

} -%}

The excerpt data is available from:

{{ extension.excerpt.title }}{{ extension.excerpt.description }}{{ extension.excerpt.more }}{{ extension.excerpt.icon[0].thumbnails['50x50'] }}{{ extension.excerpt.images[0].thumbnails['300x300'] }}

View

In the view variable Sulu writes the view data of the defined properties in your template. As an example themedia_selection stores the displayOption there.

{{ view.media.displayOption }}

Other Variables

• request.webspaceKey: Contains the key for the current webspace

1.1. The Book 29

Page 34: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• request.locale: Contains the locale for the current request

• request.portalUrl: Contains the root URL to the current portal

• request.resourceLocatorPrefix: Contains the prefix for the current portal

• request.resourceLocator: Contains the resourceLocator to the current page

• uuid: Contains the uuid of the current page

• template: Contains the template key of the current page

• creator: Contains the id of the creator of the current page

• changer: Contains the id of the changer of the current page

• created: Contains the timestamp of the creation of the current page

• changed: Contains the timestamp of the latest change of the current page

• published: Contains the timestamp of the publishing of the current page

• urls: Contains urls of all locales

Navigation

There is a Twig function that obtains the menu. You need to pass the key of the navigation context you defined in yourwebspace (Setup a Webspace). While editing a page the navigation context could be defined in settings > Navigationcontext. For many projects one or two navigation contexts might be enough:

• The main navigation usually is the main entry point for the user of the website.

• A footer navigation can be useful for imprints and similar pages.

The following screenshot shows the Sulu homepage with the main navigation on the right and the footer navigationon the bottom. As you can see the navigation returned for the navigation contexts are not necessarily flat, but can alsocontain sub pages.

The navigation contexts can also be used in any other combination you want. The separation into main and footernavigation is only a quite common example.

The advantage of this method is that the content manager can decide on his own which pages to show in the navigation.This code show an example for creating a nested navigation using all the pages marked to be shown in the mainnavigation context.

1 <ul>2 {% for item in sulu_navigation_root_tree('main', 2) %}3 <li>4 <a href="{{ sulu_content_path(item.url) }}"5 title="{{ item.title }}">{{ item.title }}</a>6 {% if item.children|length > 0 %}7 <ul>8 {% for child in item.children %}9 <li><a href="{{ sulu_content_path(child.url) }}"

10 title="{{ child.title }}">11 {{ child.title }}12 </a></li>13 {% endfor %}14 </ul>15 {% endif %}16 </li>

(continues on next page)

30 Chapter 1. What’s in our documentation?

Page 35: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

17 {% endfor %}18 </ul>

Images

If there are images defined in your template you could render them by using this code:

1 {% for image in content.images %}2 <div>3 <img src="{{ image.thumbnails['200x100'] }}" alt="{{ image.name }}"/>4 <p>{{ image.title }}</p>5 </div>6 {% endfor %}

Image formats need to be defined in the image_formats.xml in your config.

More examples

You could find more examples of how content could be accessed in our example file.

1.1. The Book 31

Page 36: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Default Template

Just have a look at our default theme, that ships with our standard installation as long with our default page templatesover at github.

1.1.5 Setup a Webspace

In this chapter we will have a look at webspaces. Therefore we will create a configuration for a basic website. Thiswill result in a single portal website with multiple localizations.

As already described in the section before, a webspace also creates a new content tree. These trees are accessible bythe navigation in the Sulu administration interface. Sulu allows you to create pages and sub pages in these trees andfill them with content. Have a closer look at Creating a Page Template for more details on the content managementprocess.

Normally you’ll create a webspace for a new website, a landingpage or a portal, that should run on your Sulu instance.

The following file shows a configuration. These lines will be explained in the following paragraphs.

<?xml version="1.0" encoding="utf-8"?><webspace xmlns="http://schemas.sulu.io/webspace/webspace"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/webspace/webspace http://schemas.

→˓sulu.io/webspace/webspace-1.1.xsd">

<name>Example</name><key>example</key>

<localizations><localization language="en"/>

</localizations>

<default-templates><default-template type="page">example</default-template><default-template type="homepage">default</default-template>

</default-templates>

<templates><template type="search">ClientWebsiteBundle:views:search.html.twig</template><template type="error">ClientWebsiteBundle:views:error.html.twig</template><template type="error-404">ClientWebsiteBundle:views:error404.html.twig</

→˓template></templates>

<navigation><contexts>

<context key="main"><meta>

<title lang="en">Mainnavigation</title></meta>

</context></contexts>

</navigation>

<resource-locator><strategy>tree_leaf_edit</strategy>

</resource-locator>(continues on next page)

32 Chapter 1. What’s in our documentation?

Page 37: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<portals><portal>

<name>example</name><key>example</key>

<environments><environment type="prod">

<urls><url language="en">example.org</url>

</urls></environment><environment type="dev">

<urls><url language="en">example.lo</url>

</urls></environment>

</environments></portal>

</portals></webspace>

Note: If you want to match all hosts you can use the {host} placeholder. Example: <url>{host}/{localization}</url>

Note: If you add a webspace to an existing installation you also have to set the correct permissions for existing users,otherwise they won’t be able to see it.

As you probably already have encountered, the root tag for our webspace definition is webspace. Afterwards you see aname, which is displayed in the administration interface. But even more important is the key, which is used internallyto generate some files and define some paths. Therefore it is really important that the webspace key is unique acrossall webspaces in a single installation.

Localizations

In the localizations-tag you can list all the available localizations in this webspace. In the example we are simplyadding the English language, but you can also define country specific language if you add a country attribute to thelocalization, so for instance the following tag would add Austrian German to the available localizations:

<localization language="de" country="at" />

For a more complete explanation you should have a look at Adding localizations.

Themes (optional)

The theme is described by a key. This key leads to a certain theme, implemented by a developer in the system. Readmore about themes in the section Adding a theme. This feature is default deactivated and therefore in the example notused. If you have multiple webspaces which should look different, you can use this feature to easily do this.

1.1. The Book 33

Page 38: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Templates

The webspace can also define certain templates in combination with a type. These template can then be retrieved fromthe webspace. E.g. Sulu uses them to retrieve the correct templates for errors. Therefore it makes use of the templatewith type error-<http-code> respectively it uses the template with the type error as a fallback. The other usecase is the search. Sulu will use the template with the type search from the webspace to display search results.

Navigation

It’s also possible to define some so called navigation contexts, which allows the user to add pages to different kindof navigations. The different contexts can be defined in the navigation-section, and this selection will be available tochoose from in the administration interface. Afterwards the developer can retrieve the navigation for a given contextby using some Twig-extensions delivered with Sulu, whereby it is not only possible to retrieve a flat list of pages, butalso to retrieve entire navigation trees.

Resource-Locator (optional)

The strategy for the resource-locator influences the design of the URLs for the content. Default value is tree_leaf_edit,which means that the resource-locator will be generated for the whole tree, but only the last part will be editable.

Currently there is only one alternative tree_full_edit, which also generates the whole tree, but lets you edit the wholeresource-locator afterwards.

The strategy also influences the behavior when renaming or moving a page. The tree_leaf_edit (in oppositetree_full_edit) will also update the resource-locator of the children.

Portals

A webspace can itself consist of multiple portals. In our simple configuration file we make use of only one portal. Theidea is that the same content can be shared among different portals and URLs. The portals can then also define forthemselves in which localization they publish the content, so that you can spread different localizations over differentURLs.

Our sample file defines just one portal, which includes a name and a key just as the webspace, whereby the key for theportal hast to be unique for the entire installation, not only within this webspace.

URLs

The most important part of the portal configuration are the environments, because they are including the URLs for theportal. A portal can have multiple environments, which have to match the environments defined in Symfony. Usuallydev, stage and prod are available. Each environment can define its own set of URLs.

Note: Please consider that you have to omit the port in the configuration. The system will work with any port, so youdon’t have to name it in the configuration.

The URLs also have to include the localization somehow. You have two possibilities to do so:

34 Chapter 1. What’s in our documentation?

Page 39: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Fixing an URL to a specific localization

The above example shows this possibility, where you fix one URL to exactly one localization. The following fragmentshows again how to this:

<url language="de" country="at">www.example.org</url>

Since it is possible to define localizations without a country, this attribute is also optional here. However, the combi-nation of language and country here must result in an existing localization.

Using placeholders in the URL definition

Another possibility is to create the URL with a placeholder. Have a look at the following line for an example:

<url>www.example.org/{localization}</url>

Placeholder are expressions in curly braces, which will be expanded to every possible value. For the above examplethat means, that an URL for every localization defined will be generated. So if you have a localization de-at and en-gb,the system will create URLs for www.example.org/de-at and www.example.org/en-us.

In the following table all the possible placeholders are listed, and explains the values of them by using the de-at-localization:

Placeholder Description Example for de-at{localiza-tion}

The name of the entire localization de-at

{language} The name of the language de{country} The name of the country, only makes sense in combination with {language} at

Now you got your webspace ready, we will create a template for a page that could be added to the webspace.

1.1.6 Configure image formats

To show images on your website you should define different image formats for optimized output.

Image formats can be defined in:

• app/config/image-formats.xml

Or when you use the SuluThemeBundle you can define the formats in your theme folder:

• path/to/<theme>/config/image-formats.xml

The following example shows you different ways to define image formats:

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd">

<!-- Fixed width and dynamic height --><format key="300x">

<scale x="300"/></format>

(continues on next page)

1.1. The Book 35

Page 40: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<!-- Dynamic width and fixed height --><format key="x200">

<scale y="200"/></format>

<!-- Fixed width and fixed height --><format key="300x200">

<scale x="300" y="200"/></format>

<!-- Max width and max height --><format key="300x200-inset">

<scale x="300" y="200" mode="inset"/></format>

</formats>

Defining meta title

You should define a meta title for every image format, since these titles are shown to the content manager in theadministration interface when cropping the images.

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd"><format key="300x">

<meta><title lang="en">Author Avatar</title><title lang="de">Autor Avatar</title>

</meta>

<scale x="300"/></format>

<format key="920x"><meta>

<title lang="en">Header</title><title lang="de">Header</title>

</meta>

<scale x="920"/></format>

</formats>

Image Compression

Global image compression

Images will not get compressed by default, if you upload them. You can set the compression for images globally inthe app/config/config.yml.

To set the compression for all images you have to add following lines to your

36 Chapter 1. What’s in our documentation?

Page 41: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

config.yml:

sulu_media:format_manager:

default_imagine_options:jpeg_quality: 80png_compression_level: 6

Its recommended to have jpeg_quality between 70-90 as this is the best compromise between quality and image size.

Specific image compression

A image compression can also be set on a specific image format the following way:

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd"><format key="300x">

<scale x="300"/>

<options><option name="jpeg_quality">80</option><option name="png_compression_level">6</option>

</options></format>

</formats>

Transformations

There are several transformations available in sulu to add some effects to your images:

Blur

Will blur the image by a given sigma parameter:

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd"><format key="300x-blur">

<scale x="300"/>

<transformations><transformation>

<effect>blur</effect><parameters>

<parameter name="sigma">6</parameter></parameters>

</transformation></transformations>

(continues on next page)

1.1. The Book 37

Page 42: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

</format></formats>

Grayscale

Will convert the image into a black/white image:

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd"><format key="300x-black">

<scale x="300"/>

<transformations><!-- Black/white effect --><transformation>

<effect>grayscale</effect></transformation>

</transformations></format>

</formats>

Gamma

Will add a gamma effect by a given correction parameter:

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd"><format key="300x-gamma">

<scale x="300"/>

<transformations><!-- Gamma effect --><transformation>

<effect>gamma</effect><parameters>

<parameter name="correction">0.7</parameter></parameters>

</transformation></transformations>

</format></format>

</formats>

Sharpen

Will add a sharpen effect:

38 Chapter 1. What’s in our documentation?

Page 43: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd"><format key="300x-sharpen">

<scale x="300"/>

<transformations><!-- Sharpen effect --><transformation>

<effect>sharpen</effect></transformation>

</transformations></format>

</formats>

Paste

The paste transformation effect will add another image on top on the rendered image. This can be used to add a borderor a copyright to the image.

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd"><format key="300x300-border">

<scale x="300" y="300"/>

<transformations><!-- Paste effect --><transformation>

<effect>paste</effect><parameters>

<parameter name="image">@AppBundle/Resources/public/border.png</→˓parameter>

</parameters></transformation>

</transformations></format>

</formats>

The given image can be positioned by adding x, y, w h parameter:

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd"><format key="300x300-border">

<scale x="300" y="300"/>

<transformations><!-- Paste effect -->

(continues on next page)

1.1. The Book 39

Page 44: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<transformation><effect>paste</effect><parameters>

<parameter name="image">@AppBundle/Resources/public/border.png</→˓parameter>

<parameter name="x">0</parameter><parameter name="y">0</parameter><parameter name="w">300</parameter><parameter name="h">300</parameter>

</parameters></transformation>

</transformations></format>

</formats>

Combining Transformations

Transformation effect can also be combined the following way:

<?xml version="1.0" encoding="UTF-8"?><formats xmlns="http://schemas.sulu.io/media/formats"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/media/formats http://schemas.sulu.

→˓io/media/formats-1.1.xsd"><format key="300x-blur-black">

<scale x="300"/>

<transformations><transformation>

<effect>blur</effect><parameters>

<parameter name="sigma">6</parameter></parameters>

</transformation>

<transformation><effect>grayscale</effect>

</transformation></transformations>

</format></formats>

1.1.7 Adding localizations

Sulu is built for companies with an international focus, translating pages into multiple different languages is a veryimportant task for a content editor using Sulu. Sulu also considers the different variations of a language amongdifferent countries. The combination of these two factors is called a localization.

Configuring webspace localizations

Localizations for the content are configured in the webspaces, as already described in Setup a Webspace. Adding an-other localization is as easy as adding another localization tag to the webspace configuration file. Localizations

40 Chapter 1. What’s in our documentation?

Page 45: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

can also be nested, which has no impact on the representation in all the dropdowns, but it will help the system to findbetter fallbacks.

So a good example using english and german as a language might look something like the following fragment.

<localizations><localization language="en">

<localization language="en" country="us"/><localization language="en" country="gb"/>

</localization><localization language="de">

<localization language="de" country="de"/><localization language="de" country="at"/><localization language="de" country="ch"/>

</localization></localizations>

With this configuration the system will contain seven different content localizations: en, en-us, en-gb, de, de-de,de-at, de-ch, whereby en-us and en-gb are falling back to en, and de-de, de-at and de-ch are falling backto de.

After adding localizations in the webspace, note that you need to run

php bin/adminconsole sulu:document:initialize

This will re-initialize the PHPCR content tree, setting up the new locale for accepting new content. Afterwardsnobody will have any permissions on this locale, so make sure that you add this permission in the permissions tab ofthe contacts.

Adding custom localizations

There is another possibility for adding non webspace related localizations. More details can be found in How to addlocalizations with the localization provider?

Usage of localizations

For the developer the only touching points with localizations are the configuration and the eventual use of a languageswitcher on the homepage. For the language switcher the urls variable delivered to the twig template can be used,which contains an associative array with the localization code being the code and the value the URL to the currentpage in this language.

The template itself does not have to be adapted for usage with multiple localizations. The twig variables are the samefor every language, only the content is different, and this is handled by Sulu for the developer.

The nesting of the localizations is very important for the column navigation of the content. In case there is a ghostpage - which means that the page is not translated into the current localization - this tree will be used to determine the“closest” language available.

1.1.8 Using smart content

What is smart content?

The smart content is one of our most powerful content types. It allows the content editor to dynamically configure anaggregation of content, whereby content does not only mean pages. This is possible due to the data providers, whichcan be registered in the system. A data provider defines which options are supported, and is responsible for loading

1.1. The Book 41

Page 46: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

the data. Sulu is already shipped with different data providers, one for pages from the content management section,and another two for contacts and accounts.

The configuration will be resolved to a set of arrays, which can easily be used in a twig template.

Configure a smart content

The smart content is configured in the template definition. The template definition is already described in Creating aPage Template. All that has to be done is to add another property for the smart content. This configuration can looksomething like the following typical XML fragment:

<property name="pages" type="smart_content"><meta>

<title lang="en">Smart Content</title></meta>

<params><param name="properties" type="collection">

<param name="title" value="title"/><param name="description" value="excerpt.description"/>

</param><param name="present_as" type="collection">

<param name="one"><meta>

<title lang="en">One column</title></meta>

</param><param name="two">

<meta><title lang="en">Two column</title>

</meta></param>

</param></params>

</property>

In this XML fragment a smart_content property named pages is defined. For pages there is also the possibilityto define a properties parameter, as you can see in the previous fragment. In this collection property can bedefined which properties of the page should be returned in the array passed to the twig template. The name of theparameter describes how the property will be accessible in the twig template, and the value is the name of the propertyon the page. Additionally there is the excerpt extension, which can be used as well, there has just excerpt. to beprefixed. This extension is available for all pages, so it is a safe bet. The problem with other properties is that youhave to make sure or at least check in the twig template if the property exists.

The value of the present_as property is injected into a dropdown, where the content editor can choose betweendifferent styles, which of course have to be implemented by the creator of the twig template. Popular options here areone or two columns with variations like with or without images.

There are also more parameters to tweak the smart content, for a deeper understanding of this there is the referencedocumentation of the Smart content.

Use the smart content in a twig template

Using the pages that are returned from the smart content in a twig template is very easy. As already described the datais returned as an array in the twig template in the content variable. In the view variable the configuration data ofthe smart content is stored.

42 Chapter 1. What’s in our documentation?

Page 47: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

This way it is really simple to display this information using a twig template:

<div property="pages">{% for page in content.pages %}

<div class="{{ view.pages.present_as }}"><h2><a href="{{ sulu_content_path(page.url) }}">{{ page.title }}</a></h2><p>{{ page.description }}</p>

</div>{% endfor %}</div>

The pages in content.pages refers to the name of the property in the template definition. Every page beingreturned by the filter described in the smart content has its own array in this variable, so that we can iterate over it. Inthe view variable the configuration of the smart content is accessible, which can be used e.g. as a CSS class in thisexample. This way the one or two column layout can be created by using CSS.

The page loop variable can then be used to access the actual content from the page. A Sulu twig extension providesthe sulu_content_path method, which builds the final URL with all the additional information required.

For more and deeper information about twig there is the excellent twig documentation.

The next step is how to add localization to Sulu.

Pagination

The smart content supports pagination, which can be activated with the param max_per_page described in thecontent-type reference Smart content.

<ul class="pagination">{% set page = view.pages.page %}

{% if page-1 >= 1 %}<li><a href="{{ sulu_content_path(content.url) }}?p={{ page-1 }}">&laquo;</a>

→˓</li>{% endif %}{% if view.smartcontent.hasNextPage %}

<li><a href="{{ sulu_content_path(content.url) }}?p={{ page+1 }}">&raquo;</a>→˓</li>

{% endif %}</ul>

<div property="pages">{% for page in content.pages %}

<div class="{{ view.pages.present_as }}"><h2><a href="{{ sulu_content_path(page.url) }}">{{ page.title }}</a></h2><p>{{ page.description }}</p>

</div>{% endfor %}</div>

The view variable page contains the current page number (default: 1) and hasNextPage is a flag which is true ifanother page exists.

Warning: To avoid performance issues it is not possible to get a number of maximum page because the systemwould have to load the whole content of each page to determine how many pages would fit to the filters.

1.1. The Book 43

Page 48: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

If you want to use different parameters for different smart content on the same page you can define the GET-parameterwith the property param page_parameter.

1.1.9 Adding a theme

In the theme we’ll define how things look to the user. You know HTML, CSS, JS and such stuff.

What is a theme

A theme defines the way the content from Sulu is presented on the website. In general it’s not more than a simplefolder containing all the required twig templates, images, scripts, fonts and all the other assets you want to use in thisspecific theme.

You can have multiple themes in one Sulu installation. Every webspace can decide which theme to use, by a simplekey in the webspace configuration file already described in Setup a Webspace. This means that it is also very easy toswitch between different themes.

This feature is not shipped with a minimal sulu-installation. But can be easily integrated.

Installation

First add the dependency to the SuluThemeBundle in your composer.json file.

composer require sulu/theme-bundle

To enable it add the following lines into the app/AbstractKernel.php and app/config/config.yml.

abstract class AbstractKernel extends SuluKernel{

/*** {@inheritdoc}

*/public function registerBundles(){

$bundles = [...

new Sulu\Bundle\ThemeBundle\SuluThemeBundle(),new Liip\ThemeBundle\LiipThemeBundle(),

...];

...

return $bundles;}

}

# LIIP Theme Configurationliip_theme:

themes: ["default"]active_theme: "default"load_controllers: false

44 Chapter 1. What’s in our documentation?

Page 49: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

This will configure a default theme which can be enabled in the app/Resources/webspaces/<webspace>.xml file byadding:

<theme>default</theme>

Create a theme

Creating a theme is as easy as creating a new folder in the Resources/themes/ folder of your bundle with the name ofthe new theme. Afterwards you have to fill this folder with all the used templates in the webspace. These templates gointo another subfolder in your theme, which you have to reference later. We recommend to name this folder templates.It is also recommended to create a folder views for more general templates, like the master template, an error page,etc., and a folder blocks for reusable templates, like the seo information.

For more concrete information about the structure of these templates you should check the Creating a Page Template.

Enable the theme

For resolving the templates we are using the LiipThemeBundle, which requires you to register your themes. You cando that in your application configuration located at app/config/config.yml. Add the name of your theme folder to thefollowing list:

liip_theme:themes: ["default", "your-new-shiny-theme"]

1.1.10 About the Sulu Content Architecture

We already heard something about Content Architecture in the introduction. Now we are starting to code will dig alittle bit deeper.

Sulu uses PHPCR as a persistence layer, and therefore follows its structure. Additionally Sulu adds another layercalled webspaces, which have already been explained in the section about the Which components are packed intoSulu?. These webspaces contain an arbitrary number of pages, which are ordered in a tree in a hierarchical way. Eachof these pages can contain content in many different localizations.

This tree also represents the actual structure of the website, so that no additional navigation tree is required. Pages canbe enabled in the navigation, and will then appear in the right spot on the navigation of the website.

The pages in Sulu have a specific template applied. The template defines which properties the page will have, wherebyeach of these properties are further specified by a content type. The content type will have a direct impact on thepossible values and configuration possibilities of the property it is applied to. There is also a further reference of allthe available Content Type Reference.

There are also some advanced features regarding the pages in Sulu. Besides the content management using the proper-ties and content types already described there is also the possibility to define internal and external links. Internal linksredirect to other pages managed by the content management section of Sulu, and external link to an arbitrary URL.

Another useful feature is the shadow page functionality. It allows to use the content of another localization. So ifa webspace defines localizations for American and British English, it is possible to use the content of the AmericanEnglish for the British English, without managing the exactly same content again. This is especially useful if there aree.g. different contact addresses for each country, but the rest of the page should be exactly the same.

With the content architecture on our mind we’ll create a webspace.

1.1. The Book 45

Page 50: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.1.11 The Sulu Admin Interface

In the last steps you setup your own Sulu website. The admin area should already be accessible. It gives a niceimpression of the features of Sulu. And that’s just what this page does: Give you a short glance on the Sulu admininterface. Everything else you’ll learn by exploring it. There won’t be a dedicated documentation.

Dashboard

We got big plans for the dashboard, but at the moment it gives you a starting point with the implemented search. Justenter whatever you want to work on and it will give you direct links.

46 Chapter 1. What’s in our documentation?

Page 51: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Contacts

Contacts on the one hand cover the handling of personal information on the other hand they are also the user providerfor Sulu Users.

You’re also able to create organizations and connect people with them.

Assets

Sulu saves assets like images or downloadable files in a global pool. They could be shared throughout webspaces.

Settings

In the settings section, stuff that is closer to the system is defined. We got user roles, categories, tags and the cache.

User Roles

You could define several roles. The admin interface looks like this:

Categories

You could categorize stuff with categories.

1.1. The Book 47

Page 52: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

48 Chapter 1. What’s in our documentation?

Page 53: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.1. The Book 49

Page 54: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Tags

Sulu also got a tagging system.

Snippets

A Snippet is a global piece of content, that could be used in every webspace.

Content

The main part of the administration area is the content. You could edit whatever you defined before. Though it is notthe biggest part on this page, this is where Sulu happens.

Profile

You could edit the personal information of your user in this section.

Lots of fun with Sulu. We hope you’ll find the love we put in it while creating it.

1.2 Cookbook

Here are some awesome recepies for lunch. The Cookbook covers some advanced topics which each covers a veryspecial problem. Create your very own menu with our recipes.

50 Chapter 1. What’s in our documentation?

Page 55: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.2. Cookbook 51

Page 56: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Our recepies

1.2.1 Caching with Varnish

Varnish is a HTTP caching proxy server which can be used to radically improve the response time of your website.

Sulu is bundled with a “soft” caching proxy, the Symfony HttpCache, but using Varnish is a more optimal solution fora large website, especially if it has lots of traffic.

In addition to being twice as fast as the default caching implementation it also supports better cache invalidation, whichmeans that your website will appear more up-to-date.

Note: “Twice as fast” is relative. The default cache implementation can respond in 0.02s compared to varnishes 0.01s- the difference here is imperceptible - but varnish will scale better and supports better invalidation.

This tutorial will walk you through the process of setting up Varnish on your own server and configuring it to workwith Sulu.

This tutorial assumes that:

• You are using the Apache2 web server

• You are running Ubuntu or Debian

The steps should apply equally to other variants.

52 Chapter 1. What’s in our documentation?

Page 57: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.2. Cookbook 53

Page 58: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Install Varnish

On Ubuntu/Debian install Varnish as follows:

apt-get install varnish

This should install and start the Varnish daemon.

Server Configurations

Web Server

Note: You may skip this section if you intend to run varnish in a development environment and do not want to changethe default port of your web server.

For a caching server to serve pages to your users, it will need to “pretend” to be the web server. Web servers listento requests on port 80 by default. We must make Varnish listen for connections on port 80 and make the web serverlisten on a different port.

Note: We are going to make the web server listen on port 8090 but there is nothing special about this port and it canbe anything as long as it does not conflict with any existing services.

Change the Listen directive in /etc/apache2/ports.conf to Listen 8090:

# /etc/apache2/ports.conf# ...Listen 8090

And change any and all virtual hosts to now listen on 8090:

# /etc/apache2/conf.d/sites-available/sulu.conf# ...<VirtualHost \*:8090>

# ...</VirtualHost>

Now you will need to configure varnish.

Varnish

Note: Skip this section if you are in a development environment and prefer to access varnish via. its default port(explained later).

By default Varnish will listen for connections on port 6081 (at least on Debian systems). If you are running aproduction system you will need to change this to the default HTTP port, port 80.

Verify which port Varnish is listening to:

54 Chapter 1. What’s in our documentation?

Page 59: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

$ ps ax | grep varnish6585 ? SLs 0:00 varnishd -f /home/daniel/.varnish/sulu.vcl -s malloc,1G -T→˓127.0.0.1:2000 -a 0.0.0.0:60816609 ? Sl 0:07 varnishd -f /home/daniel/.varnish/sulu.vcl -s malloc,1G -T→˓127.0.0.1:2000 -a 0.0.0.0:6081

The -a option indicates where Varnish is listening - it is listening on port 8083, which is incorrect.

Under Debiae/Ubuntu we can change the initialization script:

# /etc/default/varnish

# ...DAEMON_OPTS="-a :80 \

-T localhost:6082 \-f /etc/varnish/default.vcl \-S /etc/varnish/secret \-s malloc,256m-p "vcc_allow_inline_c=on"

Now restart the daemon:

/etc/init.d/varnishd restart

Varnish Configuration

The following will add full caching support for Sulu:

# /etc/varnish/default.vclvcl 4.0;

C{#include <stdlib.h>

}C

acl invalidators {"localhost";

}

backend default {.host = "127.0.0.1";.port = "8090";

}

sub vcl_recv {if (req.method == "PURGE") {

if (!client.ip ~ invalidators) {return (synth(405, "Not allowed"));

}return (purge);

}

if (req.method == "BAN") {if (!client.ip ~ invalidators) {

return (synth(405, "Not allowed"));}

(continues on next page)

1.2. Cookbook 55

Page 60: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

if (req.http.x-cache-tags) {ban("obj.http.x-host ~ " + req.http.x-host

+ " && obj.http.x-url ~ " + req.http.x-url+ " && obj.http.content-type ~ " + req.http.x-content-type+ " && obj.http.x-cache-tags ~ " + req.http.x-cache-tags

);} else {

ban("obj.http.x-host ~ " + req.http.x-host+ " && obj.http.x-url ~ " + req.http.x-url+ " && obj.http.content-type ~ " + req.http.x-content-type

);}

return (synth(200, "Banned"));}

}

sub vcl_backend_response {# Set ban-lurker friendly custom headersset beresp.http.x-url = bereq.url;set beresp.http.x-host = bereq.http.host;

// Check for ESI acknowledgement and remove Surrogate-Control headerif (beresp.http.Surrogate-Control ~ "ESI/1.0") {

unset beresp.http.Surrogate-Control;set beresp.do_esi = true;

}

if (beresp.http.X-Reverse-Proxy-TTL) {/** Note that there is a ``beresp.ttl`` field in VCL but unfortunately

* it can only be set to absolute values and not dynamically. Thus we

* have to resort to an inline C code fragment.

** As of Varnish 4.0, inline C is disabled by default. To use this

* feature, you need to add `-p vcc_allow_inline_c=on` to your Varnish

* startup command.

*/C{

const char *ttl;const struct gethdr_s hdr = { HDR_BERESP, "\024X-Reverse-Proxy-TTL:" };ttl = VRT_GetHdr(ctx, &hdr);VRT_l_beresp_ttl(ctx, atoi(ttl));

}C

unset beresp.http.X-Reverse-Proxy-TTL;}

}

sub vcl_deliver {

if (!resp.http.x-cache-debug) {unset resp.http.x-url;unset resp.http.x-host;

}(continues on next page)

56 Chapter 1. What’s in our documentation?

Page 61: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

if (obj.hits > 0) {set resp.http.X-Cache = "HIT";

} else {set resp.http.X-Cache = "MISS";

}}

Restart Varnish:

$ /etc/init.d/varnish restart

And now have a look at the headers on your website:

$ curl -I mywebsite.comHTTP/1.1 200 OK# ...Via: 1.1 varnish# ...

If you see the above Via header, then all is good and your are ready to go forward.

Configuring Sulu Invalidation

You will first need to ensure that the default “soft” cache has been disabled.

Open the website front controller (app/website.php in the standard edition) and ensure that the following linesare commented out:

// Uncomment this line if you want to use the "symfony" http// caching strategy. See// if (SYMFONY_ENV != 'dev') {// require_once __DIR__ . '/../app/WebsiteCache.php';// $kernel = new WebsiteCache($kernel);//}

Warning: If you do not comment out the above lines caching will not work as you will be using 2 caches.

Now edit app/config.yml and change the proxy client from symfony to varnish and set the address of yourvarnish server (assuming that your Varnish server is on localhost and listening on port 80):

sulu_http_cache:# ...proxy_client:

varnish:enabled: trueservers: [ 'localhost:80' ]

Now have another look at the headers from your website:

$ curl -I sulu.loHTTP/1.1 200 OKHost: sulu.lo:6081

(continues on next page)

1.2. Cookbook 57

Page 62: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

Cache-Control: max-age=1000, public, s-maxage=1000Date: Fri, 16 Jan 2015 13:46:58 GMTContent-Type: text/html; charset=UTF-8X-Reverse-Proxy-TTL: 2400X-Sulu-Handlers: public, debugX-Sulu-Proxy-Client: varnishX-Sulu-Structure-Type: AnimalsPageCacheX-Sulu-Structure-UUID: 26f73515-253b-4f98-a227-8811b830735dX-Sulu-Page-TTL: 2400X-Debug-Token: f05a02X-Debug-Token-Link: /_profiler/f05a02X-Varnish: 131202 32868Age: 88Via: 1.1 varnish-v4X-Cache: HITContent-Length: 9240Connection: keep-alive

Note: If you chose not to make Varnish listen on port 80, then use sulu.lo:6081 instead.

The meaning of all these headers will be explained in the HttpCacheBundle document. But for now you should see(providing your are in dev mode) the X-Sulu-Proxy-Client has a value of varnish.

Optimal configuration

To get the most out of the Varnish cache you should enable the tags cache handler and disable the paths handler.

The tags handler will automatically ensure that any changes you make in the admin interface are immediatelyavailable on your website.

See the HttpCacheBundle document for more information.

The following is a full configuration example:

sulu_http_cache:handlers:

tags:enabled: true

public:max_age: 240 # 4 minutesshared_max_age: 480 # 8 minutesuse_page_ttl: trueenabled: true

debug:enabled: "%kernel.debug%"

proxy_client:varnish:

enabled: trueservers: [ '127.0.0.1:80' ]

1.2.2 Maintenance Mode

When you need to deploy a new version of your project on a production environment it is often necessary to disableyour sulu-application and inform your users about it.

58 Chapter 1. What’s in our documentation?

Page 63: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Sulu maintenance mode displays a simple holding page which can be easily customized.

Create Maintenance Mode

To create a maintenance page, you first need to create a maintenance.php file:

$ cp app/maintenance.php.dist app/maintenance.php

Then you need to set the environment variable SULU_MAINTENANCE to true. For example, in your .htaccessfile (for apache)

SetEnv SULU_MAINTENANCE true

Configure Maintenance Mode

Allowed IP addresses

You may like to access your application while maintenance mode is active. Then you need to set the allowed IPs:

<?php$allowedIPs = array(

'127.0.0.1');

Translations

You can define translations for your template as follows:

<?php$translations = array(

'en' => array('title' => 'Maintenance','heading' => 'The page is currently down for maintenance','description' => 'Sorry for any inconvenience caused. Please try again

→˓shortly.',),

);

Default locale

By default, maintenance.php is automatically detecting your browsers language. If no translation for this lan-guage exists the default locale is being used. By default this is English:

<?phpdefine('DEFAULT_LOCALE', 'en');

1.2.3 Running Sulu on Heroku

Heroku enables programmers to run their applications in the cloud, and makes it really easy to scale your applicationif it follows some rules. There is a Sulu cloud edition, which follows these rules.

1.2. Cookbook 59

Page 64: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

You will find a “Deploy to Heroku” button in the README.md file of the repository of the previously mentioned Sulucloud edition. This button leads to a Heroku page for deploying your very own Sulu installation.

Give the application a name and choose between Europe and United States as a region, whatever is closer to youractual destination. This choice will also influence the performance of your website.

Unless you really want to, you should leave the SYMFONY_ENV environment variable to prod. Otherwise the buidprocess will fail, because Heroku installs the dependencies with composer install --no-dev. If you changethe environment to dev or test Sulu wil try to load some Symfony bundles, which are not installed, and thereforelead to an error during the build procedure.

The other environment variable you have to set is DOMAIN. Set it to whatever domain this installation should work on,e.g. sulu-cloud.herokuapp.com if you don’t have your own DNS entry or to something like sulu.io.

For more details about working with Heroku you should check out the Heroku Dev Center.

1.2.4 Securing your application

Sulu is delivered with two different possibilities to protect parts of your application. The first is the permissions basedon security contexts, which allow you to restrict access to entire parts of your application or Sulu. The permissions forthis kind of security are managed on a roles level. In addition to that the localization for which these permissions arevalid has to be defined on the assignment of the role to the user.

The second way is to protect the access on a per-object basis. These permissions are set on the specific object. Theuser still has to have the correct localizations assigned in order to gain access.

This tutorial will show how to use Sulu’s security functionality with your own application specific code.

Protect content using a security context

This section describes how to protect an entire part of your application (but not a specific object).

Define your security context

First of all you have to define the security context, which is represented by a simple string. This is done in the Adminclass of your Bundle:

<?php

namespace Acme\Bundle\ExampleBundle\Admin;

use Sulu\Bundle\AdminBundle\Admin\Admin;use Sulu\Component\Security\Authorization\PermissionTypes;

class AcmeExampleAdmin extends Admin{

// ...

public function getSecurityContexts(){

return ['Sulu' => [

'Acme' => ['sulu.acme.example' => [

PermissionTypes::VIEW,

(continues on next page)

60 Chapter 1. What’s in our documentation?

Page 65: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

PermissionTypes::ADD,PermissionTypes::EDIT,PermissionTypes::DELETE,

],],

],];

}

// ...}

This information is defined in the getSecurityContexts method, which should return an array. The first leveldescribes the system to which the security context applies - this would either be Sulu (for stuff in the administration)or a different context that you have defined manually.

The second level just defines the title for another separation used in the administration interface. The third leveldefines the name of the permissions themselves. This name follows a namespacing scheme based on the previouslyused names. This value is the key for an array containing all the available permission types for this security context.

Note: Since the Admin class is registered as a bundle, you can make use of different services to define the availablesecurity contexts. For example the SuluContentBundle uses a service to create an own security context for all availablewebspaces in the system.

Protect your controller

After defining a security context, you can use it to easily protect the actions of one of your controllers. All you have todo is to implement the SecuredControllerInterface telling the SuluSecurityListenerwhich securitycontext and locale to use for the permission check:

<?php

namespace Acme\Bundle\ExampleBundle\Controller;

use FOS\RestBundle\Routing\ClassResourceInterface;use Sulu\Component\Security\SecuredControllerInterface;use Symfony\Component\HttpFoundation\Request;

class ExampleController implements ClassResourceInterface, SecuredControllerInterface{

public function cgetAction(){

// code for your get action}

public function postAction(){

// code for your post action}

// ...

public function getLocale(Request $request)

(continues on next page)

1.2. Cookbook 61

Page 66: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

{return $request->get('locale');

}

public function getSecurityContext(){

return 'sulu.acme.example';}

}

The getLocale method returns the locale, which is probably determined somehow by the request, and thegetSecurityContext method defines which security context is required to access this type of resource.

The SuluSecurityListener appends the information on which type of permission (view, add, edit, delete, . . . )is required, and automatically takes care of the permission check and returns a page with a status code of 403 in casethe permissions for the currently logged in user where not sufficient.

Protecting specific objects

For some parts of your application you might want to protect specific objects. This section will describe how this isdone with the possibilities Sulu offers.

Adding the permission tab to your form

First of all you have to add the permission tab to your form to enable the user to set up the permissions accordingly.The permission tab presents a list of the available user roles and a few permission icons, which can be activated.

Therefore the probably already existing ContentNavigationProvider has to be extended by the permission tab:

<?php

namespace Sulu\Bundle\MediaBundle\Admin;

use Sulu\Bundle\AdminBundle\Navigation\ContentNavigationItem;use Sulu\Bundle\AdminBundle\Navigation\ContentNavigationProviderInterface;use Sulu\Bundle\MediaBundle\Api\Collection;use Sulu\Component\Security\Authorization\PermissionTypes;use Sulu\Component\Security\Authorization\SecurityCheckerInterface;

class ContentNavigationProvider implements ContentNavigationProviderInterface{

private $securityChecker;

public function __construct(SecurityCheckerInterface $securityChecker){

$this->securityChecker = $securityChecker;}

public function getNavigationItems(array $options = []){

// also add your other ContentNavigationItems here

$navigation = [];

(continues on next page)

62 Chapter 1. What’s in our documentation?

Page 67: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

$securityContext = 'sulu.acme.example';

if ($this->securityChecker->hasPermission($securityContext,→˓PermissionTypes::SECURITY)) {

$permissions = new ContentNavigationItem('Permissions');$permissions->setAction('permissions');$permissions->setDisplay(['edit']);$permissions->setComponent('permission-tab@sulusecurity');$permissions->setComponentOptions(

['display' => 'form','type' => Example::class,'securityContext' => $securityContext,

]);

$navigation[] = $permissions;}

return $navigation;}

}

The Using tab navigation explains this code in more detail. The only important method call here is setComponentOp-tions, the rest can stay widely the same over all bundles (of course you can change the other configuration as well asdescribed in Using tab navigation).

In setComponentOptions the type of the object to secure and the required security context are passed. For the type itis a good idea to use the class name of the entity as shown in the example. The security context is required to check ifthe current user has the permission to change the security settings in the given context.

After this addition the permission tab should already be visible in the edit form.

Configure the controller

The second part is to implement the SecuredObjectControllerInterface in the Controller handling the specific type ofentities:

<?php

namespace Acme\Bundle\ExampleBundle\Controller;

use FOS\RestBundle\Routing\ClassResourceInterface;use→˓Sulu\Component\Security\Authorization\AccessControl\SecuredObjectControllerInterface;→˓

use Sulu\Component\Security\SecuredControllerInterface;use Symfony\Component\HttpFoundation\Request;

class ExampleControllerimplements ClassResourceInterface, SecuredControllerInterface,

→˓SecuredObjectControllerInterface{

public function cgetAction(){

(continues on next page)

1.2. Cookbook 63

Page 68: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

$listBuilder = $factory->create($this->container->getParameter('sulu.model.→˓example.class'));

$this->get('sulu_core.doctrine_rest_helper')->initializeListBuilder(→˓$listBuilder, $this->getFieldDescriptors());

$listBuilder->setPermissionCheck($this->getUser(), PermissionTypes::VIEW);

$listResponse = $listBuilder->execute();

// Do something with $listResponse}

public function postAction(){

// code for your post action}

// ...

public function getLocale(Request $request){

return $request->get('locale');}

public function getSecurityContext(){

return 'sulu.acme.example';}

public function getSecuredClass(){

return Example::class;}

public function getSecuredObjectId(Request $request){

return $request->get('id');}

}

The SecuredObjectControllerInterface required three different methods. The getLocale method is the same as inthe SecuredControllerInterface, and the implementation can be shared. The getSecuredClass method has to returnthe same identifier for the type of object as used in the ContentNavigationProvider. Finally the getSecuredObjectIdreceives the request object, and has to return the id of the object from it.

The rest of the work will be done by the SuluSecurityListener in the same way as for the check of the security contexts.

Note that the cgetAction needs some special handling when the ListBuilder is used. The ListBuilder contains a set-PermissionCheck method, which takes a user and a permission. If you pass these two, you will only receive rows forwhich the given user has the given permission granted.

1.2.5 Using tab navigation

It is very easy to build your own or to extend already existing tab navigations in Sulu. The general process of displayingsuch a tab navigation in the administration interface of Sulu covers the following steps:

64 Chapter 1. What’s in our documentation?

Page 69: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1. Your JavaScript component sends a request to /admin/content-navigations?alias=acme, andmight add more options as query parameters.

2. The server responds to this request based on so called ContentNavigationProviders, which are regis-tered to listen to a certain alias, and the passed query parameters.

3. The content is returned and Sulu’s JavaScript Tab component renders the delivered information for you as tabs.

This article will describe how this can be achieved in a few simple steps.

Create a content navigation provider

If you want to create your own tab navigation, you have to build a provider for it first. A provider is just asimple service implementing the ContentNavigationProviderInterface containing a function namedgetNavigationItems. The task of this function is to return an array of ContentNavigationItems, thefollowing lines show an example of this:

<?php

namespace Acme\Bundle\Example\Admin;

use Sulu\Bundle\AdminBundle\Navigation\ContentNavigationProviderInterface;use Sulu\Bundle\AdminBundle\Navigation\ContentNavigationItem;

class AcmeContentNavigationProvider implements ContentNavigationProviderInterface{

public function getNavigationItems(array $options = array()){

$item = new ContentNavigationItem('Item');$item->setAction('item');$item->setDisplay(array('edit'));$item->setComponent('item-tab@acmeexample');$item->setComponentOptions(array());$item->setDisplayConditions(

[new DisplayCondition('template', DisplayCondition::OPERATOR_EQUAL,

→˓'overview'),]

);

return array($item);}

}

The getNavigationItems function takes an array with options. These options are all the query parameters thatwere passed via the HTTP request. You can base certain decisions on these options like if some navigation items shouldbe created at all, or you can pass these options to the JavaScript components which will be started when selecting aspecific tab.

Note: Since this class will be registered as a service, you can inject any other service you want to help you decidewhich ContentNavigationItems you want to create. It is quite common to use SecurityChecker to checkfor certain privileges before creating an item.

The ContentNavigationItem takes several arguments. The action will be appended to the URL in the admin-istration interface. Display defines in which cases the tab is appearing (available options are new for forms creating

1.2. Cookbook 65

Page 70: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

a new entry and edit for forms editing already existing entries). Component sets the name of the aura componentwhich should be started and ComponentOptions are the options which will be passed to this component.

The displayConditions will be used to determine whether the tab should be displayed for the item or not. SeeHeader how to initialize this feature.

Register the content provider as a service

Afterwards you have to register your content navigation provider as a service in the dependency injection container.This is quite basic, but you have to add a tag named sulu.admin.content_navigation together with analias, which will be used by a service to find all content navigation providers for the request sent from the javascriptcomponent.

Note: You can also register multiple services with the same alias. The items will then be merged, this way it is veryeasy to extend existing content navigations.

<?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/

→˓schema/dic/services/services-1.0.xsd"><service id="acme_example.content_navigation" class=

→˓"Acme\Bundle\Example\Admin\AcmeContentNavigation"><tag name="sulu_admin.content_navigation" alias="acme"/>

</service></container>

1.2.6 Custom error page

With Sulu it is very easy to customize the error pages for your website users. You can define a template for each HTTPstatus code.

Configuration

The following code-block from the webspace configuration file shows a default configuration for the exception tem-plates. If you want to add an own exception for example 400 you can simply add it to the list. You can specify that foreach theme.

<templates><template type="error">ClientWebsiteBundle:views:error.html.twig</template><template type="error-404">ClientWebsiteBundle:views:error404.html.twig</template><template type="error-500">ClientWebsiteBundle:views:error500.html.twig</template>

</templates>

The ExceptionController uses the status-code of the response to determine which template is responsible for theexception. If no special template is defined it uses the template without an error code.

Twig-Template

In the twig-template you can use your website master template to reuse your style.

66 Chapter 1. What’s in our documentation?

Page 71: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

{% extends "ClientWebsiteBundle:views:master.html.twig" %}

{% block title %}Error {{ status_code }}{% endblock %}

{% block content %}<h1>Error {{ status_code }}</h1><p>{{ status_text }}</p>

<p>{{ exception.message }}</p>{% endblock %}

Warning: Be careful which variable you use in your master.html.twig. If you use variables which are not definedin the error-template, the error-page cannot be rendered.

Following variables are usable inside the exception template.

Name Descriptionstatus_code status_text exception cur-rentContent urls request.webspaceKeyrequest.defaultLocale re-quest.locale request.portalUrlrequest.resourceLocatorPrefix re-quest.resourcelocator request.getrequest.post request.analyticsKey

http-status-code http-status-code message complete exception object re-sponse content which were rendered before exception was thrown local-ized urls to start page (e.g. for language-switcher) key of the currentwebspace default locale of current portal current locale url of currentportal prefix for resourcelocators of current portal current resourceloca-tor array of get parameter array of post parameter analytics key of currentwebspace

Test it

To test your error pages you can use following routes:

{portal-prefix}/_error/{statusCode}

Note: If you are not sure about your portal configuration you can get the routes with this app/webconsole router:debug| grep _error command

Examples:

sulu.lo/ch._twig_error_test ANY ANY sulu.lo /ch/_error/{code}.{_format}sulu.lo/en._twig_error_test ANY ANY sulu.lo /en/_error/{code}.{_format}sulu.lo/fr._twig_error_test ANY ANY sulu.lo /fr/_error/{code}.{_format}sulu.lo/de._twig_error_test ANY ANY sulu.lo /de/_error/{code}.{_format}sulu.lo._twig_error_test ANY ANY sulu.lo /_error/{code}.{_format}

1.2.7 Optimize for production usage

If you want to use Sulu in production there are a few more optimizations you can do than just switching to the prodenvironment. The Symfony documentation already gives an introduction into deploying applications. Since Sulu isalso a Symfony application all these tips also apply to deploying Sulu.

This cookbook entry will show even more ways to optimize the performance of Sulu in a production environment.

1.2. Cookbook 67

Page 72: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Enable doctrine caches

The Symfony documentation already describes how to activate caching for the metadata, queries and results in itsDoctrineBundle documentation.

If you have APC installed and want to enable caching using APC you can just uncomment the following lines inapp/config/admin/config_prod.yml and app/config/website/config_prod.yml:

doctrine:orm:

metadata_cache_driver: apcresult_cache_driver: apcquery_cache_driver: apc

In case you want to use other caching providers you should have a look at the DoctrineBundle documentation, wherethe configuration of other providers is explained.

1.2.8 Integrating custom filters

When you want to enable custom filters for your bundle you have to follow the following steps. It’s important toknow that the filter component works with contexts. This means for example that the lists of contacts has the contactscontext and everything concerning the filters for the list will need this context. It should therefore be unique.

Add the missing data types to the field descriptors

The custom filter feature uses the field descriptors you’ve already defined for your lists. To work as expected youshould define the type of each column. If not defined the filter component will assume it’s a string. The available datatypes are:

• string

• number / integer / float

• date / datetime

• boolean

Add a context and configuration for new filters

Add the filter configuration to e.g. app/config/admin/config.yml. The first parameter below contexts is the contextmentioned above. The fields parameter defines the url where the fields api can be found. In the features the filter hasto be enabled.

sulu_resource:contexts:

contacts:fields: "/admin/api/contacts/fields"features:

- "filters"

Extend the js configuration in your bundle

Extend the js configuration in your bundle with a config value for the bread- crumb and the route back to the list. Thelast part of the setting key is the context the filters component will use.

68 Chapter 1. What’s in our documentation?

Page 73: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Config.set('suluresource.filters.type.contacts', {breadCrumb: [

{title: 'navigation.contacts'},{title: 'contact.contacts.title', link: 'contacts/contacts'}

],routeToList: 'contacts/contacts'

});

Extend the list and toolbar initialization for the lists

The toolbar should have at least two groups and one of them should have the id 2 because the filter button will beadded in the one with id 2. Two additional parameters have to added at the end. The first one is the instance name ofthe datagrid and the second one is the selector for the container where the result of the filter (x entries match filter y)will be displayed. Therefore a div above the filter div should be added in the html.

this.sandbox.sulu.initListToolbarAndList.call(this, 'contacts', '/admin/api/→˓contacts/fields',

{el: this.$find('#list-toolbar-container'),instanceName: 'contacts',inHeader: true,groups: [

{id: 1,align: 'left'

},{

id: 2,align: 'right'

}]

},{

el: this.sandbox.dom.find('#people-list', this.$el),url: '/admin/api/contacts?flat=true',searchInstanceName: 'contacts',searchFields: ['fullName'],resultKey: 'contacts',instanceName: 'contacts',viewOptions: {

table: {icons: [

{icon: 'pencil',column: 'firstName',align: 'left',callback: function(id) {

this.sandbox.emit('sulu.contacts.contacts.load', id);}.bind(this)

}],highlightSelected: true,fullWidth: true

}}

},(continues on next page)

1.2. Cookbook 69

Page 74: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

'contacts','#people-list-info'

);

<div id="people-list-info"></div><div id="people-list"></div>

1.2.9 Extend Entities

Sulu has a very easy way to extend and replace the internal entities. This feature is not implemented for each entitybut it will be implemented for all soon.

These entities are ready to extend:

• User

• Role

• Contact

• Media

You can extend all of them in the same way. Therefor we explain it for User here.

Create a Entity

Create your own Entity for example in the ClientWebsiteBundle. You can use the doctrine:generate:entity commandfor that. Extend the generated Entity with the Sulu User class.

<?php

namespace Client\Bundle\WebsiteBundle\Entity;

use Doctrine\ORM\Mapping as ORM;use Sulu\Bundle\SecurityBundle\Entity\User as SuluUser;

/*** User

** @ORM\Table(name="se_users")

* @ORM\Entity

*/class User extends SuluUser{

/*** @var string

** @ORM\Column(name="myProperty", type="string", length=255, nullable = true)

*/private $myProperty;

/*** Set myProperty

** @param string $myProperty

(continues on next page)

70 Chapter 1. What’s in our documentation?

Page 75: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

* @return User

*/public function setMyProperty($myProperty){

$this->myProperty = $myProperty;

return $this;}

/*** Get myProperty

** @return string

*/public function getMyProperty(){

return $this->myProperty;}

}

Warning: Your Entity can have own properties, but they should have at least default values. Otherwise the normalfeatures of Sulu could crash (like the sulu:security:user:create command).

Configuration

You can specify your new Entity and if it exists your Repository in the sulu_security configuration section in the fileapp/config/config.yml.

sulu_security:objects:

user:model: Client\Bundle\WebsiteBundle\Entity\Userrepository: Sulu\Bundle\SecurityBundle\Entity\UserRepository

For the Role entity:

sulu_security:objects:

role:model: Sulu\Bundle\SecurityBundle\Entity\Rolerepository: Sulu\Bundle\SecurityBundle\Entity\RoleRepository

For the Contact entity:

sulu_contact:objects:

contact:model: Sulu\Bundle\ContactBundle\Entity\Contactrepository: Sulu\Bundle\ContactBundle\Entity\ContactRepository

For the Media entity:

1.2. Cookbook 71

Page 76: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

sulu_media:objects:

media:model: Sulu\Bundle\MediaBundle\Entity\Mediarepository: Sulu\Bundle\MediaBundle\Entity\MediaRepository

Warning: If you override the entities you lose your old tables and data. You should provide a upgrade script.

1.2.10 How to implement an entity extensible?

Sulu uses the PersistenceBundle to provide an easy way to replace or extend entities. In this tutorial we will implementour own extensible book entity.

1. Entity

We will start with the entity itself. Our extensible entity builds up upon two classes:

BookInterface

(Sulu\Bundle\BookBundle\Entity\BookInterface, Interface)

Defines the Interface of our entity and is used as type for variables of the entity. Every entity which extends or replacesour book entity must implement this interface to ensure compatibility with the rest of the system.

Book

(Sulu\Bundle\BookBundle\Entity\Book, implements BookInterface)

Implements the book entity and is the base class for extending the entity. This class is our default entity implementationand is mapped as mapped-superclass in Book.orm.xml.

Note: To ensure full exchangeability, it is mandatory to use BookInterface as type of every variable, doctrine rela-tionship and other usage of our book entity.

2. Repository

Additionally to our entity classes, we need two repository classes to handle our entities:

BookRepositoryInterface

(Sulu\Bundle\BookBundle\Entity\BookRepositoryInterface, Interface, extendsRepositoryInterface)

Defines the Interface of our repository and is used as type for variables of the BookRepository. An interface for anextensible entity extends the RepositoryInterface of the PersistenceBundle.

72 Chapter 1. What’s in our documentation?

Page 77: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

The RepositoryInterface defines a createNew() method which must be used to create new instances of an entity insteadof the constructor. It is necessary to use the method of the repository for instance creation, to avoid creating instancesof wrong entity implementations when the entity implementation is changed.

BookRepository

(Sulu\Bundle\BookBundle\Entity\BookRepository, implementsBookRepositoryInterface, optionally extends EntityRepository)

Implements the concrete repository for our entity. It is recommended that this class extends the EntityRepository classof the PersistenceBundle, which implements a dynamic createNew() method and will always return a new instance ofthe currently configured entity implementation.

Note: To ensure full exchangeability, it is mandatory to use BookRepositoryInterface as type of every variable whichholds an instance of our BookRepository.

3. Configuration

Finally we need to adjust three configuration files to register our entity as extensible.

After configuration, the PersistenceBundle will automatically set the following parameters/services to our container:

• sulu.model.book.class: currently set entity implementation (Parameter)

• sulu.repository.book: currently set repository implementation (Service)

DependencyInjection/Configuration.php

In the Configuration.php file we set our default entity and repository implementation. Theses implementations areused if no other bundle replaces or extends our entity. We implemented the class Book as our default entity and theclass BookRepository as our default repository, therefore our configuration looks something like the following codeblock.

<?phpclass Configuration implements ConfigurationInterface{

public function getConfigTreeBuilder(){

$treeBuilder = new TreeBuilder();$rootNode = $treeBuilder->root('sulu_book')

(...)->children()

->arrayNode('objects')->addDefaultsIfNotSet()->children()

->arrayNode('book')->addDefaultsIfNotSet()->children()

->scalarNode('model')->defaultValue(→˓'Sulu\Bundle\BookBundle\Entity\Book')->end()

->scalarNode('repository')->defaultValue(→˓'Sulu\Bundle\BookBundle\Entity\BookRepository')->end()

->end()

(continues on next page)

1.2. Cookbook 73

Page 78: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

->end()->end()

->end()->end();

return $treeBuilder;}(...)

}

This results in the configuration path sulu_book.objects.book.model for the model class andsulu_book.objects.book.repository for the repository class. These paths can be used to overwrite the used im-plementations.

DependencyInjection/SuluBookExtension.php

In the SuluBookExtension.php file we need to read the set configuration and define and map the respective servicesto the container. We can use the already implemented configurePersistence() method of the PersistenceExtensionTraitclass to do this. Therefore our SuluBookExtension.php will look something like this:

<?phpclass SuluBookExtension extends Extension{

use PersistenceExtensionTrait;

public function load(array $configs, ContainerBuilder $container){

$configuration = new Configuration();(...)$this->configurePersistence($config['objects'], $container);

}(...)

}

SuluBookBundle.php

In the SuluBookBundle.php file we need to add a compiler pass to automatically resolve our interface to the cur-rently set entity implementation. To do this, we can use the already implemented buildPersistence() method of thePersistenceBundleTrait class. After this our SuluBookBundle.php will look something like this:

<?phpclass SuluBookBundle extends Bundle{

use PersistenceBundleTrait;

public function build(ContainerBuilder $container){

(...)$this->buildPersistence(

['Sulu\Bundle\BookBundle\Entity\BookInterface' => 'sulu.model.book.

→˓class',],

(continues on next page)

74 Chapter 1. What’s in our documentation?

Page 79: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

$container);

}(...)

}

1.2.11 Using Elasticsearch

By default Sulu uses Zend Lucene for its search in the administration and on the website. But since Sulu uses theMassiveSearchBundle it supports the same range of different search implementations, including Elasticsearch.

The MassiveSearchBundle Documentation describes how to setup Elasticsearch, the steps explained there also workin combination with Sulu.

1.2.12 DataProvider for SmartContent

DataProviders are used to load data for SmartContent. It returns data filtered by a configuration array. This array canbe configured with an overlay in the backend form.

This configuration array includes following values:

Name DescriptiondataSource Additional constraint - like page-“folder”.tags Multiple selection of tags, which a item should have.tagOperator The item has any or all of the selected tags.categories Multiple selection of categories, which a item should have.categoryOperator The item has any or all of the selected categories.

Tags (websiteTags) and Categories (websiteCategories) can also be “injected” by GET parameters from the website.This can be handled separately from the admin-selected. Also different operators (websiteTagsOperator and website-CategoryOperator) are available.

Additional features, which can be provided with a DataProvider:

Name DescriptionpresentAs Value can be used in the website for display options - like one or two column - these values can be

freely configured by developers.page & pa-geSize

Pagination of items.

limit Maximum items for (if pagination is active) over all pages or overall.

How to create a custom DataProvider?

To create a custom data provider you simply have to create a service which implements the Interface DataProvider-Interface. This Interface provides functions to resolve the configured filters for the backend API with standardizedobjects and for the website with array and entity access. Additionally the DataProvider returns a configuration objectto enable or disable features.

There exists an abstraction layer for ORM DataProviders. This layer provides the implementation of basic Dat-aProvider functionality and Database query.

If you want to create a DataProvider for the ExampleEntity you have todo following steps.

1.2. Cookbook 75

Page 80: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1. Repository

The repository has to implement the DataProviderRepositoryInterface and provide the findByFilters function. If thedefault implementation is good enough, you can include the trait DataProviderRepositoryTrait, which, needs the func-tions createQueryBuilder (is default in repositories) and appendJoins where you are able to configure eager loadingfor the entity.

The rest of the functionality and Query generation is done in the Trait.

<?php

use Sulu\Component\SmartContent\Orm\DataProviderRepositoryInterface;use Sulu\Component\SmartContent\Orm\DataProviderRepositoryTrait;

/*** Repository for the ExampleEntities.

*/class ExampleRepository extends EntityRepository implements→˓DataProviderRepositoryInterface{

use DataProviderRepositoryTrait;

/*** {@inheritdoc}

*/public function appendJoins(QueryBuilder $queryBuilder, $alias, $locale){

$queryBuilder->addSelect('type')->leftJoin($alias . '.type', 'type');}

}

Note: Be sure that the returned entities has valid serialization configuration for JMSSerializer.

There are following hooks to influence the query generation. These are functions which are optional to override in therepository.

Name Descriptionappend(QueryBuilder $query-Builder, $alias, $locale, $options =[])

Additional select, where or joins can be added to the query to match givenoptions. The options can be generated by the data-provider and can containfor example additional filter parameter.

appendTagsRelation(QueryBuilder$queryBuilder, $alias)

If your entity is not directly connected to the tags (entity.tags) you can ap-pend here all needed joins and return the path to the tag relation.

appendCategoriesRela-tion(QueryBuilder $queryBuilder,$alias)

Same as tags.

appendDatasource($datasource,$includeSubFolders, QueryBuilder$queryBuilder, $alias)

If your dataprovider can handle datasources you can add the functionalityto filter by the datasource here.

2. DataItem

The DataItem will be used in the backend to display the filtered items. This class implements the Interface ItemInter-face.

76 Chapter 1. What’s in our documentation?

Page 81: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<?php

use Sulu\Component\SmartContent\ItemInterface;

/*** Represents example item in example data provider.

** @ExclusionPolicy("all")

*/class ExampleDataItem implements ItemInterface{

/*** @var Example

*/private $entity;

public function __construct(Example $entity){

$this->entity = $entity;}

/*** {@inheritdoc}

** @VirtualProperty

*/public function getId(){

return $this->entity->getId();}

/*** {@inheritdoc}

** @VirtualProperty

*/public function getTitle(){

return $this->entity->getTitle();}

/*** {@inheritdoc}

** @VirtualProperty

*/public function getImage(){

return $this->entity->getImage();}

/*** {@inheritdoc}

*/public function getResource(){

return $this->entity;

(continues on next page)

1.2. Cookbook 77

Page 82: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

}}

Note: If you return an image within the getImage function it will be displayed in the admin ui. You should be surethat the image is not bigger that 50x50.

3. DataProvider

Also the DataProvider is mostly abstracted by the SmartContent Component. The optimize in the configuration youcan disable or enable the form-elements to avoid filtering for that values.

<?php

use JMS\Serializer\SerializerInterface;use Sulu\Component\SmartContent\Orm\BaseDataProvider;use Sulu\Component\SmartContent\Orm\DataProviderRepositoryInterface;use Symfony\Component\HttpFoundation\RequestStack;use Sulu\Component\SmartContent\ItemInterface;

/*** Example DataProvider for SmartContent.

*/class ExampleDataProvider extends BaseDataProvider{

/*** @var RequestStack

*/private $requestStack;

public function __construct(DataProviderRepositoryInterface $repository,→˓SerializerInterface $serializer, RequestStack $requestStack)

{parent::__construct($repository, $serializer);

$this->requestStack = $requestStack;

$this->configuration = self::createConfigurationBuilder()->enableTags()->enableLimit()->enablePagination()->enablePresentAs()->setDeepLink('examples/example/{webspace}/{locale}/{id}')->getConfiguration();

}

/*** Decorates result as data item.

** @param array $data

** @return ItemInterface[]

*/protected function decorateDataItems(array $data)

(continues on next page)

78 Chapter 1. What’s in our documentation?

Page 83: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

{return array_map(

function ($item) {return new ExampleDataItem($item);

},$data

);}

/*** Returns additional options for query creation.

** @param PropertyParameter[] $propertyParameter

* @param array $options

** @return array

*/protected function getOptions(array $propertyParameter, array $options = []) {

$request = $this->requestStack->getCurrentRequest();

$result = ['type' => $request->get('type'),

];

return array_filter($result);}

}

Note: The deep-link will be used to generate a the link to the Sulu-Admin form of a single item, when the user clickon it.

4. Service Definition

Define a service with your Repository and DataProvider and add the tag sulu.smart_content.data_provider with a aliasto your DataProvider service definition.

<service id="sulu_example.example_repository" class=→˓"Sulu\Bundle\ExampleBundle\Entity\ExampleRepository"

factory-method="getRepository" factory-service="doctrine"><argument>%sulu_example.example.entity%</argument>

</service>

<service id="sulu_example.smart_content.data_provider.example" class=→˓"Sulu\Bundle\ExampleBundle\SmartContent\ExampleDataProvider">

<argument type="service" id="sulu_example.example_repository"/><argument type="service" id="serializer"/><argument type="service" id="request_stack"/>

<tag name="sulu.smart_content.data_provider" alias="example"/></service>

Afterwards you can use your new DataProvider within a normal SmartContent property.

1.2. Cookbook 79

Page 84: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Note: Mind that the class property should set to a sensible value, but it has no influence in the actual result (see theFactory service documentation of Symfony for more details). So it is very important to set the repository class correctin the doctrine metadata for this to work.

How to create a custom Datasource component?

A Datasource component is a simple aura-component which returns some data. These data can be used in the Dat-aProviderRepository::appendDatasource method.

For example returns the Content-DataProvider the UUID of the page which should be used as the parent of result set.

The following example is a simple (and not complete) example of a datasource component. If you need a full exampleplease take a look at the components media-datasource@sulumedia or content-datasource@sulucontent.

define(function() {

'use strict';

var defaults = {options: {

url: null,resultKey: null,selected: null,selectCallback: function(item) {}

},templates: {

skeleton: '' // TODO html skeleton to render component}

},

/*** namespace for events

* @type {string}

*/eventNamespace = 'smart-content.datasource.';

return {

defaults: defaults,

events: {names: {

setSelected: {postFix: 'set-selected',type: 'on'

}},namespace: eventNamespace

},

/*** Initialize component

*/initialize: function() {

(continues on next page)

80 Chapter 1. What’s in our documentation?

Page 85: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

// merge options with defaultsthis.options = this.sandbox.util.extend(true, {}, defaults, this.options);

// current selected datasourcethis.selected = this.options.selected;

// render skeleton and start subcomponentsthis.render();

},

/*** Bind events to call select callback

*/bindCustomEvents: function() {

// setter for selectedthis.events.setSelected(this.setSelected.bind(this));

},

/*** Set new selected and update UI.

** @param {String} selected can also be null.

*/setSelected: function(selected) {

this.selected = selected;

// TODO update component with new selected datasource},

/*** These function should be called to propagate the result to smart-content

→˓component.

** @param {String} selected can also be null.

*/emitSelected: function(item) {

this.selected = item.id; // identifier of itemthis.options.selectCallback(

this.selected, // will be saved and used to generate the queryitem.path // will be displayed on the first slide

);},

/*** Render container for column-navigation

*/render: function() {

this.$container = this.sandbox.dom.createElement(this.templates.skeleton()

);this.html(this.$container);

}};

});

To activate these datasource-component it has to be enabled in the DataProvider.

1.2. Cookbook 81

Page 86: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<?php

public function __construct(DataProviderRepositoryInterface $repository,→˓SerializerInterface $serializer){

parent::__construct($repository, $serializer);

$this->configuration = self::createConfigurationBuilder()->enableTags()->enableLimit()->enablePagination()->enablePresentAs()->enableDatasource(

'example@suludoc',[

'url' => '/admin/api/example','resultKey' => 'examples',

])->getConfiguration();

}

The component name and options will be used to initialize the component. Therefor you can use the url wilthis.options.url.

1.2.13 System-Collections

System-Collections are special collections which are not editable, deletable or movable. Apart from that they can beused like all other collections.

The System takes care of creating and upgrading them. Each bundle can request them, to save there images like avataror logos in the contact section.

Because of the usage of the configuration tree also the App itself can register system collection and use them.

sulu_media:system_collections:

# Prototypekey:

meta_title: []collections:

# Prototypekey:

meta_title: []

This structure will be used to create a Collection Structure like this:

System|--> Sulu contact| |--> People| |--> Organizations|--> Client Website

|--> My own System-Collection

To register own System-Collections you can prepend the configuration with your bundle extension:

82 Chapter 1. What’s in our documentation?

Page 87: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<?php

class ClientWebsiteExtension extends Extension implements PrependExtensionInterface{

/*** {@inheritdoc}

*/public function prepend(ContainerBuilder $container){

if ($container->hasExtension('sulu_media')) {$container->prependExtensionConfig(

'sulu_media',[

'system_collections' => ['client_website' => [

'meta_title' => ['en' => 'Client website', 'de' =>→˓'Client Website'],

'collections' => ['uploads' => [

'meta_title' => ['en' => 'My own System-Collection→˓', 'de' => 'Meine eigene System-Collection'],

],],

],],

]);

}}

/*** {@inheritdoc}

*/public function load(array $configs, ContainerBuilder $container){

...}

}

To use this new Collection you can use the sulu_media.system_collections.manager service.

<?php

// to get id of system collection$systemCollectionManager->getSystemCollection('client_website.uploads');

// to determine if id is a system collection (e.g. validation)$systemCollectionManager->isSystemCollection(1);

Note: The key of the system-collection consists of namespace.key. In this case namespace = client_website and key= uploads.

1.2. Cookbook 83

Page 88: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.2.14 Adding new Webspace

To create a new webspace you have to create a new file within the app/Resources/webspaces directory. The content ofthe file should be quite similar to the sulu_io.xml.dist file in this folder.

Note: The key of the webspace has to be the same as the filename without the xml extension.

To activate the webspace within sulu with a prod or stage environment you have to clear the cache with the command:

$ php bin/adminconsole cache:clear -e <environment>

Afterwards you will need to initialize the new webspace, to do so run the following command:

$ php bin/adminconsole sulu:document:initialize

To allow users to see the new webspace you also have to set the permissions in the roles for the new webspace.

After this few steps you are able to administrate and view your new webspace.

If you have any error you can use the following command to validate your webspace:

$ php bin/adminconsole sulu:content:validate:webspaces

1.2.15 How to manage analytics?

Sulu gives the content-manger an easy way to manage analytic-codes and appends them automatically to the websiteoutput without any changes in the twig-template. You can find the list of analytics under the webspace-settings.

The analytics consist of:

Title To identify it.Domains On which domain this analytics should be appended.All Domains Should it appended to all domains.Type The type (google, google_tag_manager, piwik, custom).Content The code or key of the analytic.

Sulu can handle different types of analytic-systems like google or piwik. This codes will be automatically added withthe given key and site-id (for piwik). To add other systems simply choose type custom and copy and paste the codeinto the textarea.

Warning: Be aware that custom analytics will not be evaluated and appended without validation - therefor itcould break the website directly after saving.

Override analytics template

You are able to override the analytics template with the symfony template overriding mechanism.

There are three relevant templates:

• SuluWebsiteBundle:Analytics/type/google.html.twig

• SuluWebsiteBundle:Analytics/type/google_tag_manager.html.twig

• SuluWebsiteBundle:Analytics/type/piwik.html.twig

• SuluWebsiteBundle:Analytics/type/custom.html.twig

84 Chapter 1. What’s in our documentation?

Page 89: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

You can access the following information in the twig variable analytics.

Name Type Descriptionid int A unique identifier of the analytics.title string The title of the analytics.allDomains boolean Indicates whether the analytics is on all domains of specific.content mixed Differs for the type.type string google / google_tag_manager / piwik / customdomains array Array of associated domains.

Note: The content property contains for type google / google_tag_manager the key, for piwik an associated arrayof url and siteId and for the custom type the whole script (except the <script> tag).

1.2.16 How to change the default locale provider?

Perhaps we should explain the purpose of a DefaultLocaleProvider first. A DefaultLocaleProvideris used to determine the locale of a request if the request does not already contain the information. E.g. if you openhttp://sulu.io Sulu doesn’t know which localization should be displayed, because an english and a german version ofthe homepage is available. In this case the DefaultLocaleProvider is called to provide a default locale whichis used to redirect to http://sulu.io/en.

Currently two providers are available. One makes use of the portal default localization configuration. The other triesto find the best matching locale based on the preferred language of the HTTP request.

You can provide an own DefaultLocaleProvider which has to implement theDefaultLocaleProviderInterface.

Available default locale providers:

Service ID Descriptionsulu_website.default_locale.portal_providersulu_website.default_locale.request_provider

Use portal default localization configuration Use pre-ferred language of the HTTP request

Configuration

Change the default locale provider service ID to the provider which fulfills your needs.

sulu_website:default_locale:

provider_service_id: sulu_website.default_locale.request_provider

1.2.17 How to define a default-snippet?

Sulu gives the content-manager an easy way to define default snippets per type or area. These default-snippets will beused if the snippet-selection is empty on a specific page. This default selection will not be displayed in the form andcan have a big impact on the page.

1.2. Cookbook 85

Page 90: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Defining areas

Sulu allows you to to reuse the same snippet template to use it as default for different snippet content types or load asnippet by its area key with the twig extension. The areas can be defined in the xml template of the snippets.

<key>sidebar</key>

<meta><title lang="en">Sidebar</title><title lang="de">Sidebar</title>

</meta>

<areas><area key="sidebar_default">

<meta><title lang="en">Sidebar Standard</title><title lang="de">Sidebar Default</title>

</meta></area>

<area key="sidebar_overview"><meta>

<title lang="en">Sidebar Overview</title><title lang="de">Sidebar Übersicht</title>

</meta></area>

</areas>

Note: When no areas are defined sulu automatically define an area per snippet template.

Configuration

The default snippets will be used if the following conditions are fulfilled.

1. The feature has to be activated (sulu_snippet.types.snippet.default_enabled) (default activated since 1.4)

2. The snippet-selection parameter default has to be defined. See Snippet.

3. The snippet for the configured area has to be selected in the webspace settings.

This conditions match the default snippet will be injected into the page.

Usage without the content type

To get the snippet for a specific area the developer can use the twig-function sulu_snippet_load_by_area.

1.2.18 How to manage custom-urls?

Sulu gives the content-manger an easy way to manage custom-urls or landing pages. These urls can be defined inthe webspace-settings. A custom-url is basically an additional url for a specific page. This url can have a completelydifferent schema as the other urls which will be generated by the system. For example the localization is not presentin this urls.

A custom-url consists of following properties:

86 Chapter 1. What’s in our documentation?

Page 91: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Parameter DescriptionTitle This title will be displayed in the list and should identify the use case of this url.Activated This flag indicates if the custom-url is shown on the website.Base Domain The content-manager can choose one of the configured base-domains.Custom-Url After choosing the base-domain the * can be replaced with any value.Target page and local-ization

The selected page will be displayed in the selected localization on this custom-url.

Redirect The custom URL will be redirected to URL of the the linked page.Canonical URL When the Canonical URL has been activated only the linked page will be indexed by

search engines.No Index The page will not be indexed by search engines.No Follow The links on the page will not be followed by search engines.

Note: If the target page gets deleted the custom-url will stay in the list, will be marked that the target is missing andreturns a 404 on the website.

Configuration

The base-domains (which can be selected) will be configured in the webspace.xml of the environment.

<webspace>...

<portals><portal>

...

<environments>...

<environment type="dev"><urls>

<url>sulu.lo/{localization}</url></urls><custom-urls>

<custom-url>sulu.lo/*</custom-url><custom-url>*.sulu.lo</custom-url><custom-url>*.sulu.lo/*</custom-url>

</custom-urls></environment>

</environments></portal>

</portals></webspace>

It is possible to set a wildcard (*) for subdomains and folders, which can be replaced by arbitrary values when creatinga custom-url.

Note: Be aware of that different sub-domains or different domains has to be configured on the webserver. Veryimportant is that you configure a wildcard for the subdomain if you will use it for custom-urls.

1.2. Cookbook 87

Page 92: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.2.19 How to configure Websocket?

Sulu comes with a sulu-wide websocket application. This application is optional but comes with a few feature whichcan only be used if the websocket is running.

For example the real-time notification for CollaborationBundle can only be displayed when the websocket is running.

Configuration

If you use the config layout of sulu-standard you only have to set following parameters in the parameters.ymlfile.

websocket_url The url on which the websocket should listen.websocket_port The port on which the websocket should listen.

If you have multiple installations of sulu on a single server be sure that you use unique ports per installation.

If you have your own application, you can enable the websocket and configure its url and port by placing the followinglines into your configuration:

sulu_websocket:enabled: trueserver:

http_host: "%websocket_url%"port: "%websocket_port%

Execution

To execute the websocket you have to run the command app/console sulu:websocket:run -e prod.This command starts the server and listens to the configured port.

We recommend to run the websocket with supervisor. You can use the example configuration for supervisord.

/etc/supervisor/conf.d/sulu_websocket.conf

[program:sulu_websocket]command=/var/www/sulu.io/app/console sulu:websocket:run --env prodprocess_name=sulu_websocketuser=www-datanumprocs=1directory=/var/www/sulu.ioautostart=trueautorestart=truestderr_logfile=/var/log/supervisor/sulu_websocket.logstdout_logfile=/var/log/supervisor/sulu_websocket.logstdout_logfile_maxbytes=10MB

1.2.20 How to include live-preview in my template?

Sulu provides a powerful live-preview system which can be used by every website theme. But as a prerequisite thetemplate has to be adapted a little bit.

88 Chapter 1. What’s in our documentation?

Page 93: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Quick Example

The preview-system relies on the schema definition from RDFA. This standard allows you to add information aboutthe content in the html structure without maintaining another file.

The following example is a very basic template implementation which is able to be updated by sulu.

template.html.twig

{% extends "::master.html.twig" %}

{% block content %}<div id="content" vocab="http://schema.org/" typeof="Content">

<h1 property="title">{{ content.title }}</h1></div>

{% endblock %}

master.html.twig

<!DOCTYPE html><html>

<head><title>{{ content.title }}</title>

</head><body>

{% block content %}{% endblock %}</body>

</html>

page.xml

<?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd">

<key>page</key>

<view>ClientWebsiteBundle:templates:default</view><controller>SuluWebsiteBundle:Default:index</controller><cacheLifetime>2400</cacheLifetime>

<properties><section name="highlight">

<properties><property name="title" type="text_line" mandatory="true">

<params><param name="headline" value="true"/>

</params>

<tag name="sulu.rlp.part"/></property>

<property name="url" type="resource_locator" mandatory="true"><tag name="sulu.rlp"/>

</property></properties>

(continues on next page)

1.2. Cookbook 89

Page 94: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

</section></properties>

</template>

This page consists of a single title and a url. The template is quite basic but includes the RDFa attribute prop-erty=”title” to tell sulu that the title is rendered in this container.

When the title of the page will be updated in the content form the admin will send this change to the server wherethe twig block content will be rendered. The server response contains only the part which was changed (indicatedby property=”title”). Back in the javascript application the preview on the right will update the container which ismarked with the particular property.

This process ensures a quite good performance (as good as the rendering on the server side) and minimizes the load ofthe requests.

Supported RDFa features

To see a full example of the syntax take a look in the example.html.twig <https://github.com/sulu/sulu-standard/blob/develop/src/Client/Bundle/WebsiteBundle/Resources/themes/default/templates/example.html.twig> file.

Links

For links you can use the href-attribute which will also be updated if the mentioned property was changed.

<a href="{{ sulu_content_path(content.link.url) }}" property="link">{{ content.link.title }}

</a>

Images

To update an image you can simply use the src-attribute when the mentioned property has changed

<img src="{{ image.thumbnails['170x170'] }}" alt="{{ image.title }}"/>

Multiple values

For multiple values simply use table, ul, ol or div tags and the content will be updated if the mentioned property waschanged.

<ul property="categories">{% for category in content.categories %}

<li>{{ category.name }}</li>{% endfor %}

</ul>

Snippets

For the snippet content-type all the selected snippets has to be updated. Therefore you only have to set the upperproperty.

90 Chapter 1. What’s in our documentation?

Page 95: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<div property="snippets">{% for snippet in content.snippets %}

<h2>{{ snippet.title }}</h2>{% endfor %}

</div>

Smart-Content

Smart-Content will be handled like other properties. You only have to define the property name (in this examplesimilar_pages).

<ul property="similar_pages">{% for link in content.similar_pages %}

<li><a href="{{ sulu_content_path(link.url) }}">

{{ link.title|default('No Title') }}</a>

</li>{% endfor %}</ul>

Single-Internal-Link

For rendering single-internal links you can use the MarkupBundle. This example will place the title of the page in thecontent of the anchor tag.

<sulu:link property="similar_pages" href="{{ content.singleInternalLink }}" title="My-→˓Title"/>

Blocks

Blocks are the only property-type which needs a different syntax beside the property attribute.

<div property="blocks" typeof="collection">{% for block in content.blocks %}

<div rel="blocks" typeof="block"><div property="title">{{ block.title }}</div>

</div>{% endfor %}

</div>

You have to define the property=”blocks” as typeof=”collection” and each item of the block as typeof=”block” andset the relation to the parent property, in this case “blocks”.

With these definitions the system is able to update only the title of the first block item and doesn’t have to return theentire container of the block-property in the response.

1.2.21 How to deactivate the RequestAnalyzer?

The RequestAnalyzer has the very important task of recognizing e.g. at which webspace and locale the currentrequest is targeted. It also recognizes if the current request is not valid based on some rules, e.g. if there is no webspace

1.2. Cookbook 91

Page 96: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

available at the requested URL. In this case the RequestAnalyzer throws an exception, which makes it quite easyto find about errors in your webspace configuration.

However, this behavior might be disturbing for requests in which you are fully aware that there is no webspace availableand you also do not need one. For these special requests the RequestAnalyzer can be easily turned off.

This is achieved using the request attributes from Symfony. Sulu scans this property for field called_requestAnalyzer, and avoids calling it when this attribute is set to false. The easiest way to achieve this isusing the routing configuration file, which might look something like this:

sulu_example.route:path: /some-urldefaults:

_controller: SuluExampleBundle:Controller:index_requestAnalyzer: false

1.2.22 How to use the RequestAnalyzer with ESI requests?

The symfony documentation already describes how to use edge side includes to cache parts of pages with differentlife times. However, if you are using the render_esi function in combination with the controller function asshown in the following code, you might encounter issues:

{{ render_esi(controller('AppBundle:News:latest', { 'maxPerPage': 5 })) }}

This will probably work for most controllers, but if the latestAction of the NewsController makes use ofthe RequestAnalyzer it might fail, because the RequestAnalyzer can’t analyze the request. This is causedby the fact that Symfony generates a special URL for this render_esi call.

The solution to this problem is to also pass the portal and the locale (if the rendered content should not have the samelocale as the rest of the page) to the options:

{{ render_esi(controller('AppBundle:News:latest', { 'maxPerPage': 5, _portal: request.→˓portalKey, _locale: request.locale })) }}

1.2.23 Server Configuration

The preferred way to develop your Sulu application is to use PHP’s built-in web server.

In a production system, you will run Sulu on a web server such as Apache 2 or Nginx. Read the following articles tolearn more about the configuration of these tools.

Apache 2.2

The Apache HTTP Server is the world’s most used web server (according to Wikipedia). It is open source, mature andreliable.

To serve a Sulu website with Apache, you need to adapt your hosts file, create a virtual host configuration file andrestart your server.

Host Name Configuration

Add the domain of your site to the hosts file. Depending on your operating system, this file can be found in differentplaces:

92 Chapter 1. What’s in our documentation?

Page 97: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• Unix: /etc/hosts

• Windows: %SystemRoot%\System32\drivers\etc\hosts

On a development machine, we could use the domain sulu.lo (“lo” stands for “local”). Add that domain to the end ofthe hosts file:

# ...

127.0.0.1 sulu.lo

When you type the URL http://sulu.lo in your browser, the browser will now load the page from your computer.

Note: You may need to restart your browser after changing the hosts file.

But before it works, we need to tell Apache what to do when that URL is loaded.

Virtual Host Configuration

Let’s add the Apache configuration file for the sulu.lo domain.

<VirtualHost *:80>DocumentRoot "/var/www/sulu.lo/web"ServerName sulu.lo<Directory "/var/www/sulu.lo/web">

Options Indexes FollowSymLinksAllowOverride AllOrder allow,denyAllow from allSetEnv SYMFONY_ENV dev

<IfModule mod_expires.c>ExpiresActive OnExpiresDefault "access plus 1 month"ExpiresByType image/gif "access plus 1 month"ExpiresByType image/png "access plus 1 month"ExpiresByType image/svg+xml "access plus 1 month"ExpiresByType image/jpeg "access plus 1 month"ExpiresByType image/jpg "access plus 1 month"ExpiresByType text/javascript "access plus 1 month"ExpiresByType text/css "access plus 1 month"ExpiresByType font/woff2 "access plus 1 month"ExpiresByType font/woff "access plus 1 month"ExpiresByType font/eot "access plus 1 month"ExpiresByType font/ttf "access plus 1 month"

</IfModule>

<IfModule mod_deflate.c>SetOutputFilter DEFLATESetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-varySetEnvIfNoCase Request_URI \.(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-

→˓varySetEnvIfNoCase Request_URI \.pdf$ no-gzip dont-vary

BrowserMatch ^Mozilla/4 gzip-only-text/htmlBrowserMatch ^Mozilla/4\.0[678] no-gzip

(continues on next page)

1.2. Cookbook 93

Page 98: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

BrowserMatch \bMSIE !no-gzip !gzip-only-text/html</IfModule>

</Directory></VirtualHost>

Note: It is a good practice to create a virtual host for each of your Sulu projects. They are separated by domain andyou’ll got full control on what you expose.

MAMP Pro

In general you should configure your vHost like the Apache 2.2 paragraph above describes it.

If you want to enable the dev-environment (including the debug toolbar) you have to be sure that the vHost envi-ronment variable (SetEnv SYMFONY_ENV dev) is set properly and you configured a DEV-domain within yourwebspace.xml.

94 Chapter 1. What’s in our documentation?

Page 99: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

File Permissions

Finally, we need to fix the permissions of our project so that the web server is able to read and write them.

This command is different for sulu-standard and sulu-minimal.

sulu-standard

Run the following commands on Linux:

HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' |→˓grep -v root | head -1 | cut -d\ -f1`sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs uploads→˓uploads/* web/uploads web/uploads/* app/datasudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs uploads→˓uploads/* web/uploads web/uploads/* app/data

Or these commands for Mac OSX:

HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' |→˓grep -v root | head -1 | cut -d\ -f1`sudo chmod +a "$HTTPDUSER allow delete,write,append,file_inherit,directory_inherit"→˓app/cache app/logs uploads uploads/* web/uploads web/uploads/* app/datasudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" app/→˓cache app/logs uploads uploads/* web/uploads web/uploads/* app/data

Or these commands for Windows (with IIS web server):

$rule = New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList @(→˓"IUSR","FullControl","ObjectInherit, ContainerInherit","None","Allow")$folders = "app\cache", "app\logs", "app\data", "uploads", "uploads\*", "web\uploads",→˓ "web\uploads\*"foreach ($f in $folders) { $acl = Get-Acl $f; $acl.SetAccessRule($rule); Set-Acl $f→˓$acl; }

sulu-minimal

Run the following commands on Linux:

HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' |→˓grep -v root | head -1 | cut -d\ -f1`sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX var/cache var/logs var/→˓uploads var/uploads/* web/uploads web/uploads/* var/indexes var/sessionssudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX var/cache var/logs var/→˓uploads var/uploads/* web/uploads web/uploads/* var/indexes var/sessions

Or these commands for Mac OSX:

HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' |→˓grep -v root | head -1 | cut -d\ -f1`sudo chmod +a "$HTTPDUSER allow delete,write,append,file_inherit,directory_inherit"→˓var/cache var/logs var/uploads var/uploads/* web/uploads web/uploads/* var/indexes→˓var/sessionssudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" var/→˓cache var/logs var/uploads var/uploads/* web/uploads web/uploads/* var/indexes var/→˓sessions (continues on next page)

1.2. Cookbook 95

Page 100: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

Or these commands for Windows (with IIS web server):

$rule = New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList @(→˓"IUSR","FullControl","ObjectInherit, ContainerInherit","None","Allow")$folders = "var\cache", "var\logs", "var\indexes", "var\sessions", "var\uploads",→˓"var\uploads\*", "web\uploads", "web\uploads\*"foreach ($f in $folders) { $acl = Get-Acl $f; $acl.SetAccessRule($rule); Set-Acl $f→˓$acl; }

Nginx

The Nginx configuration could look something like.

server {listen 80;

server_name sulu.lo;root /var/www/sulu.lo/web;

error_log /var/log/nginx/sulu.lo.error.log;access_log /var/log/nginx/sulu.lo.at.access.log;

# strip app.php/ prefix if it is presentrewrite ^/app\.php/?(.*)$ /$1 permanent;

location /admin {index admin.php;try_files $uri @rewriteadmin;

}

location @rewriteadmin {rewrite ^(.*)$ /admin.php/$1 last;

}

location / {index website.php;try_files $uri @rewritewebsite;

}

# expirelocation ~* \.(?:ico|css|js|gif|jpe?g|png|svg|woff|woff2|eot|ttf)$ {

try_files $uri /website.php/$1?$query_string;access_log off;expires 30d;add_header Pragma public;add_header Cache-Control "public";

}

location @rewritewebsite {rewrite ^(.*)$ /website.php/$1 last;

}

# pass the PHP scripts to FastCGI server from upstream phpfcgi

(continues on next page)

96 Chapter 1. What’s in our documentation?

Page 101: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

location ~ ^/(website|admin|app|app_dev|config)\.php(/|$) {include fastcgi_params;fastcgi_pass unix:/var/run/php5-fpm.sock;fastcgi_buffers 16 16k;fastcgi_buffer_size 32k;fastcgi_split_path_info ^(.+\.php)(/.*)$;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;fastcgi_param SYMFONY_ENV dev;

}}

Warning: Be sure to also configure your local host-file, if running Sulu locally.

File Permissions

Finally, we need to fix the permissions of our project so that the web server is able to read and write them.

This command is different for sulu-standard and sulu-minimal.

sulu-standard

Run the following commands on Linux:

HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' |→˓grep -v root | head -1 | cut -d\ -f1`sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs uploads→˓uploads/* web/uploads web/uploads/* app/datasudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs uploads→˓uploads/* web/uploads web/uploads/* app/data

Or these commands for Mac OSX:

HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' |→˓grep -v root | head -1 | cut -d\ -f1`sudo chmod +a "$HTTPDUSER allow delete,write,append,file_inherit,directory_inherit"→˓app/cache app/logs uploads uploads/* web/uploads web/uploads/* app/datasudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" app/→˓cache app/logs uploads uploads/* web/uploads web/uploads/* app/data

Or these commands for Windows (with IIS web server):

$rule = New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList @(→˓"IUSR","FullControl","ObjectInherit, ContainerInherit","None","Allow")$folders = "app\cache", "app\logs", "app\data", "uploads", "uploads\*", "web\uploads",→˓ "web\uploads\*"foreach ($f in $folders) { $acl = Get-Acl $f; $acl.SetAccessRule($rule); Set-Acl $f→˓$acl; }

sulu-minimal

Run the following commands on Linux:

1.2. Cookbook 97

Page 102: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' |→˓grep -v root | head -1 | cut -d\ -f1`sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX var/cache var/logs var/→˓uploads var/uploads/* web/uploads web/uploads/* var/indexes var/sessionssudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX var/cache var/logs var/→˓uploads var/uploads/* web/uploads web/uploads/* var/indexes var/sessions

Or these commands for Mac OSX:

HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' |→˓grep -v root | head -1 | cut -d\ -f1`sudo chmod +a "$HTTPDUSER allow delete,write,append,file_inherit,directory_inherit"→˓var/cache var/logs var/uploads var/uploads/* web/uploads web/uploads/* var/indexes→˓var/sessionssudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" var/→˓cache var/logs var/uploads var/uploads/* web/uploads web/uploads/* var/indexes var/→˓sessions

Or these commands for Windows (with IIS web server):

$rule = New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList @(→˓"IUSR","FullControl","ObjectInherit, ContainerInherit","None","Allow")$folders = "var\cache", "var\logs", "var\indexes", "var\sessions", "var\uploads",→˓"var\uploads\*", "web\uploads", "web\uploads\*"foreach ($f in $folders) { $acl = Get-Acl $f; $acl.SetAccessRule($rule); Set-Acl $f→˓$acl; }

How to Use PHP’s built-in Web Server

PHP (>= 5.4) comes with a built-in web server. This server can be used to run your Sulu application during develop-ment. This way, you don’t have to bother configuring a full-featured web server such as Apache or Nginx.

Caution: The built-in web server is meant to be run in a controlled environment. It is not designed to be used onpublic networks.

The server can be started with the server:start command. You will have to start two different servers for theadministration and the website:

bin/adminconsole server:startbin/websiteconsole server:start

These commands will start two servers listening on http://127.0.0.1:8000 and http://127.0.0.1:8001 in the background.

You can change the IP and port of the web servers by passing them as argument:

bin/adminconsole server:start 192.168.0.1:8080bin/websiteconsole server:start 192.168.0.1:8081

The server can be stopped again with the server:stop command:

bin/adminconsole server:stopbin/websiteconsole server:stop

Read the Symfony documentation on the built-in web server to learn more about the different server:* commandsand options.

98 Chapter 1. What’s in our documentation?

Page 103: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.2.24 How to add localizations with the localization provider?

If you are creating a bundle that has its own localizations they should be registered in the Sulu system. Otherwise youwon’t be able to use Sulu’s security features, for example.

It’s possible to add locales by simple defining a service with the LocalizationProvider and pass your custom locales asarguments.

Example

<service id="sulu_product.localization_provider" class=→˓"Sulu\Component\Localization\Provider\LocalizationProvider">

<argument>%sulu_product.locales%</argument>

<tag name="sulu.localization_provider"/></service>

1.2.25 Provider for CKEditor Internal-Sulu-Link

LinkProvider are used to load data for the “Internal-Sulu-Link” Plugin for the CKEditor. It returns an array of LinkIteminstances, identified by ids which will be passed to the TeaserProviderInterface::preload function. This feature canbe used by the CKEditor Plugin “Sulu-Internal-Link” or by adding Markup to your twig-templates (see followingexample and the chapter MarkupBundle).

<sulu:link provider="page" href="123-123-123"/>

The LinkItem consists of the following properties:

• id

• title

• url

• published

Example

This example assumes that the entities will be selected by a datagrid. For this we already build an abstract implemen-tation which can be configured with the LinkConfiguration.

namespace AppBundle\Link;

use Sulu\Bundle\ContentBundle\Markup\Link\LinkConfiguration;use Sulu\Bundle\ContentBundle\Markup\Link\LinkItem;use Sulu\Bundle\ContentBundle\Markup\Link\LinkProviderInterface;

class LinkProvider implements LinkProviderInterface{

/*** {@inheritdoc}

*/public function getConfiguration(){

return new LinkConfiguration(

(continues on next page)

1.2. Cookbook 99

Page 104: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

'app.link','ckeditor/link/list@sulucontent',[

'url' => '...', // your api url to load list'hrefUrl' => '...<%=link.href%>', // your api url to load single item'idKey' => 'id','titleKey' => 'name','publishedExpression' => [new DisplayCondition('published',

→˓DisplayCondition::OPERATOR_EQUAL, true)],'resultKey' => '...', // your api result-key'searchFields' => ['title'],'matchings' => [

['content' => 'public.title','name' => 'title',

],],

]);

}

/*** {@inheritdoc}

*/public function preload(array $hrefs, $locale, $published = true){

if (0 === count($hrefs)) {return [];

}

$items = ...; // load items by idforeach ($items as $item) {

$result[] = new LinkItem(...); // create link-item foreach item}

return $result;}

public function find(array $ids, $locale){

if (0 === count($ids)) {return [];

}

$items = ...; // load items by idforeach ($items as $item) {

$result[] = new Teaser(...); // create teaser foreach item}

return $result;}

}

Now you can create a service for this class and add the tag <tag name=”sulu.link.provider” alias=”{your link-type}”/>.

100 Chapter 1. What’s in our documentation?

Page 105: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

How to create a custom link-select component?

A link-select aura-component, which provides the ability to select or deselect items.

The following example is a simple (and not complete) example. If you need a full example please take a look at thecomponents ckeditor/link/list@sulucontent or ckeditor/link/page@sulucontent or ckeditor/link/article@suluarticle.

define(function() {

'use strict';

return {

defaults: {options: {

link: {},locale: null,webspace: null,setHref: function(id, title, published) {},selectCallback: function(id, title, published) {}

}},

initialize: function() {this.resolveHref();this.render();

},

resolveHref: function() {if (!this.options.link.href) {

this.options.setHref();

return;}

this.loadSingle(this.options.link.href, this.options.locale).→˓then(function(data) {

this.options.setHref(data.id, data.title, data.published);}.bind(this));

},

loadSingle: function(href, locale) {return ...; // load selected item by href

},

render: function() {var $container = $(this.templates.contentDatasource());this.$el.append($container);

this.sandbox.start([

{name: 'datagrid@husky',options: {

el: '#href-select',instanceName: 'link',

(continues on next page)

1.2. Cookbook 101

Page 106: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

url: ..., // your api urlresultKey: ..., // your api result-keyclickCallback: function(id, item) {

this.options.selectCallback(id, item.title, true);}.bind(this),selectedCounter: false,paginationOptions: {

dropdown: {limit: 20

}},viewOptions: {

table: {selectItem: false

}},matchings: [

{content: 'public.id',name: 'id'

},{

content: 'public.title',name: 'title'

}]

}}

]);

}};

});

1.2.26 Provider for XML-Sitemap

SitemapProvider are used to load data for the XML-Sitemap. It returns an array of SitemapUrl instances. This Apihas to be paginated because Google only allows 50000 urls in a single Sitemap. The SitemapController takes care ofgenerating a sitemapindex if more than one Provider or more than one pages are available. Otherwise it will deliver athe Sitemap of the first Provider.

The SitemapUrl consists of the following properties:

• loc - Url to page.

• lastmod (optional) - Latest modification datetime.

• changefreq (optional) - Frequency of change (see SitemapUrl::CHANGE_FREQUENCY_* constants)

• priority (optional) - Priority of page in relation to other pages.

• alternateLinks (optional) - Alternate links like other representations or translations

The Sulu core provides a single Provider for pages (including homepage). Custom modules can provide their ownProviders that this URLs also will be published over the sitemap.xml.

102 Chapter 1. What’s in our documentation?

Page 107: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Example

This is a simple example which assumes that the logic to load entities is implemented in the Repository.

namespace AppBundle\Sitemap;

use AppBundle\Entity\ExampleRepository;use Sulu\Bundle\WebsiteBundle\Sitemap\Sitemap;use Sulu\Bundle\WebsiteBundle\Sitemap\SitemapProviderInterface;use Sulu\Bundle\WebsiteBundle\Sitemap\SitemapUrl;

class SitemapProvider implements SitemapProviderInterface{

/*** @var ExampleRepository

*/private $repository;

/*** @param ExampleRepository $repository

*/public function __construct(ExampleRepository $repository){

$this->repository = $repository;}

/*** {@inheritdoc}

*/public function build($page, $portalKey){

$result = [];foreach ($this->repository->findAllForSitemap($page, self::PAGE_SIZE) as

→˓$item) {$result[] = new SitemapUrl($item->getRoute()->getPath(), $item->

→˓getChanged());}

return $result;}

/*** {@inheritdoc}

*/public function createSitemap($alias){

return new Sitemap($alias, $this->getMaxPage());}

/*** {@inheritdoc}

*/public function getMaxPage(){

return ceil($this->repository->countForSitemap() / self::PAGE_SIZE);}

}

Now you can create a service for this class and add the tag <tag name=”sulu.sitemap.provider” alias=”{your link-

1.2. Cookbook 103

Page 108: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

type}”/>.

1.2.27 Improve Sitemap Speed

The sitemap of Sulu is based on small pieces, which are generated by so called SitemapProvider (see Provider forXML-Sitemap). Each provider returns mostly 50000 links which can return many links, which would take a biggeramount of time. The Google bot does not wait a long time for the sitemap to be returned. Therefore Sulu is able to pre-generate the whole sitemap and cache it on the filesystem. This can be triggered by calling the following command.This can also be done by a cron-job.

app/websiteconsole sulu:website:dump-sitemap

1.2.28 Enable the adobe creative suite

Sulu supports the usage of the image editor provided by Adobe Creative SDK. Enabling the image editor is a two stepprocess, since the SDK requires an API key to run inside your application.

Note: Be aware that the images are sent to the adobe servers for editing if you use the adobe creative suite.

Getting the API key

Use the My Apps page to register your own application. If you don’t have an account yet create one. Afterwards createan application for the web platform. Follow the instructions and copy the API key which will be shown at the end.

Configure Sulu to use the API key

After retrieving the API key you have to configure Sulu to use that API key. Therefore you have to set thesulu_media.adobe_create_key configuration option. You can do that e.g. in the app/config/admin/config.yml file.

sulu_media:adobe_creative_key: your-api-key

Afterwards there should be another option available in the media edit overlay called “Edit original image”.

1.2.29 Generating thumbnails for video files with ffmpeg

FFmpeg is a library to process video files. Sulu is able to use this libraries to generate thumbnail images for videofiles.

1. Install ffmpeg-bundle:

composer require pulse00/ffmpeg-bundle

2. Add bundle to app/AbstractKernel.php:

104 Chapter 1. What’s in our documentation?

Page 109: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

abstract class AbstractKernel extends SuluKernel{

public function registerBundles(){

$bundles = [...

new Dubture\FFmpegBundle\DubtureFFmpegBundle(),];

return $bundles;}

...}

3. Add configuration app/config/config.yml:

dubture_f_fmpeg:ffmpeg_binary: /usr/local/bin/ffmpeg # path to ffmpegffprobe_binary: /usr/local/bin/ffprobe # path to ffprobebinary_timeout: 300 # Use 0 for infinitethreads_count: 4

It is possible to work through the recipes, although most of the people will pick the ones, which are most similar totheir own tasks. We’re open to suggestions.

1.3 Reference

In our reference we’ll describe how to configure stuff and how to use existing things. You can find list of functionalitywe put in Sulu and how you could use it. At the moment there are components, Content Types and Twig Extensions.The Reference aims to developers who already worked through the introduction and know how Sulu works.

What you can find in the Reference

1.3.1 Parameter Reference

You can customize your Sulu installation by changing parameter values of the app/config/parameters.ymlfile. This guide documents each of the keys in this file.

1.3. Reference 105

Page 110: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

106 Chapter 1. What’s in our documentation?

Page 111: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Parameter Descriptiondatabase_driver Defines which database driver will be useddatabase_host The address of the server that is running the databasedatabase_port The port used to access the database on the serverdatabase_name The name of the databasedatabase_user The name of the database userdatabase_passwordThe password of the database usermailer_transport The protocol to send mailsmailer_host The server from which the mails will be sentmailer_user The username for sending mailsmailer_password The password for sending mailslocale The default locale for the systemsecret An unique key needed by the symfony frameworksulu_admin.nameA name, which will be shown in the administration interfacesulu_admin.emailAdministrator email addressweb-socket_port

The port which will be used for the content preview in the HTTP polling mode

websocket_url The URL which will be used for the content preview in the HTTP polling modeph-pcr_backend

The PHPCR backend definition, defaults to the doctrine-dbal, check the PHPCR documentationfor more configuration options

ph-pcr_workspace

The PHPCR workspace which will be used

phpcr_user The user for phpcrphpcr_pass The password for phpcrphpcr_cache PHPCR caching type

1.3.2 System Requirements for Running Sulu

Sulu is built on the shoulders of Giants. This page describes the requirements your system needs to fulfill when yourun a Sulu application.

Mandatory Requirements

The following requirements must be met to run Sulu:

• Mac OSX, Linux or Windows

• Apache or Nginx with enabled URL rewriting

• PHP 5.5 or higher

• the intl extension for PHP

• the fileinfo extension for PHP

• the gd or imagick extension for PHP

• a database management system supported by Doctrine

• Composer

Recommended Requirements

The following requirements are optional, but recommended for using Sulu in production:

1.3. Reference 107

Page 112: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• Apache Jackrabbit

Development Requirements

If you want to build parts of the system on your own, you will additionally need:

• Node.js

• Grunt

• Ruby

• Compass

1.3.3 Content Type Reference

As already described in Creating a Page Template a template consists of multiple content types, which enable the userto manage content in a semantic way.

The simplest template possible looks something like the this:

<?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.0.xsd"><key>default</key>

<view>ClientWebsiteBundle:templates:default</view><controller>SuluWebsiteBundle:Default:index</controller><cacheLifetime>2400</cacheLifetime>

<meta><title lang="en">Default</title>

</meta>

<properties><property name="title" type="text_line" mandatory="true">

<meta><title lang="en">Title</title>

</meta>

<tag name="sulu.rlp.part"/></property>

<property name="url" type="resource_locator" mandatory="true"><meta>

<title lang="en">Resourcelocator</title></meta>

<tag name="sulu.rlp"/></property>

</properties></template>

This chapter will describe which types you can insert within the properties tag. Every content type in the docu-mentation comes with an example property tag to clarify the usage.

108 Chapter 1. What’s in our documentation?

Page 113: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

This documentation also specifies the available parameters and tags for each content type:

Block

Description

The block content type allows to group an arbitrary amount of other content types. Each block can define multipletypes with with different content types included. These blocks can then be repeated and ordered by the content managerin the Sulu-Admin.

A quite common use case is to combine a text editor with a media selection. This way a text can be directly linked toan image via the assignment to the same block. This approach has its biggest benefit over putting images into the texteditor when used in combination with responsive design. When using multiple content types in a block the templatedeveloper has the freedom to place the image where and in which format it makes sense. In contrast, adding imagesto the text editor would make it quite hard to adapt the format and placement in the twig template.

Parameters

No parameters available

Example

Please note that the configuration of the block content type differs from the other content types.

Instead of a property-tag a block-tag is used. The default-type-attribute is mandatory and describes whichof the types are use by default.

The other essential attribute is the types-tag, which contains multiple type-tags. A type defines some titles and itscontaining properties, whereby all the available Content Type Reference (except the block itself, since we do notsupport nesting) can be used. These types are offered to the content manager via dropdown.

The example only shows a single type, combining a media selection with a text editor as described in the description.

<block name="blocks" default-type="editor" minOccurs="0"><meta>

<title lang="de">Inhalte</title><title lang="en">Content</title>

</meta><types>

<type name="editor_image"><meta>

<title lang="de">Editor mit Bild</title><title lang="en">Editor with image</title>

</meta><properties>

<property name="images" type="media_selection" colspan="3"><meta>

<title lang="de">Bilder</title><title lang="en">Images</title>

</meta><params>

<param name="type" value="image"/><param name="displayOptions" type="collection">

<param name="leftTop" value="false"/>(continues on next page)

1.3. Reference 109

Page 114: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<param name="top" value="true"/><param name="rightTop" value="false"/><param name="left" value="true"/><param name="middle" value="false"/><param name="right" value="true"/><param name="leftBottom" value="false"/><param name="bottom" value="true"/><param name="rightBottom" value="false"/>

</param></params>

</property>

<property name="article" type="text_editor" colspan="9"><meta>

<title lang="de">Artikel</title><title lang="en">Article</title>

</meta></property>

</properties></type>

</types></block>

Category list

Description

Shows a list of all available categories. The user can select with a checkbox which ones to assign to the page.Categories can be managed in the settings section of Sulu. The selection will be saved as an array.

Parameters

No parameters available

Example

<property name="categories" type="category_list"><meta>

<title lang="en">Category List</title></meta>

</property>

Checkbox

Description

Shows a simple checkbox, the state of the checkbox will be saved as a boolean.

110 Chapter 1. What’s in our documentation?

Page 115: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Parameters

Parameter Type Descriptiontype string Defines the look of the checkbox, can either be “checkbox” or “toggler”

Example

<property name="available" type="checkbox"><meta>

<title lang="en">Available</title></meta>

</property>

Color

Description

Shows a text line with an attached color picker, the inserted content will be saved as simple string.

Parameters

No parameters available

Example

<property name="color" type="color"><meta>

<title lang="en">Color</title></meta>

</property>

Date

Description

Shows a text line with an attached date picker. The inserted content will be saved as a normalized string.

Parameters

Parameter Type Descriptiondis-play_options

collec-tion

Datepicker options from http://bootstrap-datepicker.readthedocs.org/en/latest/options.html

1.3. Reference 111

Page 116: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Example

<property name="date" type="date"><meta>

<title lang="en">Date</title></meta>

<params><param name="display_options" type="collection">

<param name="format" value="yyyy-mm-dd"/><param name="startDate" value="2000-01-01"/>

</param></params>

</property>

Email

Description

Shows a text line, the inserted content will be validated against a email regex and saved as a simple string.

Parameters

No parameters available

Example

<property name="email" type="email"><meta>

<title lang="en">E-Mail</title></meta>

</property>

Internal links

Description

Shows a list with the possibility to add links to other pages managed in Sulu. Additionally it populates all the fieldsdefined in the template configuration to the HTML template. The content is stored as an array of references.

Parameters

Parame-ter

Type Description

properties collec-tion

Defines with which key which property of the linked page should be populated to theHTML template.

112 Chapter 1. What’s in our documentation?

Page 117: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Example

<property name="links" type="internal_links"><meta>

<title lang="en">Links</title></meta>

<params><param name="properties" type="collection">

<param name="title" value="title"/><param name="article" value="article"/>

</param></params>

</property>

Location

Description

Adds the possibility to assign geographic information to a page. Can be used either with Google Maps and Open StreetMaps.

Parameters

Parameter Type Descriptioncountries collec-

tionA collection of countries represented as string assigned to unique keys (usually the ISOcode of the country)

map-Providers

collec-tion

Defines the available map providers

default-Provider

string The preselected provider in the dropdown

geolocator-Name

string The alias of the service, which should be used for geolocation

Example

<property name="location" type="location"><meta>

<title lang="en">Location</title></meta><params>

<param name="countries" type="collection"><param name="AT" value="Austria"/><param name="FR" value="France"/><param name="GB" value="Great Britain"/>

</param></params>

</property>

1.3. Reference 113

Page 118: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Media selection

Description

Shows a list with the possibility to assign some assets from the media section to a page. Also allows to define aposition, which can be handled later in the template.

Parameters

Parameter Type Descriptiontypes string A comma separated list of available asset types to assign. Each item in the list must be

one of document, image, video or audio.displayOp-tions

col-lec-tion

A collection of booleans, which defines to which positions the assets can be assigned(leftTop, top, rightTop, . . . )

defaultDis-playOption

string Defines which of the displayOptions is the default one

formats col-lec-tion

A collection of image formats, which will be available when opening the cropping overlayin the page form. Contains all image formats by default.

Example

<property name="images" type="media_selection"><meta>

<title lang="en">Images</title></meta>

<params><param name="types" value="image,video"/><param name="displayOptions" type="collection">

<param name="leftTop" value="true"/><param name="top" value="true"/><param name="rightTop" value="true"/><param name="left" value="true"/><param name="middle" value="false"/><param name="right" value="true"/><param name="leftBottom" value="true"/><param name="bottom" value="true"/><param name="rightBottom" value="true"/>

</param><param name="defaultDisplayOption" value="left"/><param name="formats" type="collection">

<param name="640x960" /></param>

</params></property>

114 Chapter 1. What’s in our documentation?

Page 119: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Multiple Select

Description

Adds the possibility to choose multiple values from a given list of values.

Parameters

Parameter Type Descriptionvalues collection A collection of values to choose from.

Example

<property name="single" type="multiple_select"><meta>

<title lang="en">Multiple Select</title></meta><params>

<param name="values" type="collection"><param name="option1">

<meta><title lang="en">Option 1</title>

</meta></param><param name="option2">

<meta><title lang="en">Option 2</title>

</meta></param>

</param></params>

</property>

Contact selection

Description

Shows a list with the possibility to assign some people or organizations from the contact section to a page. Also allowsto define a position, which can be handled later in the template.

Parameters

Parameter Type Descriptioncontact boolean Person tab should be visible or not.account boolean Organizations tab should be visible or not.

1.3. Reference 115

Page 120: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Example

<property name="contacts" type="contact"><meta>

<title lang="en">Contacts</title></meta>

<params><param name="contact" value="true"/><param name="account" value="true"/>

</params></property>

<ul property="contacts">{% for contact in content.contacts %}

<li>{{ contact.type == 'contact' ? contact.fullName : contact.name }}({% for email in contact.emails %}

<a href="mailto:{{ email.email }}">{{ email.email }}</a>{% if not loop.last %}&nbsp;|&nbsp;{% endif %}

{% endfor %})

</li>{% endfor %}

</ul>

Password

Description

Shows a password input field, the inserted content will be saved as a simple string.

Parameters

No parameters available

Example

<property name="password" type="password"><meta>

<title lang="en">Password</title></meta>

</property>

Phone

Description

Shows a text line, the inserted content will be validated against a phone number regex and saved as a simple string.

116 Chapter 1. What’s in our documentation?

Page 121: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Parameters

No parameters available

Example

<property name="phone" type="phone"><meta>

<title lang="en">Phone number</title></meta>

</property>

Resource locator

Description

Shows a text line with a non-editable prefix, which represents the routes to this position in the content tree. The partof the current page can be edited in the available text line. Additionally there is a button with the URL history of thecurrent page, where parts of the history can also be deleted or reactivated.

Tags

Tag Descriptionsulu.rlp The resource locator with this tag defines the URL to a specific page.sulu.rlp.partFields marked with this tag are used to generate the URL for a specific page. If more than one field ist

marked, the values of these fields will be concatenated into the resource locator.

Parameters

No parameters available

Example

<property name="title" type="text_line"><tag name="sulu.rlp.part"/>

</property><property name="subtitle" type="text_line">

<tag name="sulu.rlp.part"/></property><property name="resource_locator" type="resource_locator">

<meta><title lang="en">Resource locator</title>

</meta>

<tag name="sulu.rlp"/></property>

1.3. Reference 117

Page 122: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Single internal link

Description

Shows a field, on which exactly one link to another page can be assigned.

Parameters

No parameters available

Example

<property name="link" type="single_internal_link"><meta>

<title lang="en">Link</title></meta>

</property>

Usage

Currently this content type only returns the UUID of the target page. In order to construct a link to the page use:

{% set target = sulu_content_load(content.myLink) %}

Then target.content will give you access to the URL and other properties of the target page.

Single Select

Description

Adds the possibility to choose a single value from a given list of values.

Parameters

Parameter Type Descriptionvalues collection A collection of values to choose from.default_value string The name of the param which should be set as default.

Example

<property name="single" type="single_select"><meta>

<title lang="en">Single Select</title></meta><params>

<param name="default_value" value="option1"/>(continues on next page)

118 Chapter 1. What’s in our documentation?

Page 123: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<param name="values" type="collection"><param name="option1">

<meta><title lang="en">Option 1</title>

</meta></param><param name="option2">

<meta><title lang="en">Option 2</title>

</meta></param>

</param></params>

</property>

Smart content

Description

Shows a list of items, which depend on a configurable filter. Depending on the DataProvider you can define where theitems come from (Datasource), what tags the filtered items must have, how they are sorted, and how many results youwant to get. Additionally you can define some presentation types, so that the content manager can decide if the itemsshould be displayed e.g. in one column or two columns. The filter is saved as a JSON string in the database.

The DataProviders are backend modules which handle the selected filters and return the items which fit to this filters.There are some predefined ones but you can add your own DataProvider easily. How you can do this is described inDataProvider for SmartContent

A very important feature is the exclude_duplicates parameter which offers the possibility to filter already useditems on a website. If this parameter is set to true the smart-content uses the Reference Store to detect already useditems and filters them.

1.3. Reference 119

Page 124: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Parameters

Parame-ter

Type Description

provider string DataProvider alias for content of SmartContent - Default ‘content’.max_per_pagein-

te-ger

Limits the results per page. Omit this parameter to disable pagination.

page_parameterstring Defines the page number key to be used in the website query string.tags_parameterstring Defines the tags key to be used in the website query string. This comma separated list of tag

names will be combined (AND) with the selected tags from the backend.cate-gories_parameter

string Defines the categories key to be used in the website query string. This comma separated listof category ids will be combined (AND) with the selected tags from the backend.

web-site_tags_operator

string OR or AND to define how the tags will be combined in the query.

web-site_categories_operator

string OR or AND to define how the categories will be combined in the query.

properties col-lec-tion

Defines the property names which will be exposed in the HTML template.

present_as col-lec-tion

A collection of strings, which can be configured for different presentation modes. If morethan one element is given, the user can choose between the elements in this collection. Theselected value is also passed to the HTML template.

cate-gory_root

string Root category (key) to display category-tree.

dis-play_options

col-lec-tion

Hide form-elements (tags, categories, sorting, limit, presentAs) can hide available elementsfor DataProvider.

ex-clude_duplicates

bool If the provider is able to detect duplicates the content-type filters already loaded records.

Return Value

This values are available in the view variable in the twig templates.

120 Chapter 1. What’s in our documentation?

Page 125: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Name Type DescriptiondataSource string Uuid of data-sourceincludeSubFolders bool Is TRUE if subfolders will be crawledcategories string[] Selected categoriescategoryOperator string Operator which combines selected categoriestags string[] Selected tagstagOperator string Operator which combines selected tagswebsiteCategories string[] Selected categories over GET parameterwebsiteCategoryOperator string Operator which combines GET parameter categorieswebsiteTags string[] Selected tags over GET parameterwebsiteTagOperator string Operator which combines GET parameter tagssortBy string Selected sort columnsortMethod string Selected sort method - ASC or DESCpresentAs string selected present as valuelimitResult string Selected limit for resultpage int Current page numberhasNextPage bool Is TRUE if another page exists

The “content” values depends on the DataProvider.

Note: You can determine content properties with the twig function dump.

DataProvider

These providers are predefined for Sulu-Entities.

Content Pages

Alias: “content”

This provider filters content pages. You can choose a parent page as data source, whose child pages will be filtered bythe DataProvider.

Parameters

Parameter Type Descriptionproperties collection Defines the property names which will be exposed in the HTML template.

Note: “properties” can include structure properties or extension data:

• title - is a property of the structure

• excerpt.title - is a property of the excerpt structure extension with the name title

For an example see Example for “content” DataProvider

1.3. Reference 121

Page 126: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Snippet

Alias: “snippet”

This provider filters snippets.

Parameters

Parameter Type Descriptiontype string If defined only snippets from this type will be returnedproperties collection Defines the property names which will be exposed in the HTML template.

Contact - People

Alias: “contact”

This provider filters the contacts.

Account - Organization

Alias: “account”

This provider filters the accounts.

Media

Alias: “media”

This provider filters the media.

Parameters

Parameter Type Descriptionmimetype_parameter string name of mime-type GET parameter (default: mimetype)type_parameter string name of media-type GET parameter (default: type)

Additionally the provider provides some additional filter for the website. With the PropertyParameter mime-type_parameter and type_parameter the name of the GET parameter can be specified.

For example the MimeType can be filtered by adding ?mimetype=application/pdf to the content URL. Same takeseffect for ?type=image with the media type (which is basically a group of mime-types).

Example for “content” DataProvider

Page template

<property name="smart_content" type="smart_content"><meta>

<title lang="en">Smart Content</title></meta>

(continues on next page)

122 Chapter 1. What’s in our documentation?

Page 127: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<params><param name="provider" value="content"/><param name="max_per_page" value="5"/><param name="page_parameter" value="p"/><param name="properties" type="collection">

<param name="article" value="article"/><param name="excerptTitle" value="excerpt.title"/><param name="excerptTags" value="excerpt.tags"/><param name="excerptImages" value="excerpt.images"/><param name="excerptDescription" value="excerpt.description"/>

</param><param name="present_as" type="collection">

<param name="two"><meta>

<title lang="en">Two columns</title></meta>

</param><param name="one">

<meta><title lang="en">One column</title>

</meta></param>

</param></params>

</property>

Twig template

<ul class="pagination">{% set page = view.pages.page %}

{% if page-1 >= 1 %}<li><a href="{{ sulu_content_path(content.url) }}?p={{ page-1 }}">&laquo;</a>

→˓</li>{% endif %}{% if view.smartcontent.hasNextPage %}

<li><a href="{{ sulu_content_path(content.url) }}?p={{ page+1 }}">&raquo;</a>→˓</li>

{% endif %}</ul>

<div property="pages">{% for page in content.pages %}

<div class="col-lg-{{ view.pages.presentAs == 'two' ? '6' : '12' }}"><h2>

<a href="{{ sulu_content_path(page.url) }}">{{ page.title }}</a></h2><p>

<i>{{ page.excerptTitle }}</i> | <i>{{ page.excerptTags|join(', ') }}</i></p>{% if page.excerptImages|length > 0 %}

<img src="{{ page.excerptImages[0].thumbnails['50x50'] }}" alt="{{ page.→˓excerptImages[0].title }}"/>

{% endif %}

(continues on next page)

1.3. Reference 123

Page 128: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

{% autoescape false %}{{ page.article }}

{% endautoescape %}</div>

{% endfor %}</div>

Note: If you have not defined the parameter max_per_page you can omit the pagination.

Snippet

Description

Shows a list with the possibility to assign an arbitrary amount of snippets. Snippets are small blocks managed in theglobal section, which can be reused on as many pages as necessary. The assigned snippets will be saved as an array ofreferences.

Parameters

Pa-rame-ter

Type Description

snip-pet-Type

string The type of snippet to assign.

de-fault

string- false

If this parameter is true or a specified area and enabled in config the content-type will load thesnippet which can be specified by the content-manager in the webspace settings.

Note: The fallback mechanism has to be enabled in the config: sulu_snippet.types.snippet.default_enabled. (defaultactivated since 1.4)

Example

<property name="snippets" type="snippet"><meta>

<title lang="en">Snippets</title></meta>

<params><param name="snippetType" value="sidebar"/><param name="default" value="sidebar_overview"/>

</params></property>

124 Chapter 1. What’s in our documentation?

Page 129: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Tag list

Description

Shows a simple text line with an autocomplete feature for the available Tags in the system. Tags can be managed inthe settings section of Sulu. The assigned tags will be saved as an array.

Note: Tags which do not already exist will be created.

Parameters

No parameters available

Example

<property name="tags" type="tag_list"><meta>

<title lang="en">Tags</title></meta>

</property>

Text area

Description

Shows a simple text area, the inserted content will be saved as simple string.

Parameters

No parameters available

Example

<property name="description" type="text_area"><meta>

<title lang="en">Description</title></meta>

</property>

Text editor

Description

Shows a rich text editor, capable of formatting text as well. The output of the editor will be stored as HTML in a stringfield.

1.3. Reference 125

Page 130: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Parameters

Parame-ter

Type Description

table booleanAdds tools for creating tables to the text editorlink booleanAdds buttons for creating links to the text editorpaste_from_wordbooleanAdds a button to paste content from word to the text editor. If you add text via this button,

some characters which could cause troubles are removed.height inte-

gerSets the initialize height of the texteditor.

max_height inte-ger

Sets the maximum height to which the texteditor can grow.

Texteditor supports also all ckeditor config parameters in snakecase.

Example

<property name="article" type="text_editor"><meta>

<title lang="en">Article</title></meta>

<params><param name="table" value="true"/><param name="link" value="true"/><param name="paste_from_word" value="true"/><param name="height" value="100"/><param name="max_height" value="200"/><!-- CKEditor Parameters examples: --><param name="extra_allowed_content" value="img(*)[*]; span(*)[*]; div(*)[*];

→˓iframe(*)[*]; script(*)[*]" /><param name="ui_color" value="#ffcc00"/>

</params></property>

Text line

Description

Shows a simple text line, the inserted content will be saved as simple string.

Parameters

Parameter Type Descriptionheadline boolean If true the height and font size of the text line get increased.

126 Chapter 1. What’s in our documentation?

Page 131: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Example

<property name="title" type="text_line"><meta>

<title lang="en">Title</title></meta><params>

<param name="headline" value="true"/></params>

</property>

Time

Description

Shows a text line, the inserted content will be validated against a localized time string and saved as a simple string.

Parameters

No parameters available

Example

<property name="time" type="time"><meta>

<title lang="en">Time</title></meta>

</property>

URL

Description

Shows a text line, the inserted content will be validated against an URL regex and saved as a simple string.

Parameters

Parameter Type Descriptiondefaults collection Default values for input (scheme and specificPart).schemes collection List of available schemes in dropdown and validation.

Example

1.3. Reference 127

Page 132: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<property name="url" type="url"><meta>

<title lang="en">URL</title></meta>

<params><param name="defaults" type="collection">

<param name="scheme" value="http://"/><param name="specific_part" value="www.google.at"/>

</param><param name="schemes" type="collection">

<param name="http://"/><param name="https://"/>

</param></params>

</property>

Teaser Selection

The “teaser_selection” content type is used for displaying teasers to other content in your website. These teasers couldbe arranged as list or grid, like in this example:

In the administration interface, the widget is displayed as a selector for the teasers. Content managers can choose anumber of target contents. By default, the text from the “Excerpt & Categories” tab of the target content is shown.You can however customize the text of the teaser if you like.

128 Chapter 1. What’s in our documentation?

Page 133: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Configuration

Add a field of type “teaser_selection” to your page template:

<!-- app/Resources/templates/pages/overview.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xi="http://www.w3.org/2001/XInclude"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.1.xsd">

<!-- ... -->

<properties><!-- ... -->

<property name="teasers" type="teaser_selection"><meta>

<title lang="en">Teasers</title></meta>

</property>

<!-- ... --></properties>

</template>

Rendering

In Twig, the field contains an array of teasers. Iterate the array and format the teasers as you like:

1.3. Reference 129

Page 134: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<ul property="teasers">{% for teaser in content.teasers %}

<li><a href="{{ sulu_content_path(teaser.url) }}">{{ teaser.title }}</a></li>{% endfor %}

</ul>

Each teaser is an object with the following properties:

Property Type Descriptionid string The ID of the teasertype (e.g. con-tent or article)

string The type of the teaser

locale string The locale, e.g. “de_AT”title string The title of the teaser. This is usually taken from the “Excerpt & Categories” tab of the

target content, but can be changed for each teaserdescription string The description of the teaser. This is usually taken from the “Excerpt & Categories” tab

of the referenced content, but can be changed for each teasermoreText string The text of the “More” linkmediaId string The ID of the image displayed with the teaser. Defaults to the first image in the tab

“Excerpt & Categories”, but can be changed for each teaserurl string The relative URL of the target content

Parameters

The following parameters can be used to customize the field in the page template:

Pa-rame-ter

Type Description

present_ascol-lec-tion

A collection of strings. Each string is typically a CSS class that is used to render the teaser list.You can configure the <title> of each entry that is shown in the admin

Configurable Presentation

Sometimes, a content manager wants to control exactly how a list of teasers is presented. You can plan for differentrendering variants in your design and let the content manager choose one variant in the administration interface.

Use the present_as option to configure the rendering variants:

<!-- app/Resources/templates/pages/overview.xml --><?xml version="1.0" ?><template xmlns="http://schemas.sulu.io/template/template"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xi="http://www.w3.org/2001/XInclude"xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.

→˓sulu.io/template/template-1.1.xsd">

<!-- ... -->

<properties>(continues on next page)

130 Chapter 1. What’s in our documentation?

Page 135: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

<!-- ... -->

<property name="teasers" type="teaser_selection"><meta>

<title lang="en">Teasers</title></meta>

<params><param name="present_as" type="collection">

<param name="three-columns"><meta>

<title lang="en">3 Columns</title></meta>

</param><param name="five-columns">

<meta><title lang="en">5 Columns</title>

</meta></param>

</param></params>

</property>

<!-- ... --></properties>

</template>

The content manager can choose one of these variants in the administration interface:

The selected value can be used to set the CSS class of the teaser element in Twig:

<ul property="teasers" class="{{ view.teasers.presentAs|default('') }}">{% for teaser in content.teasers %}

<li><a href="{{ sulu_content_path(teaser.url) }}">{{ teaser.title }}</a></li>{% endfor %}

</ul>

Custom Content with Teaser Providers

If you want to display teasers of custom data, create an implementation of TeaserProviderInterface. Forexample, we’ll make it possible to select from a list of recipes:

<?php

namespace AppBundle\Teaser;

(continues on next page)

1.3. Reference 131

Page 136: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

use Sulu\Bundle\ContentBundle\Teaser\Configuration\TeaserConfiguration;use Sulu\Bundle\ContentBundle\Teaser\Provider\TeaserProviderInterface;use Sulu\Bundle\ContentBundle\Teaser\Teaser;

class RecipeTeaserProvider implements TeaserProviderInterface{

/*** Returns the configuration for rendering the teaser provider in the

* administration interface

** @return TeaserProvider

*/public function getConfiguration(){

return new TeaserConfiguration(// The title in the dropdown of the administration interface'Recipe',// The JavaScript component started for selecting the content// In this case, we use the generic list component'teaser-selection/list@sulucontent',[ // Options of the JavaScript component

'url' => '/admin/api/recipe', // The API URL'resultKey' => 'recipes', // The key in the JSON returned by the

→˓API'searchFields' => ['title'], // The fields passed to the API when

→˓filtering for content'matchings' => [ // The columns shown in the list

['content' => 'Title', // The column title'name' => 'title', // The field shown in the column

],[

'content' => 'Rating','name' => 'rating',

],],

]);

}

/*** Returns the actual teaser data.

** @return Teaser[] The teasers

*/public function find(array $ids, $locale){

if (0 === count($ids)) {return [];

}

$items = ...; // load items by id

foreach ($items as $item) {$result[] = new Teaser(...);

}

(continues on next page)

132 Chapter 1. What’s in our documentation?

Page 137: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

return $result;}

}

Register the provider in Symfony’s service container and tag it with sulu.teaser.provider to make it func-tional:

<service id="recipe_teaser_provider" class="AppBundle\Teaser\RecipeTeaserProvider"><tag name="sulu.teaser.provider" alias="{your teaser-type}"/>

</service>

Custom JavaScript Components

A teaser selection component is an AuraJS component that provides functionality to select and deselect items.

The following is a simple (and incomplete) example. If you want to see a full example, take a look at the componentsteaser-selection/content@sulucontent and teaser-selection/list@sulucontent.

define(function() {

'use strict';

return {initialize: function() {

var $container = $('<div/>');this.$el.append($container);

this.sandbox.start([

{name: 'column-navigation@husky',options: {

el: $container,url: ..., // your api urlinstanceName: this.options.instanceName,actionIcon: 'fa-plus-circle',resultKey: ..., // your api result-keyshowOptions: false,responsive: false,markable: true,sortable: false,premarkedIds: _.map(this.options.data, function(item) {

return item.id;}),actionCallback: function(item) {

this.options.selectCallback({type: '...', id: item.id}→˓); // your teaser-type

}.bind(this)}

}]

);}

};});

1.3. Reference 133

Page 138: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.3.4 Twig Extensions

Sulu provides its own Twig functions and filters In addition to the standard set of Twig functions which you can use inwebsite templates.

CoreBundle

Functions

sulu_breadcrumb

Returns the breadcrumb for a given node UUID

Example:

{% for item in sulu_breadcrumb(uuid) %}<a href="{{ sulu_content_path(item.url) }}">{{ item.title }}</a>

{% endfor %}

Arguments:

• uuid: string - UUID of page node for which to show the breadcrumb

Returns:

• array

– content: Content of page

– view: View of page

– extension: Extensions of page

– uuid: UUID of page

– template: Template name of page

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– path: Path of page

– urls: Urls of page

– published: Publish date of page

– shadowBaseLocale: Shadow locale of page

sulu_content_load

Returns a Structure for the given UUID

{% set page = sulu_content_load('1234-1234-1234-1234-1234') %}

Arguments:

134 Chapter 1. What’s in our documentation?

Page 139: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• uuid: string - UUID of structure

Returns:

• array

– content: Content of page

– view: View of page

– extension: Extensions of page

– uuid: UUID of page

– template: Template name of page

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– path: Path of page

– urls: Urls of page

– published: Publish date of page

– shadowBaseLocale: Shadow locale of page

sulu_content_load_parent

Return the parent of the Structure with the given UUID

{% set page = sulu_content_load_parent('1234-1234-1234-1234-1234') %}

Arguments:

• uuid: string - UUID of structure parent

Returns:

• array

– content: Content of page

– view: View of page

– extension: Extensions of page

– uuid: UUID of page

– template: Template name of page

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– path: Path of page

– urls: Urls of page

1.3. Reference 135

Page 140: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

– published: Publish date of page

– shadowBaseLocale: Shadow locale of page

sulu_content_path

Returns the absolute URL for the content at the given path. The domain is taken from app/Resources/webspaces/*.io.xml and your current environment. In case you have multiple URLs in one environment, youcan prioritize one by giving it <url main="true">.

<ul class="nav nav-justified">{% for item in content.snippets[0].internalLinks %}

<li><a href="{{ sulu_content_path(item.url, item.webspaceKey) }}" title="{{

→˓item.title }}">{{ item.title }}</a></li>

{% endfor %}</ul>

Arguments:

• url: string - Url to get path

• webspaceKey string - If item is not in the same webspace as current content (optional)

• locale string - If item is not in the same locale as current content (optional)

• domain string - If a specific domain should be used to generate the url (optional)

• scheme string - If a different scheme (as the current scheme) should be used to generate the url (optional)

Returns: string - Absolute URL

sulu_content_root_path

Returns the absolute URL for the content root at the given path

<ul class="nav nav-justified">{% for item in content.snippets[0].internalLinks %}

<li><a href="{{ sulu_content_root_path(item.url, item.webspaceKey) }}" title="

→˓{{ item.title }}">{{ item.title }}</a></li>

{% endfor %}</ul>

Arguments:

• url: string - Url to get path

• webspaceKey string - If item is not in the same webspace as current content (optional)

• locale string - If item is not in the same locale as current content (optional)

• domain string - If a specific domain should be used to generate the url (optional)

• scheme string - If a different scheme (as the current scheme) should be used to generate the url (optional)

Returns: string - Absolute URL

136 Chapter 1. What’s in our documentation?

Page 141: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

sulu_meta_alternate

Note: This documentation is a incomplete.

Note: This method is deprecated, use sulu_seo instead

Return alternate links for the given URLs

Arguments:

• urls: string - Urls

Returns:

Alternate links

sulu_meta_seo

Note: This documentation is a incomplete.

Note: This method was replaced by sulu_seo, which is also already deprecated. Use the predefined template asdescribed in Rendering Pages with Twig.

Return SEO metatags with fallbacks

Arguments:

• extension: array

• content: array

Returns:

Seo fallbacks

sulu_navigation_flat

Returns navigation from the given page in a flat list data-structure.

Arguments:

• uuid: string The uuid for which the navigation should be loaded

• context: string - optional: context to filter navigation

• depth: integer - optional: depth to load (1 - one level deep, 2 - two levels deep, . . . )

• loadExcerpt: boolean - optional: load data from excerpt tab

Returns:

• array

– uuid: UUID of page

1.3. Reference 137

Page 142: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

– title: Title of page

– url: URL for page

– template: Template name of page

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– nodeType: Type of node

– path: Path of page

– excerpt: Excerpt (if load-excerpt is true)

sulu_navigation_root_flat

Returns navigation from root in a flat list data-structure.

Arguments:

• context: string - optional: context to filter navigation

• depth: integer - optional: depth to load (1 - one level deep, 2 - two levels deep, . . . )

• loadExcerpt: boolean - optional: load data from excerpt tab

Returns:

• array

– uuid: UUID of page

– title: Title of page

– url: URL for page

– template: Template name of page

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– nodeType: Type of node

– path: Path of page

– excerpt: Excerpt (if load-excerpt is true)

sulu_navigation_root_tree

Returns navigation from root in a tree data-structure.

Arguments:

• context: string - optional: context to filter navigation

138 Chapter 1. What’s in our documentation?

Page 143: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• depth: integer - optional: depth to load (1 - one level deep, 2 - two levels deep, . . . )

• loadExcerpt: boolean - optional: load data from excerpt tab

Returns:

• array

– uuid: UUID of page

– title: Title of page

– url: URL for page

– template: Template name of page

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– nodeType: Type of node

– path: Path of page

– excerpt: Excerpt (if load-excerpt is true)

sulu_navigation_tree

Returns navigation from the given page in a tree data-structure.

Arguments:

• uuid: string The uuid for which the navigation should be loaded

• context: string - optional: context to filter navigation

• depth: integer - optional: depth to load (1 - one level deep, 2 - two levels deep, . . . )

• loadExcerpt: boolean - optional: load data from excerpt tab

Returns:

• array

– uuid: UUID of page

– title: Title of page

– url: URL for page

– template: Template name of page

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– nodeType: Type of node

– path: Path of page

– excerpt: Excerpt (if load-excerpt is true)

1.3. Reference 139

Page 144: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

sulu_seo

Note: This method is deprecated. Use the predefined template as described in Rendering Pages with Twig.

Returns all the SEO related HTML tags as one string, including:

• Title

• Description

• Keywords

• Alternate links

• Canonical tag

{{ sulu_seo(extension.seo, content, urls, shadowBaseLocale) }}

Arguments:

• extension: array - The values of the SEO extension

• content: array - The values of the actual content

• urls: array - All urls in all localizations for this page

• shadowBaseLocale: string - The locale the page shadows to, in case the page is a shadow page

Returns:

All HTML strings as a simple string

sulu_sitemap

Returns sitemap for given Webspace and Locale (or default is the current locale and webspace).

Arguments:

• locale string - locale for determine sitemap (optional)

• webspaceKey string - webspace for determine sitemap (optional)

sulu_sitemap_url

Returns url for given Webspace and locale.

Arguments;

• url string - The uuid of the current content

• locale string - optional: locale for determine url

• webspaceKey string - optional: webspace for determine url

Returns:

Url for a given webspace and locale

140 Chapter 1. What’s in our documentation?

Page 145: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Filters

sulu_util_multisort

Allows arrays of arrays or objects to be sorted by any properties which are accessible via. the Symfony PropertyAc-cessor path(s).

{% for content in content.smartcontent|sulu_util_multisort('[title]', 'asc') %}{# ... #}

{% endfor %}

You can specify an array of paths to enable cascading sorting, for example

{% for content in content.smartcontent|sulu_util_multisort(['[title]', '[description]→˓'], 'asc') %}

Arguments:

• path: Property path

• direction: Direction to sort, either ASC or DESC

SnippetBundle

sulu_snippet_load

Returns content array for given snippet uuid.

{% set snippet = sulu_snippet_load('1234-1234-1234-1234-1234') %}{{ snippet.title }}

Arguments:

• uuid: string - The uuid of requested content.

• locale: string - optional: Locale to load snippet.

Returns:

• array

– uuid: UUID of page

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– content: Snippet content data

– view: Snippet view data

1.3. Reference 141

Page 146: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

sulu_snippet_load_default

Note: This method is deprecated, use sulu_snippet_load_by_area instead.

Returns content array of selected default snippet for given snippet type.

{% set snippets = sulu_snippet_load_default('default') %}{{ snippets[0].content.title }}

Note: The system currently only support one default per type.

Arguments:

• snippetType: string - The type to search for default snippets.

• webspaceKey: string - optional: The webspace to get default snippet settings.

• locale: string - optional: The locale to load snippet.

Returns:

An array of:

• array

– uuid: UUID of page

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– content: Snippet content data

– view: Snippet view data

sulu_snippet_load_by_area

Returns the selected snippet from the webspace settings for the given snippet area.

{% set snippets = sulu_snippet_load_by_area('sidebar_overview') %}{{ snippets.content.title }}

Arguments:

• area: string - The area to search for snippet.

• webspaceKey: string - optional: The webspace to get area snippet settings.

• locale: string - optional: The locale to load snippet.

Returns:

• array

– uuid: UUID of page

142 Chapter 1. What’s in our documentation?

Page 147: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

– changed: Changed date of page

– changer: User ID of changer

– created: Date of creation

– creator: User ID of creator

– content: Snippet content data

– view: Snippet view data

MediaBundle

sulu_get_media_url

Returns relative URL to the given media.

{% set url = sulu_get_media_url(media, 'inline') %}

Configuration:

Following configuration is optional and means, that the default dispositionType is “attachment” for each file and onlyif the mimeTypes of a file match “application/pdf” or “image/jpeg” it’s the “inline” dispositionType.

If the default dispositionType would be “inline” and some files should be “attachment”, than the configuration of“mime_types_attachment” should be filled and “mime_types_inline” should be empty.

sulu_media:disposition_type:default: "attachment"mime_types_inline: ["application/pdf", "image/jpeg"]mime_types_attachment: []

Arguments:

• media: object - The media object

• dispositionType: string - override default configuration (‘inline’, ‘attachment’) (optional)

Returns: string - Relative URL

sulu_resolve_media

Returns resolved media with needed properties for a given media object.

{% set media = sulu_resolve_media(contact.medias[0], 'de') %}<img src="{{ media.thumbnails['100x100'] }}" title="{{ media.title }}" />

Arguments:

• media: object - The media object.

• locale: string - Locale to resolve metadata.

Returns: object - Object with all needed properties, like thumbnails, title, description and url.

1.3. Reference 143

Page 148: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

sulu_resolve_medias

Returns resolved medias with needed properties for a given media array.

{% set medias = sulu_resolve_medias(contact.medias, 'de') %}{% for media in medias %}

<img src="{{ media.thumbnails['100x100'] }}" title="{{ media.title }}" />{% endfor %}

Arguments:

• media: object[] - The media object.

• locale: string - Locale to resolve metadata.

Returns: object[] - Object with all needed properties, like thumbnails, title, description and url.

TagBundle

sulu_tags

Returns all tags in the system.

<ul><li><a href="{{ sulu_tag_url_clear() }}">None</a></li>{% for tag in sulu_tags() %}

<li><a href="{{ sulu_tag_url(tag) }}">{{ tag.name }}</a></li>{% endfor %}

</ul>

Returns: array - array of serialized Tag instances

sulu_tag_url

Returns current URL with the given tag as GET parameter.

Arguments:

• tag: array - Serialized Tag instance to determine value

• tagsParameter: string - optional “tags”: parameter name

Returns: string - current URL with given tag in tags parameter

sulu_tag_url_append

Returns current URL and append given tag to GET parameter.

Arguments:

• tag: array - Serialized Tag instance to determine value

• tagsParameter: string - optional “tags”: parameter name

Returns: string - current URL with given tag in tags parameter

144 Chapter 1. What’s in our documentation?

Page 149: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

sulu_tag_url_clear

Returns current URL and clear the given GET parameter.

Arguments:

• tagsParameter: string - optional “tags”: parameter name

Returns: string - current URL removed tags parameter

CategoryBundle

sulu_categories

Returns all categories in the system.

<ul><li><a href="{{ sulu_category_url_clear() }}">None</a></li>{% for category in sulu_categories('en', 'category_key') %}

<li id="{{ category.key }}"><a href="{{ sulu_category_url(category) }}">{{ category.name }}</a>

</li>{% endfor %}

</ul>

Arguments:

• locale: If item is not in the same locale as current content (optional)

• parentKey: If only specific categories should be loaded set a parent category key (optional)

Returns: array - array of serialized Category instances

sulu_category_url

Returns current URL with the given category as GET parameter.

Arguments:

• category: array - Serialized Category instance to determine value

• categoryParameter: string - optional “category”: parameter name

Returns: string - current URL with given category in categories parameter

sulu_category_url_append

Returns current URL and append given category to GET parameter.

Arguments:

• category: array - Serialized Category instance to determine value

• categoryParameter: string - optional “category”: parameter name

Returns: string - current URL with given category in categories parameter

1.3. Reference 145

Page 150: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

sulu_category_url_clear

Returns current URL and clear the given GET parameter.

Arguments:

• categoryParameter: string - optional “category”: parameter name

Returns: string - current URL removed category parameter

ContactBundle

sulu_resolve_contact

Returns user entity.

{{ sulu_resolve_contact(contactId).fullName }}

Arguments:

• id: int - Id of the requested contact.

Returns: Contact - Object with all needed properties.

SecurityBundle

sulu_resolve_user

Returns user entity.

{{ sulu_resolve_user(changer).contact.fullName }}

Arguments:

• id: int - Id of the requested user.

Returns: User - Object with all needed properties.

1.3.5 Document Manager

Sulu Document Manager

The Sulu Document Manager is a layer which sits between the PHPCR repository and the application model. Itprovides a layer of domain abstraction on top of the raw PHPCR session, workspace, query manager, etc.

It is similar in concept to a typical ORM (for example Doctrine ORM) with some differences.

The following is an example:

<?php// find a document in a specific locale and set a new title$document = $documentManager->find('/cmf/contents/foobar', 'de');$document->setTitle('Hello');

// persist the document then flush the changes

(continues on next page)

146 Chapter 1. What’s in our documentation?

Page 151: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

$documentManager->persist($document);$documentManager->flush();

If you are familiar with Doctrine this will seem very familiar. There are some differences however:

• Persist commits the changes to the node immediately, changes made to the document later on will not be takeninto account on flush(). It is better to think of persist() as a function which prepares a snapshot of the currentstate of the document to be persisted.

• The document manager is localization aware by default.

Some other things to note:

• The Document Manager is 100% event based. This makes it very extensible, all of the functionality is providedby Event Subscribers.

• Documents are defined with “Behavior” interfaces, which the event subscribers use to determine if and how thedocument should be handled.

Using the Document Manager

Finding documents

Documents can be located using either their UUID or their path:

<?php$document = $documentManager->find('/path/to/document');$document = $documentManager->find('842e61c0-09ab-42a9-87c0-308ccc90e6f4');

To find a localized document:

$germanDocument = $documentManager->find('842e61c0-09ab-42a9-87c0-308ccc90e6f4', 'de→˓');

Additionally, options can be specified:

<?php$fooDocument = $documentManager->find('842e61c0-09ab-42a9-87c0-308ccc90e6f4', 'de',→˓array(

'my_option' => 'foobar',));

Persisting documents

The Sulu Document Manager requires that you persist() documents and then flush the document manager.

Note: The persist operation, unlike other document/object managers, takes a snapshot of the document in itscurrent state and maps the data to the PHPCR node.

Changes made to the document after calling persist will not be taken in to account when flush is called.

Below is a simple persist operation:

1.3. Reference 147

Page 152: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<?php$document = new MyDocument();$document->setTitle();

$documentManager->persist($document, 'fr', array('path' => '/path/to/persist/to',

));

$documentManager->flush();

This persists the document in the French language at the path. The path is given as an option. The path option comesfrom the ExplicitPathSubscriber subscriber. The amount of options available depends on which subscribersyou have registered.

See the Behaviors chapter for more information.

The Path Builder

The structure of the Sulu content repository is configurable. This means that if you hard code a path /cmf/sulu_io/contents then your code could break, as both the cmf and contents segments of this path are configurable.

The path builder provides a way to elegantly compose content repository paths by passing an array of path segments:

$pathBuilder = $container->get('sulu_document_manager.path_builder');$path = $pathBuilder->build(array('%base%', 'sulu_io', '%content%', 'path/to/article→˓');

The above code would produce the path /cmf/sulu_io/contents/path/to/article using the default con-figuration.k

Path segments enclosed within % characters are resolved by the PathSegmentRegistry, which uses configurationto map path segment names to values. Other segments are interpreted literally.

Creating Documents

The Sulu Document Manager uses interfaces to determine how a document is handled. These interfaces are known asbehaviors. Behaviors act upon documents.

Note: It is equally possible to implement what is now the conventional mapping pattern using XML, YAML, annota-tion, etc. But for now only behavioral interfaces are supported.

The Document

<?php

namespace Acme\Bundle\FooBundle\Document;

use Sulu\Component\DocumentManager\Behavior\Mapping\NodeNameBehavior;use Sulu\Component\DocumentManager\Behavior\Mapping\PathBehavior;use Sulu\Component\DocumentManager\Behavior\Mapping\UuidBehavior;

(continues on next page)

148 Chapter 1. What’s in our documentation?

Page 153: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

class SomeDocument implementsNodeNameBehavior,PathBehavior,UuidBehavior,

{private $nodeName;private $path;private $uuid;private $targetDocument;

public function getNodeName(){

return $this->nodeName;}

public function getPath(){

return $this->path;}

public function getUuid(){

return $this->uuid;}

}

The above document will have the nodes path, UUID and node name populated. The properties are mandatory and thebehaviors will expect them to be there, if they are not then an exception will be thrown explaining which propertiesneed to be added.

Note: The behaviors will often use Reflection to set the value of an objects properties, bypassing any protection thatproperty may have.

Note: Because the Document Manager uses interfaces and does not depend on metadata mapping you can put yourdocument anywhere you want without changing any configuration.

Defining the alias and type

In order for the document manager to recognize existing managed documents and persist new ones, you must addsome mapping to your configuration.

This configuration can be done in you applications config.yml file:

sulu_document_manager:mapping:

# ...my_new_document:

phpcr_type: acme:somedocumentclass: Acme\Bundle\FooBundle\Document\SomeDocument

Above we define three things:

1.3. Reference 149

Page 154: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1. The alias of the document as my_new_document. This alias can be used instead of the long class name whenmanaging the document.

2. A PHPCR type which will be used to map the document to the class name.

3. The class which should be managed.

Data Fixtures

The Sulu DocumentManager integration includes a fixture loader which allows you to load static data into your contentrepository.

Getting Started

Shown below is the simple data fixtures:

<?php// YourBundle/DataFixtures/Document/SomeFixture.php

namespace YourBundle\DataFixtures\Document;

use Sulu\Component\DocumentManager\DocumentManager;use Sulu\Bundle\DocumentManagerBundle\DataFixtures\DocumentFixtureInterface;use Sulu\Component\Content\Document\WorkflowStage;

class SomeFixture implements DocumentFixtureInterface{

public function getOrder(){

return 10;}

public function load(DocumentManager $documentManager){

$document = $documentManager->create('page');

/** @var \Sulu\Bundle\ContentBundle\Document\PageDocument $document */$document = $documentManager->create('page');$document->setLocale('en');$document->setTitle('foo bar page');$document->setStructureType('default');$document->setResourceSegment('/foo-bar-page');$document->setWorkflowStage(WorkflowStage::PUBLISHED);$document->getStructure()->bind(array(

'title' => 'foo bar page'));

$document->setExtension('excerpt',[

'title' => 'foo title','description' => 'bar description','categories' => [],'tags' => []

]);

(continues on next page)

150 Chapter 1. What’s in our documentation?

Page 155: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

$documentManager->persist($document, 'en', array('parent_path' => '/cmf/sulu_io/contents',

));

# Optional: If you don't want your document to be published, remove this line$documentManager->publish($document, 'en');$documentManager->flush();

}}

Note that:

• The class name MUST end with Fixture for it to be recognized

• The class MUST be placed in <your bundle>/DataFixtures/Document in order for it to be loaded automatically.

You can now execute your data fixture using the sulu:document:fixtures:load command.

$ php app/console sulu:document:fixtures:load

By default this command will purge and re-initialize the workspace before loading all of the fixtures.

Warning: Unless you use the –append option, your workspace will be purged!

Advanced Usage

You can specify directories instead of having the command automatically find the fixtures:

$ php app/console sulu:document:fixtures:load --fixtures=/path/to/fixtures1 --→˓fixtures=/path/to/fixtures2

You can also specify if fixtures should be appended (i.e. the repository will not be purged) and if the initializer shouldbe executed.

Append fixtures:

$ php app/console sulu:document:fixtures:load --append

Do not initialize:

$ php app/console sulu:document:fixtures:load --no-initialize

Using the Service Container

If you need the service container you can implement the SymfonyComponentDependencyInjectionContainerAwareIn-terface:

<?php// YourBundle/DataFixtures/Document/SomeFixture.php

namespace YourBundle\DataFixtures\Document;

(continues on next page)

1.3. Reference 151

Page 156: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

use Sulu\Bundle\DocumentManagerBundle\DataFixtures\DocumentFixtureInterface;use Symfony\Component\DependencyInjection\ContainerAwareInterface;use Symfony\Component\DependencyInjection\ContainerInterface;

class SomeFixture implements DocumentFixtureInterface, ContainerAwareInterface{

private $container;

public function setContainer(ContainerInterface $container = null){

$this->container = $container;}

}

Behaviors

The document manager interacts with objects (documents) with event subscribers. The event subscribers use interfacesto determine if they should apply themselves to a document. These interfaces are known as Behaviors.

This chapter will explain all of the behaviors which are available in Sulu.

Each section will show, according to need, the behavior interface required to implement the behavior, the propertieswhich you MUST implement and any options which will be available.

Auditing

Auditing subscribers record and provide access to details of when the document was modified and the user that modi-fied it.

Blame

Behavior: Sulu\Component\DocumentManager\Behavior\BlameBehavior

This behavior will record the identifier of the user who created the object and when the document is updated, the userwho updated it.

The user identifier will be retrieved from the Symfony session, or can be explicitly specified with an option.

If no user identifier is available, no action will be taken.

Properties:

• $creator: The identifier of the user that created the object.

• $changer: The identifier the last user to have changed the object.

Options:

• blame.user_id: Specify or override the identifier which will be recorded.

Timestamp

Behavior: Sulu\Component\DocumentManager\Behavior\Audit\TimestampBehavior

Record the time when the object was created and the time when the object was updated.

152 Chapter 1. What’s in our documentation?

Page 157: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Properties:

• $created: The date the object was created.

• $changed: The data the object was changed.

Mapping

Mapping subscribers set and provide access to properties in the document.

Children

Behavior: Sulu\Component\DocumentManager\Behavior\Mapping\ChildrenBehavior

Provides access from the document to the children of the document in the content tree. The property will be populatedwith a collection object.

Note: Children documents are loaded lazily, so this behavior does not entail a performance penalty.

Properties:

• $children: The children collection.

Locale

Behavior: Sulu\Component\DocumentManager\Behavior\Mapping\LocaleBehavior

Provides access to the documents Locale at the time the object was hydrated according to the DocumentRegistry.

Properties:

• $locale: The locale the document is currently loaded in.

NodeName

Behavior: Sulu\Component\DocumentManager\Behavior\Mapping\NodeNameBehavior

Maps the PHPCR node name.

Properties:

• $nodeName: The locale the document is currently loaded in.

Parent

Behavior: Sulu\Component\DocumentManager\Behavior\Mapping\ParentBehavior

Map and assign the parent document through getParent and setParent methods.

Unmanaged parent documents with a UUID will be returned UnknownDocument instances. Unmanaged parentdocuments with no UUID will not be hydrated (they will be NULL).

1.3. Reference 153

Page 158: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Warning: This event currently incurs a performance penalty as it needs to eagerly load the parent PHPCR node.This could potentially have a noticable impact when loading a large number of nodes.

Path

Behavior: Sulu\Component\DocumentManager\Behavior\Mapping\PathBehavior

Map the path of the document within the content repository.

Properties:

• $path: The path to the document.

Title

Behavior: Sulu\Component\DocumentManager\Behavior\Mapping\TitleBehavior

Map the title of the document.

Properties:

• $title: Title of the document.

Uuid

Behavior: Sulu\Component\DocumentManager\Behavior\Mapping\UuidBehavior

Map the UUID (Universally Unique Identifier) of the document.

Properties:

• $uuid: The UUID of the document.

Path

Path subscribers affect the location of the document within the content repository.

AliasFiling

Behavior: Sulu\Component\DocumentManager\Behavior\Path\AliasFilingBehavior

This is a filing behavior which will automatically place the document at given path as a child of a node named afterthe documents alias as defined in the configuraiton mapping.

For example, if the base path is /cms/content and the document has an alias of article and the namemy-article then the document will be stored at /cms/content/article/my-article.

AutoName

Behavior: Sulu\Component\DocumentManager\Behavior\Path\AutoNameBehavior

The auto-name subscriber will automatically set the node name of the PHPCR node as a slugified version of its title(the document must also implement the TitleBehavior).

154 Chapter 1. What’s in our documentation?

Page 159: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Explicit

Behavior: None. This behavior is depends entirely on options.

This subscriber allows the path of the document to be set explicitly through the use of options. This subscriber requiresno interfaces, it is available on all documents automatically.

For example:

<?php$documentManager->persist($document, 'de', array(

'path' => '/path/to/document'));

Options:

• path: Absolute path to where the document should be stored.

• parent_path: Specify only the parent path (the node name could then be determined through another mech-anism, e.g. the AutoName behavior.

• node_name: Specify only the node name

• auto_create: If any “missing” parent nodes should be automatically created.

Sulu Specific

The following behaviors are specific to Sulu.

Content

Behavior: Sulu\Component\Content\Document\Behavior\ContentBehavior

Maps the structure content to the document. The content is mapped as a ContentContainer instance.

Properties:

• $content: The content container.

Extension

Behavior: Sulu\Component\Content\Document\Behavior\ExtensionBehavior

Sets and provides access to the extension data.

LocalizedContent

Behavior: Sulu\Component\Content\Document\Behavior\LocalizedContentBehavior

Allows the document to potentially have different structure type for each locale.

NavigationContext

Behavior: Sulu\Component\Content\Document\Behavior\NavigationContextBehavior

Enables the document to have navigation contexts assigned to it.

1.3. Reference 155

Page 160: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Order

Behavior: Sulu\Component\Content\Document\Behavior\OrderBehavior

Documents implementing this behavior will have a sulu:order property added to the PHP node which will enablethe document the order to remain constant in both the tree and in query results.

Page

Behavior: Sulu\Component\Content\Document\Behavior\PageBehavior

Documents implementing this behavior will be treated as “pages” - that is they are expected to represent a singlewebpage with an associated route.

This behavior extends the Webspace behavior.

RedirectType

Behavior: Sulu\Component\Content\Document\Behavior\RedirectTypeBehavior

Documents implementing this behavior are able to optionally redirect to either an internal or an external resource.

ResourceSegmentBehavior

Behavior: Sulu\Component\Content\Document\Behavior\ResourceSegmentBehavior

Maps a resource segment which will be used when generating the URI for the document.

Route

Behavior: Sulu\Component\Content\Document\Behavior\RouteBehavior

Documents implementing this behavior will act as routes. Routes are documents which are located at a path represent-ing one of the URIs of a page document. The route contains a reference to the page.

ShadowLocale

Behavior: Sulu\Component\Content\Document\Behavior\ShadowLocaleBehavior

The implementing document will have the possibility to enable a “shadow locale” and load its content from a differentlocale within the same document.

StructureTypeFiling

Behavior: Sulu\Component\Content\Document\Behavior\StructureTypeFilingBehavior

Implementing documents will be stored at a path depending on their structure type. Snippets implement this behavior.

156 Chapter 1. What’s in our documentation?

Page 161: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Webspace

Behavior: Sulu\Component\Content\Document\Behavior\WebspaceBehavior

Provides access to the documents webspace name.

WorkflowStage

Behavior: Sulu\Component\Content\Document\Behavior\WorkflowStageBehavior

Documents implementing this interface can have a workflow stage applied to them. For example “test” and “published”are workflow stages.

Debugging

One of the disadvantages of an event based system is that tracking what happens and when it happens can be tricky.The Document Manager provides some tools to ameliorate this problem.

Subscriber Debug Command

It is often useful to know which subscribers are being called and the order in which they are called. If you are usingSulu, then this can be achieved via the following command:

$ ./app/console sulu:document:subscriber:debug remove+--------------------------------------------------------------------------+----------→˓--------+----------+| Class | Method→˓ | Priority |+--------------------------------------------------------------------------+----------→˓--------+----------+| Sulu\Bundle\SearchBundle\EventListener\ContentSubscriber |→˓handlePreRemove | 600 || Sulu\Component\Content\Document\Subscriber\ContentRemoveSubscriber |→˓handleRemove | 550 || Sulu\Component\DocumentManager\Subscriber\Phpcr\RemoveSubscriber |→˓handleRemove | 500 || Sulu\Component\Content\Document\Subscriber\Compat\MapperRemoveSubscriber |→˓handlePreRemove | 500 || Sulu\Component\DocumentManager\Subscriber\Core\RegistratorSubscriber |→˓handleRemove | 490 || Sulu\Bundle\SearchBundle\EventListener\ContentSubscriber |→˓handlePostRemove | -100 || Sulu\Component\Content\Document\Subscriber\Compat\MapperRemoveSubscriber |→˓handlePostRemove | -100 |+--------------------------------------------------------------------------+----------→˓--------+----------+

Here we list all of the subscribers which will be executed when a remove event is fired.

A full list of events can be retrieved if you omit the argument:

$ ./app/console sulu:document:subscriber:debug+----------------------+| Event |

(continues on next page)

1.3. Reference 157

Page 162: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

+----------------------+| persist || hydrate || remove || refresh || copy || move || create || clear || find || reorder || flush || query.create || query.create_builder || query.execute || configure_options |+----------------------+

Logging

The Document Manager provides detailed logging about which subscribers are executed, the state of the event and thetime taken by each event to be executed, for example:

0.012195 S\C\C\D\S\ExtensionSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\C\D\S\WebspaceSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.076923 S\C\C\D\S\ContentSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\C\D\S\NavigationContextSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\C\D\S\RedirectTypeSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\C\D\S\WorkflowStageSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\C\D\S\OrderSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\C\D\S\RouteSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\D\S\B\A\BlameSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\D\S\B\A\TimestampSubscriber handlePersist n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\D\S\B\M\NodeNameSubscriber handleNodeName n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\D\S\B\M\UuidSubscriber handleUuid n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents0.000000 S\C\D\S\B\M\ParentSubscriber handleChangeParent n:/cmf/sulu_io/→˓contents/test1 d:0000000021b01e32000000005bcf8fba l:en p:/cmf/sulu_io/contents

Have a closer look:

[1] [2] [3] [4]0.012195 S\C\C\D\S\ExtensionSubscriber handlePersist n:/cmf/sulu_io/contents/test1→˓d:0000000021b01e32000000005bcf8fba l:en

158 Chapter 1. What’s in our documentation?

Page 163: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1. The time taken by the subscriber, expressed as a fraction of a second.

2. The class name. The namespace is compressed to allow for greater readability.

3. The method which handled the event

4. Event details, retrieved by the events getDebugMessage method.

The event details are context sensitive, the following lists all abbreviations:

• n: PHPCR Node path or UUID

• d: Document path or UUID

• p: Parent node path or UUID

• l: Locale

• i: Identifier (used for find events)

• did: Destination ID (used in copy/move events)

• dnam: Destination name (used in copy/move events)

• after: If a node should be ordered after or not (only for reorder events)

Warning: Logging will slow down your application drastically. It should only be enabled in development envi-ronments.

Extending the Document Manager

Where to put things?

Any classes which relate to the documents or the document manager should first, by convention, be placed within aDocument namespace.

Documents themselves should be placed directly under this namespace and other types of class should be placed insub namespaces with appropriate names. For example:

src/Bundle/MyBundle/Document/FooDocument.phpsrc/Bundle/MyBundle/Document/Initializer/FooInitializer.phpsrc/Bundle/MyBundle/Document/Subscriber/FooSubscriber.php

1.3.6 Glossary

A glossary is:

A list of terms in a particular domain of knowledge with their definitions.

This page aims to list all of the terminology used within Sulu both as a reference and as a guide to use when namingthings in the code-base.

Component Of a Structure – a named set of Properties. Used by blocks. ??

Document Documents are the domain representation of nodes from the PHPCR content repository. For example“PageDocument”, or “SnippetDocument”.

The namespace used within components/bundles for all things relating to document the document managercomponent.

1.3. Reference 159

Page 164: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Document type The short name for a class of document, for example “page” the name of the PageDocument class.

Locale Represents a linguistic region, for example de, de_at, en or en_us.

Localized Of a Property - the state of being localized, or capable of being translated.

Metadata Literally data about data. Typically a data structure with information such as field mappings which shouldbe applied to a different data structure.

In the context of Sulu this applied to Structures, Properties and Documents.

Non-localized Of a Property - the state of not being localized, not capable of being translated.

Path Always refers to the path of an object within the content repository, for example /cmf/sulu_io/contents/animals/dog is a path.

Page A page is basic type of document. Pages are accessible directly with URLs and they represent pages of yourwebsite.

Parameter In relation to Property and Structure items; a configuration parameter which relates to the configurationof the content type.

Prefix The former part of a web facing URL which is defined by the portal, it is followed by the resource locator. Theprefix may include the locale.

Property This term refers to the items in a Structure.

Property Type Property types are the way Sulu represents different types of “content”. For example, email, text andsmart_content are three examples of Sulu Property Types

Resource locator The later part of a web facing URL belonging to some document, excluding the host and prefixsegment. For example /articles/foo is a resource locator, however /de/articles/foo and http://example.com/articles/foo are not. The resource locator will never include the locale.

Segment As applying to URLs and Paths - a section of a path or URL, presumably delimited by /.

Shadow Of a document. A localized document can specify that it should be loaded in a different locale. The targetlocale is called the “shadow” locale.

Workflow Stage The stage of the workflow, for example “published” and “test” are stages.

Snippet Snippets are like pages except that they are not accessible directly with URLs. Snippets are typically aggre-gated within pages.

Structure Structures represent dynamic content in Sulu. A structure is a collection of Properties.

Structure type The name of a given structure, e.g. overview, hotel or article.

Webspace In Sulu a webspace encapsulates all of the data of one or more domains which use the same data.

Webspace Document The document at the root of the webspace tree – the homepage.

Have fun with our Reference.

1.4 Bundles

The Sulu code is structured in Symfony Bundles. Some of them are more important to developers who work with Suluthan others. In this documentation we documented the most important ones.

We documented the following bundles

160 Chapter 1. What’s in our documentation?

Page 165: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.4. Bundles 161

Page 166: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.4.1 AdminBundle

The AdminBundle contains important features for developing the User-Interface of bundles. It provides various hookswhich can be used by Javascript-components in various bundles to reuse code or to just deal with different tasks moreeasily.

Javascript-Hooks

Javascript-hooks are just special-name-properties placed in the return-object of javascript-components. The Admin-Bundle recognizes the hooks and executes functionality, like rendering a header with tabs or just manipulating thecolumn-layout.

Layout

With the layout-hook the three-column-layout (navigation, content, sidebar) can be manipulated

Lets see an example:

layout: {navigation: {

collapsed: true},content: {

width: 'fixed',topSpace: false,leftSpace: false,rightSpace: true

},sidebar: {

width: 'max',url: '/admin/widget-groups/my-widget-group'

}}

This layout-property just needs to be placed in the return block of the javascript-component and the AdminBundledoes the rest.

162 Chapter 1. What’s in our documentation?

Page 167: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

List of all available options:

Parame-ter

Type Description

naviga-tion

Object contains properties affecting the navigation-column

naviga-tion.collapsed

Boolean If true the navigation transitions to collapsed state

naviga-tion.hidden

Boolean If true the navigation gets hidden

content Object contains properties affecting the content-columncon-tent.width

String fixed` to keep the column at a fixed width or ``max to make thecolumn take the maximum of the available space

con-tent.leftSpace

Boolean If false the content-column has no padding on the left

con-tent.rightSpace

Boolean If false the content-column has no padding on the right

con-tent.topSpace

Boolean If false the content-column has no padding on the top

sidebar’ Ob-ject|Boolean

Contains properties affecting the sidebar-column. If false the sidebar gets hidden.

side-bar.width

String fixed to keep the column at a fixed width or max to make the column take the maximumof the available space

side-bar.url

String Url from which markup gets fetched and placed inside the sidebar

exten-dExisting

Boolean If true the passed configurations for navigation, column and sidebar won’t be merged withthe defaults. So the existing layout stays the same and only the explicitly passed propertiestake effect.

Note: Either the sidebar-column or the content-column must have a fixed width. If the width of both columns is oftype max the behaviour is not specified.

The defaults of the layout-hook are:

layout: {extendExisting: false,navigation: {

collapsed: false,hidden: false

},content: {

width: 'fixed',leftSpace: true,rightSpace: true,topSpace: true

},sidebar: false

}

To get the default layout just write layout: {}.

1.4. Bundles 163

Page 168: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Header

The header-hook renders a header (the blue bar with the toolbar in it) into your component. Moreover it takes care ofthe tab-handling.

Most often you’ll find yourself defining a component which contains the header-hook and methods for manipulatingthe toolbar or other methods which implement behaviour which is the same over all tabs.

The header-hook in such a component could look something like:

header: function() {return {

tabs: {url: 'url/to/tabsData',componentOptions: {

values: this.data}

},toolbar: {

languageChanger: true,buttons: {save: {},settings: {

options: {dropdownItems: {

delete: {}}

}}

}},title: 'My title'

};}

What this hook does is essentially the following:

• It initializes the tabs with the data returned from the defined url. From then on you’ll never have to worry abouttabs again. The whole routing to and starting your tabs component is handled automatically if the data returnedby tabs.url has the right format.

• It initializes the toolbar with the defined buttons. For information on how to configure buttons and even how tocreate your own buttons have look Sulu-Buttons.

• It injects the title into every tab (or into your current component if no tabs specified)

Note: The header-hook can also be a function which returns the object seen in the example. Within this function youhave access to things like this.options.

Tab Conditions

To enable tab-conditions in the header you have to do following steps:

1. Set the option tabs.componentOptions.values. This object will be used to evaluate the conditions.

2. Emit the event this.sandbox.emit(‘sulu.header.saved’, savedData) with the newly saved data. This will updatethe tabs and reevaluate the conditions with given data.

164 Chapter 1. What’s in our documentation?

Page 169: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

List of all available options:

Parameter Type Descriptiontitle String|FunctionA title which gets injected into every tab-component or into the current component

if no tabs exist. If it’s a function it must return a string.noBack Boolean if true no back icon gets renderedtabs Object contains configuration-properties about the tabstabs.url String url to load the tabs-data fromtabs.data Object tabs-data. Either this option or the tabs.url option must be set when working

with tabs.tabs.options Object an object which gets merged into the component-options of every tab-componenttabs.componentOptionsObject an object which gets merged into the component-option of the husky-tab compo-

nenttabs.componentOptions.valuesObject which will be used to evaluate the tab display conditiontabs.container String|Objecta selector or a dom-object into which the tabs-components get renderedtoolbar Object contains configuration-properties about the toolbartoolbar.buttons Object an object of sulu-buttons. For the documentation on sulu-buttons have a look Sulu-

Buttons.toolbar.options Object an object of options to pass to the husky-toolbar-componenttool-bar.languageChanger

Ob-ject|Boolean

contains configuration-properties for the language-changer dropdown. If just set totrue renders a dropdown with the system-locales and emits events as a callback

tool-bar.languageChanger.url

String An url to load the items for the language-changer dropdown

tool-bar.languageChanger.preSelected

String the preselected language id

tool-bar.languageChanger.callback

Func-tion

a callback function which gets executed when the language-changer-dropdowngets changed

Load-component-data

As a front-end developer you’ll often find yourself loading data at the beginning of your component startup andcontinuing only after the data has been loaded.

The load-component-data-hook simplifies this task. Within the return-object of a javascript-component, you can spec-ify a loadComponentData method where you load your data. This method must return a promise or your desireddata straight away. If such a method is specified the AdminBundle delays the startup of your component and setsthis.data with your loaded data (where this is the context of your component). So when your componentsinitialize method gets called you can conveniently access your data via this.data and don’t have to worryabout asynchronicity.

So the loadComponentData and initialize method of your component would look something like:

/*** This method gets called by the AdminBundle

*/loadComponentData: function() {

var promise = $.Deffered();

$.ajax({url: '/url-to-your-data',

}).done(function(data) {//resolve promise. So your component can continue with the startup

(continues on next page)

1.4. Bundles 165

Page 170: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

promise.resolve(data);});

return promise;},

/*** When this method gets called this.data is already set with

* your loaded data

*/initialize: function() {

// this.data is set with your data and can be used for whatever you wantthis.render(this.data);

}

Defaults

The default-hook merges and prepares the options, translations and templates automatically. This normalizes andcentralize the functionality for default values in component.

define(function () {

'use strict';

var defaults = {options: {

instanceName: 'example',items: []

},translations: {

exampleButtonLabel: 'example.button.label',exampleHeader: 'example.header'

},templates: {

skeleton: ['<h1><%= translations.exampleHeader %></h1>','<button><%= translations.exampleButtonLabel %></button>'

].join('')}

};

return {

defaults: defaults,

initialize: function () {},

render: function () {this.dom.html(

this.templates.skeleton({translations: this.translations}

));

}(continues on next page)

166 Chapter 1. What’s in our documentation?

Page 171: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

};});

Options

The defaults.options will be merged with this.options from aura.

Translations

The defaults.translations will be merged with the this.options.translations to overwrite translations of the componentand translated with globalize.

Templates

The defaults.templates will be merged with this.options.templates to overwrite templates of the component and pre-pared with _.template.

Events

The event-hook manages events and creates callable functions to throw or catch aura events. It encapsulate the ‘ cre-ateEventName’ function to build event-names with the this.events.namespace, this.options.instanceName and postFixof the given events.

define(['services/husky/util'], function (util) {

'use strict';

/*** Namespace for events.

** @type {string}

*/var eventNamespace = 'smart-content.categories.';

return {

defaults: {options: {instanceName: 'example'}

},

events: {names: {

initialized: {postFix: 'initialized'},getData: {

postFix: 'get-data',type: 'on'

}},namespace: eventNamespace

},

(continues on next page)

1.4. Bundles 167

Page 172: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

initialize: function () {this.loadData().then(function (data) {

this.data = data;

// emit eventthis.events.initialized(data);

});

// getter for datathis.events.getData(function (callback) {

callback(this.data);}.bind(this));

},

loadData: function () {

// ...

return util.Deferred();}

};});

Note: The event-names will be build with this schema: <namespace>.[<instancename>.]<postFix>.

Types

The events-hook can handle different types of events.

emit - default Uses this.sandbox.emit to emit an aura event.on Uses this.sandbox.on to add a event-listener to a aura event.once Uses this.sandbox.once to add a one-time event-listener to a aura event.

Sticky-Toolbar

The sticky-toolbar extension enables the datagrid list toolbar to stick under the header, when the page will be scrolled.

To enable this extension call set the property stickyToolbar to a non false value. If this value is a number it will beused as the scroll-edge where the toolbar will snap-in. You need this for example if you use tabs in your component tocompensate the height of the tabs container (90 + 50 = 90 for the header and 50 for the tabs-container).

{stickyToolbar: true // or a number like: 140

}

To reset the scroll-container if you rerender your component you can call the this.sandbox.stickyToolbar.reset() method.

168 Chapter 1. What’s in our documentation?

Page 173: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Collaboration

The collaboration-hook initializes collaboration-component. This component is able to communicate with the serverand determine which user is currently working on the same record.

Example

return {collaboration: function() {

return {id: this.options.id,type: 'example'

};},

initialize: function() {render();

},

...}

This basic component initializes the collaboration and shows a warning when a user edits the same record (examplewith the same id).

Sulu-Buttons

The husky-toolbar-component needs to be passed data, which defines what buttons it should render or what happenswhen somebody clicks on a button. Essentially Sulu-Buttons are about passing buttons to the toolbar-component in aneasier and more elegant way than just copying the same buttons all over the place. With Sulu-buttons you can definebuttons in a specific place, extend other buttons and override properties really easily.

Introduction

The admin-bundle contains an aura-extension in which all default buttons are specified as well as methods to getbuttons and add buttons to the pool of default buttons.

An example for such a button is:

{name: 'save',template: {

icon: 'floppy-o',title: 'public.save',disabled: true,callback: function() {

app.sandbox.emit('sulu.toolbar.save', 'edit');}

}}

As you can see a button gets registered with a name and a template, which is the actual button meeting the specificationsof the husky-framework.

1.4. Bundles 169

Page 174: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

The same holds for dropdown-items, for which also defaults are specified in the admin-bundle. For example:

{name: 'delete',template: {

title: 'public.delete',callback: function() {

app.sandbox.emit('sulu.toolbar.delete');}

}}

Retrieve buttons

The aura-extension in the admin-bundle extends every sandbox of a javascript-component with the method sulu.buttons.get. In your own component you can call this function like for example:

var generatedButtons = this.sandbox.sulu.buttons.get({edit: {},save: {

options: {callback: function() {//do something//}}

},settings: {

options: {dropdownItems: {

delete: {}}

}}

}});

The sulu.buttons.get method returns an array of buttons which meet the specification of thehusky-framework. In our example this array contains the template of the edit-button, the templateof the save`-button but with the callback-property replaced with our own one andthe template of the ``settings-button which has the template of the delete-dropdownItem as the onlydropdown-item.

If you want the settings-button two times in the same toolbar with - let’s say - different dropdown-items you can makeuse of the parent property;

var generatedButtons = this.sandbox.sulu.buttons.get({settings1: {

parent: 'settings',options: {

dropdownItems: {delete: {}

}}

},settings2: {

parent: 'settings',options: {

dropdownItems: {

(continues on next page)

170 Chapter 1. What’s in our documentation?

Page 175: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

table: {}}

}}

});

Add your own buttons

Additionally to the sulu.buttons.get method the aura-extension provides the following methods:

• sulu.buttons.add: takes a name and a button-template

• sulu.buttons.dropdownItems.add: takes a name and a dropdownItem-template

• sulu.buttons.push: takes an array of objects which all must contain a name and a template property

• sulu.buttons.dropdownItems.push: takes an array of objects which all must contain a name and atemplate property

• sulu.buttons.getApiButton: takes the name of a button-template and returns the actual template. Canbe used to extend an existing button-template.

So with this methods you can easily add your own buttons and dropdown-items to the pool. These buttons are thenglobally available via the sulu.buttons.get method.

When adding your own button the preferable place to specify them is in a requirejs-component named sulu-buttons.jswithin the extensions-folder of your bundle. Adding the buttons and dropdown-items to the pool should then be donein the js/main.js file of your bundle in which the sulu-buttons.js file is required.

If you want to specify your own button which extends another existing button you can do the following. In this examplethe settings button is extended with a custom title.

var copyOfSettings = app.sandbox.sulu.buttons.getApiButton('settings');copyOfSettings.title = 'My own title';this.sandbox.sulu.buttons.add('my-settings-button', copyOfSettings);

Note: Don’t overuse the possibility to extend an existing button and provide a new one. Extending and providingyour own button should only be done if the same button comes up in multiple places. If you just need to overwritesome properties of a default button in a single-place just use the sulu.buttons.get method.

Include custom css

Sulu provides the package require-css to manage custom css. You can load css files in any require component with theprefix css!. You have to omit the file extension.

require.config({paths: {

sulucontact: '../../sulucontact/js',sulucontactcss: '../../sulucontact/css'

}});

define(['css!sulucontactcss/main'], function(){});

1.4. Bundles 171

Page 176: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

This will include the css file and manage multiple usages. If you want to load more than one css file simply add moreentries in the array.

CKEditor

The build in editor CKEditor has a quite powerful plugin system. It can be used to create additional toolbar-buttonsand dialogs. Sulu gives you the ability to add these as plugins to your bundle.

Example

The code for this example was “borrowed” from http://docs.ckeditor.com/#!/guide/plugin_sdk_sample_1.

1. Create a plugin file

define(function() {return function(sandbox) {

return {// The plugin initialization logic goes inside this method.// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.

→˓pluginDefinition.html#initinit: function( editor ){

// Define an editor command that inserts an abbreviation.// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.editor.html

→˓#addCommandeditor.addCommand( 'abbrDialog',new CKEDITOR.dialogCommand(

→˓'abbrDialog' ) );// Create a toolbar button that executes the plugin command.// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.ui.html

→˓#addButtoneditor.ui.addButton( 'Abbr',{

// Toolbar button tooltip.label: 'Insert Abbreviation',// Reference to the plugin command name.command: 'abbrDialog',// Button's icon file path.icon: this.path + 'images/icon.png'

} );// Add a dialog window definition containing all UI elements and

→˓listeners.// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.dialog.html

→˓#.addCKEDITOR.dialog.add( 'abbrDialog', function ( editor ){

return {// Basic properties of the dialog window: title, minimum size.// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.

→˓dialog.dialogDefinition.htmltitle : 'Abbreviation Properties',minWidth : 400,minHeight : 200,// Dialog window contents.

(continues on next page)

172 Chapter 1. What’s in our documentation?

Page 177: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.→˓dialog.definition.content.html

contents :[

{// Definition of the Basic Settings dialog window tab

→˓(page) with its id, label, and contents.// http://docs.cksource.com/ckeditor_api/symbols/

→˓CKEDITOR.dialog.contentDefinition.htmlid : 'tab1',label : 'Basic Settings',elements :[

{// Dialog window UI element: a text input

→˓field for the abbreviation text.// http://docs.cksource.com/ckeditor_api/

→˓symbols/CKEDITOR.ui.dialog.textInput.htmltype : 'text',id : 'abbr',// Text that labels the field.// http://docs.cksource.com/ckeditor_api/

→˓symbols/CKEDITOR.ui.dialog.labeledElement.html#constructorlabel : 'Abbreviation',// Validation checking whether the field is

→˓not empty.validate : CKEDITOR.dialog.validate.notEmpty(

→˓"Abbreviation field cannot be empty" )},{

// Another text input field for the→˓explanation text with a label and validation.

type : 'text',id : 'title',label : 'Explanation',validate : CKEDITOR.dialog.validate.notEmpty(

→˓"Explanation field cannot be empty" )}

]},{

// Definition of the Advanced Settings dialog window→˓tab with its id, label and contents.

id : 'tab2',label : 'Advanced Settings',elements :[

{// Yet another text input field for the

→˓abbreviation ID.// No validation added since this field is

→˓optional.type : 'text',id : 'id',label : 'Id'

}]

(continues on next page)

1.4. Bundles 173

Page 178: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

}],// This method is invoked once a user closes the dialog

→˓window, accepting the changes.// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.

→˓dialog.dialogDefinition.html#onOkonOk : function(){

// A dialog window object.// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.

→˓dialog.htmlvar dialog = this;// Create a new abbreviation element and an object that

→˓will hold the data entered in the dialog window.// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.

→˓dom.document.html#createElementvar abbr = editor.document.createElement( 'abbr' );

// Retrieve the value of the "title" field from the "tab1→˓" dialog window tab.

// Send it to the created element as the "title"→˓attribute.

// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.→˓dom.element.html#setAttribute

abbr.setAttribute( 'title', dialog.getValueOf( 'tab1',→˓'title' ) );

// Set the element's text content to the value of the→˓"abbr" dialog window field.

// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.→˓dom.element.html#setText

abbr.setText( dialog.getValueOf( 'tab1', 'abbr' ) );

// Retrieve the value of the "id" field from the "tab2"→˓dialog window tab.

// If it is not empty, send it to the created→˓abbreviation element.

// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.→˓dialog.html#getValueOf

var id = dialog.getValueOf( 'tab2', 'id' );if ( id )

abbr.setAttribute( 'id', id );

// Insert the newly created abbreviation into the cursor→˓position in the document.

// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.→˓editor.html#insertElement

editor.insertElement( abbr );}

};} );

}};

};});

174 Chapter 1. What’s in our documentation?

Page 179: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

2. Register your new plugin

In the main.js file of your bundle you can require the newly created plugin and register it through the initializefunction.

sandbox.ckeditor.addPlugin('my-toolbar', // which toolbar should include the button'Abbr', // name of toolbar-button defined in the plugin'abbr', // Name of pluginnew Abbr(app.sandboxes.create('plugin-abbr'))

);

You can now use your plugin in all Ckeditor instances.

Note: To create your overlays or other plugins in our aura-system you can also use the sandbox to start your owncomponent. But you have to be sure that you did stop all your components to reduce the memory-usage and calls tothe event-system.

1.4.2 AudienceTargetingBundle

If you want to display different content on the same URL based on some characteristics of the visitor, you can use theAudienceTargetingBundle for that.

It allows the content manager to define the audience target groups on his own. Each target group has one or multiplerules, which are used to determine the target group of the visitor.

Installation

Enable the bundle in app/AbstractKernel.php:

<?phpabstract class AbstractKernel extends SuluKernel{

// ...

public function reigsterBundles(){

$bundles = [// ...new Sulu\Bundle\AudienceTargetingBundle\SuluAudienceTargetingBundle(),

];

return $bundles;}

}

Add the routes for the administration interface to the routing configuration file (app/config/admin/routing.yml):

# ...sulu_audience_targeting:

resource: "@SuluAudienceTargetingBundle/Resources/config/routing.xml"prefix: /admin/target-groups

(continues on next page)

1.4. Bundles 175

Page 180: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

sulu_audience_targeting_api:resource: "@SuluAudienceTargetingBundle/Resources/config/routing_api.xml"prefix: /admin/api

And the routes for the website in the corresponding configuration file (app/config/website/routing.yml):

# ...sulu_audience_targeting:

resource: "@SuluAudienceTargetingBundle/Resources/config/routing_website.xml"

Finally the cache has to be correctly configured. You have the choice between the Symfony Cache and Varnish.

For the Symfony cache the front controller for the website at web/website.php has to be told to make use of the usercontext feature. You do that by uncommenting the line with the WebsiteCache and adding true as a second parameter:

<?php$kernel = new WebsiteCache($kernel, true);

If you want to use the more powerful Varnish instead, you have to install it on your machine and configure it usinga VCL. The following example can be used for that, however, it requires the header module, which comes with thevarnish-modules package.

vcl 4.0;

import header;

backend default {.host = "127.0.0.1";.port = "8001";

}

sub vcl_recv {if (req.http.Cookie ~ "_svtg" && req.http.Cookie ~ "_svs") {

set req.http.X-Sulu-Target-Group = regsub(req.http.Cookie, ".*_svtg=([^;]+).*→˓", "\1");

} elseif (req.restarts == 0) {set req.http.X-Sulu-Original-Url = req.url;set req.http.X-Sulu-Target-Group = regsub(req.http.Cookie, ".*_svtg=([^;]+).*

→˓", "\1");set req.url = "/_sulu_target_group";

} elseif (req.restarts > 0) {set req.url = req.http.X-Sulu-Original-Url;unset req.http.X-Sulu-Original-Url;

}

unset req.http.Cookie;}

sub vcl_deliver {if (resp.http.X-Sulu-Target-Group) {

set req.http.X-Sulu-Target-Group = resp.http.X-Sulu-Target-Group;set req.http.Set-Cookie = "_svtg=" + resp.http.X-Sulu-Target-Group + ";

→˓expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/;";

return (restart);}

(continues on next page)

176 Chapter 1. What’s in our documentation?

Page 181: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

if (resp.http.Vary ~ "X-Sulu-Target-Group") {set resp.http.Cache-Control = regsub(resp.http.Cache-Control, "max-age=(\d+)",

→˓ "max-age=0");set resp.http.Cache-Control = regsub(resp.http.Cache-Control, "s-maxage=(\d+)

→˓", "s-maxage=0");}

if (req.http.Set-Cookie) {set resp.http.Set-Cookie = req.http.Set-Cookie;header.append(resp.http.Set-Cookie, "_svs=" + now + "; path=/;");

}}

Finally you have to make sure that the bundle is correctly recognized by Symfony. This includes the following steps:

• Clear the Symfony cache with the cache:clear command or manually deleting the cache folder

• Rebuild the translations with the sulu:translate:export command

• Install the new assets using the assets:install –symlink

• Make sure that the users the feature should be enabled for have the correct permissions

Manually set target group

Sulu will try to determine a matching target group based on the rules the content manager defines. But it is alsopossible to set a target group manually. That might be useful if you want to divide visitors into separate target groupsbased on some behavior, e.g. filling out a form, starting a download, etc.

Therefore we have introduced the TargetGroupStore. You can simply call its updateTargetGroupId method and Suluwill do the rest for you. This would like this in an action of a Controller:

<?phpuse Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller {public function indexAction() {

// determine the desired target group based on form values, etc.$targetGroupId = 0;$this->get('sulu_audience_targeting.target_group_store')

->updateTargetGroupId($targetGroupId);}

}

Note: The target group that will be set manually should have quite a high priority, otherwise another higher prioritizedtarget group might override that based on its defined rule.

Create custom rules

The cool thing about target groups are the rules you can define on them, which will automatically evaluated by Sulu.There are a few rules built-in, like a referrer rule, browser rule or a page rule. However, you might still have a veryspecific use case, which requires to implement your own custom rule.

Luckily this possibility is also built-in into Sulu. First of all you have to write your own implementation of theRuleInterface:

1.4. Bundles 177

Page 182: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<?php

namespace Acme\Bundle\Rule;

use Sulu\Bundle\AudienceTargetingBundle\Rule\RuleInterface;

class ExampleRule implements RuleInterface {public function evaluate(array $options){

// return true if the rule is matching, otherwise false}

public function getName(){

// return the name of the rule}

public function getType(){

// return an implementation of the RuleTypeInterface}

}

The interface consists of three different methods, lets have a closer look at each one of them:

The easiest one is getName, whatever you return here will be shown in the rules dropdown.

The getType method returns how the rule is displayed in the admin. This is what the content manager will be facing, ifthis rule was chosen. There are a few possibilities, represented by classes implementing the RuleTypeInterface. Theyusually take some kind of name as constructor parameter, which will be used as key when storing this information ina JSON field in the database. The content of this JSON field is what will be passed to the $options argument of theevaluate method later. Until now there are implementations for Text, Select, KeyValue and for a InternalLink.

The evaluate method will be called for every appearance of the rule in all the target groups, until one of the targetgroups matches. The $options argument will be filled with the information from the conditions the content managerhas configured as already mentioned above. Based on this information you have to define if the current request can beevaluated to true.

Note: In most cases you need to inject other services to your rule, in order to be able to evaluate them in a sensibleway. Quite often this is the RequestStack, which allows you to get the current Request object and allows you to evaluatecertain values against the request.

Finally your implementation has to be registered as service using the sulu.audience_target_rule tag:

<?xml version="1.0" ?><container xmlns="http://symfony.com/schema/dic/services"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.

→˓com/schema/dic/services/services-1.0.xsd"><services>

<service id="acme_bundle.rule"class="Acme\Bundle\Rule\ExampleRule">

<!-- inject whatever services you need --><tag name="sulu.audience_target_rule" alias="acme"/>

</service></services>

(continues on next page)

178 Chapter 1. What’s in our documentation?

Page 183: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

</container>

Note: Mind that the alias of the tag has to be unique.

1.4.3 AutomationBundle

The AutomationBundle provides the functionality for managing automated tasks. This tasks can be used for example topublish or unpublish pages at a specific timestamp. This handlers (like publish) can be implemented by the applicationor any bundle.

Basic Ideas

The basic idea behind this bundle was to enhance the content manager with the possibility to plan when a page willbe online. But also other tasks should be possible. One of many scenarios could be to send a mail after 6 months tocheck the content of the page

As basic implementation for task scheduling and running the PHP-Task Library and the Bundle was used.

Note: Outside the “automation” bundle the PHP-Task Library can be used for recurring or single-time tasks whichare necessary for your application. For more information about the library and the bundle itself take a look at thePHP-Task Documentation.

1.4. Bundles 179

Page 184: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

As recommended run-mode (beside the kernel.terminate Listener) you should use the task:run commandinside a cron-job. For the interval of the cron-job you have to decide how precise the task should run at the giventimestamp. For example if you choose an interval of 1 hour the tasks will only run each hour.

Installation

All of the dependencies will be installed automatically with sulu/sulu. Because of this you only have to registerthe AutomationBundle and the TaskBundle in your Kernel and app/config/admin/routing.yml file.

...

// php-tasknew Task\TaskBundle\TaskBundle(),

// sulunew Sulu\Bundle\AutomationBundle\SuluAutomationBundle(),

...

sulu_automation_api:type: restresource: "@SuluAutomationBundle/Resources/config/routing_api.xml"prefix: /admin/api

sulu_automation:type: restresource: "@SuluAutomationBundle/Resources/config/routing.xml"prefix: /admin/automation

For recommended run mode and other settings you should use the default configuration.

Further Topics

Custom Handler

A custom handler provides functionality which can be scheduled with the automation-tab (see Integration intocustom module). The handler can implement custom logic based on the connected entity (e.g. publish or removepage).

To use a handler in the automation tab it has to implement the interface AutomationTaskHandlerInterfacewhich extends the TaskHandlerInterface from php-task.

<?php

namespace AppBundle\Automation;

use AppBundle\Entity\NewsItem;use Sulu\Bundle\AutomationBundle\TaskHandler\AutomationTaskHandlerInterface;use Sulu\Bundle\AutomationBundle\TaskHandler\TaskHandlerConfiguration;use Symfony\Component\OptionsResolver\OptionsResolver;

class PublishHandler implements AutomationTaskHandlerInterface{

/*** This function will be called inside the task:run command

(continues on next page)

180 Chapter 1. What’s in our documentation?

Page 185: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

* when a scheduled task elapses.

** @param array $workload

** @return string|\Serializable as the result of this handler.

*/public function handle($workload){

$id = $workload['id'];$locale = $workload['locale'];

// TODO implement custom logic}

/*** The given OptionsResolver will be used to validate the workload

* before scheduling the task.

** @param OptionsResolver $optionsResolver

** @return OptionsResolver

*/public function configureOptionsResolver(OptionsResolver $optionsResolver){

return $optionsResolver->setRequired(['id', 'locale'])->setAllowedTypes('id', 'string')->setAllowedTypes('locale', 'string');

}

/*** Returns true if handler supports given class.

** @param string $entityClass

** @return bool

*/public function supports($entityClass){

return $entityClass === NewsItem::class|| is_subclass_of($entityClass, NewsItem::class);

}

/*** Returns the configuration for this handler.

** @return TaskHandlerConfiguration

*/public function getConfiguration(){

// The first parameter will be used as translation key in the// frontendreturn TaskHandlerConfiguration::create('app.publish');

}}

1.4. Bundles 181

Page 186: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<service id="app.automation.publish_handler" class=→˓"AppBundle\Automation\PublishHandler">

<tag name="task.handler"/></service>

Note: If you integrate this into a public bundle you should check the existence of the AutomationBundle in yourextension and omit this definition if it is not registered.

This handler will now be selectable in the automation-tab of the NewsItem.

Integration into custom module

The AutomationBundle provides a simple way to integrate the automation-tab into your own custom module.

1. Handler

Be sure the application provides some handlers for the custom entity-class. This can be ensured by including customhandlers and supporting it via the interface (see Custom Handler).

2. NavigationProvider

Create a new NavigationProvider and register it in the services.xml file (see Using tab navigation).

182 Chapter 1. What’s in our documentation?

Page 187: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

<?php

namespace AppBundle\Admin;

use Sulu\Bundle\AdminBundle\Navigation\ContentNavigationItem;use Sulu\Bundle\AdminBundle\Navigation\ContentNavigationProviderInterface;use Sulu\Bundle\ArticleBundle\Document\ArticleDocument;use Sulu\Bundle\ContentBundle\Document\PageDocument;

class ArticleAutomationContentNavigationProvider implements→˓ContentNavigationProviderInterface{

public function getNavigationItems(array $options = []){

$automation = new ContentNavigationItem('sulu_automation.automation');$automation->setId('tab-automation');$automation->setAction('automation');$automation->setDisplay(['edit']);

// This component is already implemented in the automation-bundle.$automation->setComponent('automation-tab@suluautomation');// The entityClass option will customize the tab for your entity.$automation->setComponentOptions(['entityClass' => NewsItem::class]);

return [$automation];}

}

Note: If you integrate this into a public bundle you should check the existence of the AutomationBundle in yourextension and omit this definition if it is not registered.

3. Use it

After clearing the cache you can see the newly created tab in your custom-module and make use of it.

1.4.4 CollaborationBundle

This bundle activates the collaboration feature of Sulu. It shows a label, when other content managers are currentlyediting the same record. It works with and without websockets, whereby the version with websockets notifies the con-tent editors in real time. If websockets are not activated the first editor opening the record will not see any notificationin case another editor opens the record. However, all the following editors will see a warning containing the usernamesof the editors having the record opened at the time.

The bundle saves this data in the cache, so that it is working in combination with AJAX polling and even continues towork if the websockets has crashed in between. The records are identified by a type, which is a simple string uniquelydescribing the type of record (e.g. page or contact) and the identifier of the record.

The following code snippet shows how the collaboration feature can be activated in any component:

collaboration: function() {if (!this.options.id) {

return;(continues on next page)

1.4. Bundles 183

Page 188: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

}

return {id: this.options.id,type: 'example'

};}

The first statement checks if an id is set and returns early if not. This is what we usually want, since no id indicatesthat a new record is created and there will not be a conflict if two users are adding new records at the same time.

The return value of the method is used by a JavaScript hook, which starts the collaboration component with theseoptions.

1.4.5 ContentBundle

The SuluContentBundle is one of the most central parts of Sulu. It offers the content management functionality ofSulu, which is its most important part.

Drafting

The drafting feature of Sulu allows to work on a draft of a page, which will not be available on the website directly.For this to happen the page has to be published. This document will explain the technical structure behind this featureand how to maintain it.

Structure

In general we use PHPCR to store our unstructured content like pages and snippets. We use two different PHPCRworkspaces, which can be compared to schemas in relational databases. There is one for all the drafts and one for thealready published content.

If a document is saved using the persist method from the DocumentManager it will only be saved inthe draft workspace and therefore not being available on the website. When the publish method from theDocumentManager is called the content of the passed document will be saved to the live workspace, and though beavailable on the website.

Sulu also maintains two different search indexes for pages. There is one for the published data, named afterthe scheme sulu_page_<webspace>-<locale>-i18n_published and one for the drafting data calledsulu_page_<webspace>-<locale>-i18n.

Configuration

The sessions are configured in the sulu_document_manager.sessions option. Sessions can havenames, which can be used to reference them in the sulu_document_manager.default_session andsulu_document_manager.live_session. This configuration will tell the system where to save the contentfor the drafts and live documents. See the following configuration for an example:

sulu_document_manager:default_session: defaultlive_session: livesessions:

(continues on next page)

184 Chapter 1. What’s in our documentation?

Page 189: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

default:backend: "%phpcr_backend%"workspace: "%phpcr_workspace%"username: "%phpcr_user%"password: "%phpcr_pass%"

live:backend: "%phpcr_backend%"workspace: "%phpcr_workspace%_live"username: "%phpcr_user%"password: "%phpcr_pass%"

Note: Remember to set the default session differently for both Kernels. The AdminKernel should have the defaultsession configured as default_session, whereby the WebsiteKernel should have the live session configured asdefault_session. So the documents are read from the correct workspace for both Kernels.

Session handling

Most of the commands with the doctrine:phpcr: take a --session parameter, which specifies which of theconfigured sessions should be used. So if you want to start e.g. the PHPCR Shell:

app/console doctrine:phpcr:shell --session=live

If you want to start the PHPCR Shell with the default_session you can simply omit the --session parameter.

Search handling

As already described the pages are stored in two different search indexes. So if you want to reindex the content youhave to address both of these indexes. You do so by using two different console commands:

app/console massive:search:reindex # Reindex the default sessionapp/webconsole massive:search:reindex # Reindex the live session

Versioning

The versioning feature of Sulu will create a new version each time a page has been published. Sulu will also show atable in the settings tab of each page, showing the already created version. From this table old versions can be restored,which will result in a new draft.

Mind that this feature is only available for Jackrabbit, because Jackalope does currently not support versioning for itsDoctrine DBAL transportation layer.

Configuration

The sulu_document_manager.versioning.enabled flag decides if versioning is activated.

sulu_document_manager:versioning:

enabled: true

1.4. Bundles 185

Page 190: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

ContentRepository

The content-repository was developed to query the content raw-data. The developer can decide which properties willbe loaded.

The main goal of the repository is to centralize the core features ghost, shadow and internal links. These things willbe handled full automatically. If you are using this repository.

Usage

There are two ways to use it. One in the front-end over an rest-api and one with a backend service.

API

The URL of the API is /admin/api/nodes or /admin/api/nodes/<uuid>.

The mandatory parameters are:

Name Example Descriptionlocale de Localization which will be used.webspace sulu_io Webspace from which content will be loaded.fields ‘’ Comma separated list of properties.

Note: If the parameter fields is not set the content will be resolved with the slow legacy system (which is deprecated).

You can specify following optional parameters:

Name Default Descriptionexclude-ghosts false If true ghost will be filtered.exclude-shadows false If true shadows will be filtered.

Over the mapping you can specify which properties will be loaded by the repository (for example: ‘title,order,article’).

The list endpoint also accepts a parent parameter. If this parameter is set the given page will be used to query forchildren else the webspace root is default.

The single endpoint also accepts a tree flag. If it is true all parents and siblings are returned else only the requestedpage will be returned.

Service

The id of the service is sulu_content.content_repository. The methods can be used as described in the phpdocs.

The mapping variable contains information for the mapping process.

Name DescriptionhydrateShadow if this is false no shadow pages will be returned.hydrateGhost if this is false no ghost pages will be returned.followInternalLink if this is false the link will not be resolved.properties List of hydrated properties

186 Chapter 1. What’s in our documentation?

Page 191: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

You can build this mapping over the mapping builder:

$mapping = MappingBuilder::create()->setHydrateGhost()->setHydrateShadow()->addProperties(['title'])->getMapping();

Security

Every page has its own permission tab, which is integrated like described in Securing your application. The settingsplaced in this tab has an influence on the UI of Sulu. The UI might also be adapted due to the permissions defined inSecurity Contexts (see the documentation at Security for more details).

The form adapts to the permissions by changing its toolbar. The delete option in the edit dropdown is only availableif the user has the delete permission on this page. The other toolbar items are only available if the user has the editpermission.

The column navigation for the content is a bit more complicated. The entire tree is always visible, but the availablefunctionality is changing based on the permissions of the page. The delete permission is tied to the delete item in theoption dropdown. The edit permission is required to move and sort the page in the content tree. For sorting the pagethe edit permission of all siblings and the parent page is also required.

For copying a page the user has to have the view permission on the source page.

In addition to that the icon appearing on hovering one of the items in the content tree depends on its permission. If theuser is allowed to edit the page a pencil shows up, in case the user has only the permission to view the page an eyes isshown instead. In case the user has no permission at all, there also will not appear any icon on hovering.

1.4.6 HashBundle

The SuluHashBundle adds an easy to use solution for protecting entities and documents from being overridden byaccident. This can happen if two users open the same form in the user interface. If the user which opened the formlater saves its changes first, the later save will cause the changes to be overridden.

Serialization

The HashSerializeEventSubscriber is the most important part of the HashBundle. The subscriber’s job is to add thehash of the object to the serialized representation being delivered in the response. So the serializer has to be used todeliver the response with the hash value automatically.

Hashing

The HashSerializeEventSubscriber uses the AuditableHasher for hashing documents and entities. This hasher calcu-lates a hash based on the user who modified a document and the time when that change happened. So this only worksif the AuditableInterface (for entities) or the AuditableBehavior (for documents) is implemented in the given object.

Validation

This is usually done by the controller itself. The SuluHashBundle comes with a service calledsulu_hash.request_hash_checker, which has a checkHash method taking the request, the object and the identifierof an object (since the decoupled component cannot know how to get the identifier of the object).

1.4. Bundles 187

Page 192: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

This method will extract the _hash parameter from the request and compare it with a newly created hash from thepassed object, and throw an InvalidHashException in case the hashes do not match. The SuluHashBundle alsoprepends the configuration of the FOSRestBundle so that it recognizes the Exception and returns a response codewith the status code 409 (CONFLICT). It also adds a code of 1103 to the Response, so that certain actions like show-ing an overlay asking if the changes should be overridden are possible.

The hash checker also checks the value of the query parameter force, which allows to override the object even if it haschanged in the mean time.

The following code snippet shows how the hash checker can be included in a controller:

$this->get('sulu_hash.request_hash_checker')->checkHash($request, $object, $object->getId());

1.4.7 HttpCacheBundle

The SuluHttpCache bundle provides tight integration between Sulu and HTTP caching proxies.

Handlers

All of the functionality of the Sulu HTTP cache implementation is encapsulated in “handlers”. Handlers are responsi-ble primarily for handling the caching of Sulu Structure objects (e.g. Page and Snippet classes).

Most handlers will need to talk to a HTTP proxy cache implementation. The cache implementations are provided bythe FOSHttpCache component. Examples of handlers which do not need to talk to a cache implementation are theDebugHandler and the PublicHandler.

Handler classes implement the HandlerInterface and are further specialized with three further interfaces:

• HandlerFlushInterface: This handler is capable of being “flushed” - this will normally be a proxy forProxyClient#flush.

• HandlerInvalidateInterface: This handler can invalidate the cache.

• HandlerUpdateResponseInterface: This handler can update the response.

Aggregate Handler

Implements: flush, invalidate and update response.

The aggregate handler is special as it aggregates other handlers so that you can apply multiple handlers at the sametime.

This is the default handler and by default it will aggregate all enabled handlers.

Debug Handler

Implements: update response.

The debug handler simply adds debug information to your response:

• x-sulu-handler: The handlers which are enabled

• x-sulu-proxy-client: The name of the proxy client being used

• x-sulu-structure-type: The structure type for which the response is being returned

188 Chapter 1. What’s in our documentation?

Page 193: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• x-sulu-structure-uuid: The UUID of the Structure

• x-sulu-page-ttl: Send the TTL (time to live) to the proxy client with the header X-Resverse-Proxy-TTL(which is defined as a constant in the Sulu\Component\HttpCache class).

For example:

x-sulu-handlers: paths, public, debugx-sulu-proxy-client: symfonyx-sulu-structure-type: OverviewPageCachex-sulu-structure-uuid: 22a92d46-74ab-46cc-b47c-486b4b8a06a7x-sulu-page-ttl: 2400

Url Handler

Implements: flush, invalidate and update response.

The url handler will invalidate all the URLs associated with the subject Structure when it is updated. For example,if a page is located at http://sulu.lo/de/my/page and http://sulu.lo/en/my/page then these twoURLs will be purged in the cache, and their contents will be updated when they are next requested.

Note: When using the url handler you need to be aware that pages which reference the structure being invalidatedwill not be updated - for example pages which aggregate the subject structure in a SmartContent content type.

Public Handler

Implements: update response.

The public handler adds generic caching information to the response which will be consumed by the users web browser.For example a max-age time of 3600 will instruct the users browser to locally cache the page for one hour beforerequesting it from the server again.

The following are example headers added by the public handler (the X-Reverse-Proxy header will normally beremoved by the cache implementation):

Cache-Control: max-age=240, public, s-maxage=240X-Reverse-Proxy-TTL: 2400

Tags Handler

The tags handler is the most comprehensive cache invalidation strategy, it will invalidate both the URLs of the structureand the URLs of the pages which display references to the structure. . It must be used in conjunction with a proxyclient which supports Banning. Currently this handler can only be used with Varnish.

This handler works by sending all of the UUIDs of the structures which are contained in a page response to the proxyclient. The proxy client can then store this information along with the cached HTML response.

When you update any structure in the admin interface it will instruct the HTTP proxy to purge all the caches whichhave a reference to the UUID of the structure you have updated.

Example header sent by the tags handler (which will be removed by varnish):

X-Cache-Tags: 22a92d46-74ab-46cc-b47c-486b4b8a06a7,cf4a07fe-91d0-41be-aed8-→˓b1c9ee1eb72a

1.4. Bundles 189

Page 194: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

This header will be written at the end of the response by using the Reference Store. This service collects the enti-ties/documents which was used to render the page.

Proxy Clients

Symfony Http Cache

The Symfony HTTP cache is the default caching client for Sulu CMF. It is integrated directly into Sulu.

It works by “wrapping” the kernel. You can find it in the website front controller web/website.php:

// web/website.php// ...

// Comment this line if you want to use the "varnish" http// caching strategy. See http://sulu.readthedocs.org/en/latest/cookbook/caching-with-→˓varnish.htmlif (SYMFONY_ENV != 'dev') {

require_once __DIR__ . '/../app/WebsiteCache.php';$kernel = new WebsiteCache($kernel);

}

It will need to be disabled when using varnish.

Varnish

The varnish proxy client is provided by the FOSHttpCache component.

See Caching with Varnish for more information about setting up varnish.

Default configuration

# Default configuration for extension with alias: "sulu_http_cache"sulu_http_cache:

default_handler: aggregate

# Configuration for structure cache handlershandlers:

aggregate:enabled: true

# Handlers to aggregate, e.g. all or any of tags, path, publichandlers: []

public:enabled: falsemax_age: 300shared_max_age: 300

# Use the dynamic pages cache lifetime for reverse proxy serveruse_page_ttl: true

url:enabled: false

tags:

(continues on next page)

190 Chapter 1. What’s in our documentation?

Page 195: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

enabled: falsedebug:

enabled: falseproxy_client:

symfony:enabled: false

varnish:enabled: false

# Addresses of the hosts Varnish is running on. May be hostname or ip,→˓and with :port if not the default port 80.

servers: # Required

# Prototypename: ~

# Default host name and optional path for path based invalidation.base_url: null

1.4.8 MarkupBundle

The MarkupBundle provides the feature of extending output formats as with different so called tags. These tags willbe automatically parsed and replaced before the response will be sent.

Example

This example contains a sulu-related example. The tag sulu:link represents a link to another page. This tag willbe replaced via a valid anchor where the href attribute contains the UUID of the page.

<html><body>

<sulu:link href="123-123-123" title="test-title" /></body>

</html>

Results into:

<html><body>

<a href="/test" title="test-title">Page Title</a></body>

</html>

Core Tags

sulu:link

The link tag provides the possibility to link to other pages by uuid. This uuid will be validated at response time andreplaced by a proper anchor tag.

1.4. Bundles 191

Page 196: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Example

<sulu:link href="123-123-123" title="test-title" /><sulu:link href="123-123-123" target="_blank">Link Text</sulu:link>

Results into:

<a href="/test" title="test-title">Page Title</a><a href="/test" title="Page Title" target="_blank">Link Text</a>

sulu:media

The media tag provides the possibility to create download links for medias.

Example

<sulu:media id="1" title="Title"/><sulu:media id="1" title="Title">Link-Text</sulu:media><sulu:media id="1">Link-Text</sulu:media>

Results into:

<a href="/media/1/download/image.jpg?v=1" title="Title">Media-Title</a><a href="/media/1/download/image.jpg?v=1" title="Title">Link-Text</a><a href="/media/1/download/image.jpg?v=1" title="Media-Title">Link-Text</a>

Extending

To enable replacement of your custom tags you can define a service which implements the TagInterface.

class LinkTag implements TagInterface{

/*** Returns new tag with given attributes.

** @param array $attributes attributes array of each tag occurrence.

** @return array Tag array to replace all occurrences.

*/public function parseAll($attributesByTag) {

$result = [];foreach($attributesByTag as $tag => $attributes) {

$url = ; // load url via uuid from document-manager$pageTitle = ...; // load page-title via uuid from document-manager

$result[$tag] = sprintf('<a href="%s" title="%s">%s</a>', $url,→˓$pageTitle, $attributes['content']);

}

return $result;}

}

192 Chapter 1. What’s in our documentation?

Page 197: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

When registering your service simple add the tag <tag name="sulu_markup.tag" tag="link"/>.

Note: In combination with the plugin-system of the CKEditor you can easily provide a user-interface to manage yourcustom tag.

Namespaces

Namespaces will be used to find tags with a special behavior. The default namespace is sulu, but you can registeryour own namespace by adding a new service and register your TagInterface implementations with this newnamespace.

<service id="app.html_extractor"class="Sulu\Bundle\MarkupBundle\Markup\HtmlTagExtractor">

<argument type="string">custom-namespace</argument>

<tag name="sulu_markup.parser.html_extractor"/></service>

<service id="app.tag" class="AppBundle\CustomTag"><tag name="sulu_markup.tag" namespace="custom-namespace"

tag="custom-tag" type="html" /></service>

With this definitions you can use <custom-namespace:custom-tag/> in your response.

1.4.9 MediaBundle

The MediaBundle is responsible for the handling of all media assets in Sulu. It also offers functionality for resizingand scaling images automatically. Assets can be grouped in nestable collections for better organization.

Security

Separate permissions can be applied to every collection. These permissions also apply to the assets contained in thecollection.

So the view permission is required to see the collection itself in the navigation and its assets in any available view inthe system.

The add permission is needed to upload new assets and add new subcollections to a collection. With the edit permissionthe user is allowed to edit the title and other attributes of the collections and its containing assets, and finally the deletepermission is required to delete assets and entire collections.

1.4.10 PersistenceBundle

The Sulu Persistence bundle provides extensions for managing Doctrine entities and their repositories.

1.4.11 PreviewBundle

The PreviewBundle provides the feature of preview for custom-entities. It is build on top of the route-bundle and canonly be introduced for entities which have a RouteDefaultsProvider.

1.4. Bundles 193

Page 198: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

This Provider will be used to determine which controller should be used to render the HTML of the entity. This HTMLshould contain the definition property=”. . . ” which will be used to find the place where the content takes place. Seethe documentation for that in How to include live-preview in my template?.

Example

This is a simple (and not complete) example of a news-entity form.

Sulu-Admin form

define(['services/sulupreview/preview'], function(Preview) {

return {

header: { ... },

layout: function() {return {

content: {width: 'fixed',leftSpace: true,rightSpace: true

},sidebar: (!!this.options.id) ? 'max' : false

}},

initialize: function() {this.render();

this.sandbox.on('sulu.toolbar.save', this.save.bind(this));this.preview.bindDomEvents(this.$el);

},

render: function() { ... },

save: function(action) { ... },

loadComponentData: function() {if (!this.options.id) {

return {};}

return this.sandbox.util.load(this.options.url).done(function(data) {this.preview = Preview.initialize({});this.preview.start(

'AppBundle\\Entity\\News', // your entity-classthis.options.id,this.options.locale,data

);

return data;});

(continues on next page)

194 Chapter 1. What’s in our documentation?

Page 199: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

}};

When binding DOM Events by calling this.preview.bindDomeEvents(this.$el) you need to add the class preview-update to your elements.

PreviewObjectProvider

The PreviewObjectProvider is the interface which will be used to load, bind and de/serialize the object.

<?php

namespace News\Preview;

use Sulu\Bundle\PreviewBundle\Preview\Object\PreviewObjectProviderInterface;

class NewsObjectProvider implements PreviewObjectProviderInterface{

public function getObject($id, $locale){

return ...; // load the object by id}

public function getId($object){

return $object->getId();}

public function setValues($object, $locale, array $data){

... // bind data-array to the object}

public function setContext($object, $locale, array $context){

... // context change is for example a template change (e.g. in pages or→˓articles)

}

public function serialize($object){

return serialize($object);}

public function deserialize($serializedObject, $objectClass){

return unserialize($serializedObject);}

}

After registering a service with this class and the tag <tag name=”sulu_preview.object_provider”class=”AppBundleEntityNews”/> you should be able to see the live-preview in your form.

1.4. Bundles 195

Page 200: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.4.12 RouteBundle

The RouteBundle provides the feature of managing website-routes for custom-entities. These routes will be generatedby calling the RouteManager which uses a configurable schema for creating the route-path.

Every Entity can be combined with a route. You only have to call the create method of the servicesulu_route.manager.route_manager with an entity implementing the RoutableInterface.

The update method will update the route-path with the configured schema and hold “history” (301 redirects) routesfor the old routes.

To resolve the route on request you have to provide a RouteDefaultsProvider which defines the controller and attributesof the route.

Further Topics

Custom Route-Generator

The RouteBundle enables you to define custom route generators. These generators will be called every time you createor update a route for the given entity. For each entity you can define which generator will be used.

The following example will generate a route by using the core route generator, but you can define route schemas foreach entity type.

<?php

class RouteGenerator implements RouteGeneratorInterface{

/*** @var RouteGeneratorInterface

*/private $routeGenerator;

/*** {@inheritdoc}

*/public function __construct(RouteGeneratorInterface $routeGenerator){

$this->routeGenerator = $routeGenerator;}

/*** {@inheritdoc}

*/public function generate($entity, array $options){

$type = $entity->getType();

if (!array_key_exists($type, $options)) {throw new \Exception(sprintf('Route-schema for type "%s" not configured!',

→˓ $type));}

return $this->routeGenerator->generate($entity, ['route_schema' => $options[→˓$type]]);

}

(continues on next page)

196 Chapter 1. What’s in our documentation?

Page 201: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

/*** {@inheritdoc}

*/public function getOptionsResolver(array $options){

// allow all options.return (new OptionsResolver())->setDefined(array_keys($options));

}}

Register this as a service with the tag sulu.route_generator (with alias custom for example) and use this alias in theconfiguration:

sulu_route:mappings:

AppBundle\Entity\Recipe:generator: schemaoptions:

route_schema: /{translator.trans('recipe')}/{object.getTitle()}AppBundle\Entity\Example:

generator: customoptions:

type1: /example/{object.getName()}type2: /example/{object.getTitle()}

Example

This example will create a simple example-entity which will be available on the website with an own route.

Entity

First extend your entity with the properties id, locale and a new many-to-one relation route:

reciepe.orm.xml:

...

<field name="id" type="int"/><field name="locale" type="string" length="5"/>

<many-to-one field="route" target-entity="Sulu\Bundle\RouteBundle\Entity\Route"><cascade>

<cascade-all/></cascade><join-column name="idRoutes" referenced-column-name="id" nullable="true" on-

→˓delete="SET NULL"/></many-to-one>

Reciepe.php:

namespace AppBundle\Entity;

use Sulu\Bundle\RouteBundle\Model\RoutableInterface;use Sulu\Bundle\RouteBundle\Model\RouteInterface;

(continues on next page)

1.4. Bundles 197

Page 202: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

(continued from previous page)

class Recipe implements RoutableInterface{

/*** @var string

*/private $title;

/*** @var RouteInterface

*/private $route;

public function getTitle() { ... }

public function setTitle() { ... }

/*** {@inheritdoc}

*/public function getId() { ... }

/*** {@inheritdoc}

*/public function getLocale() { ... }

/*** {@inheritdoc}

*/public function setRoute(RouteInterface $route) { ... }

/*** {@inheritdoc}

*/public function getRoute() { ... }

}

Route-Schema

Configure the route-schema in the file app/config/config.yml:

sulu_route:mappings:

AppBundle\Entity\Recipe:generator: schemaoptions:

route_schema: /{translator.trans('recipe')}/{object.getTitle()}

Note: You can use the translator in the schema to create translated routes.

198 Chapter 1. What’s in our documentation?

Page 203: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

RecipeController

namespace AppBundle\Controller;

use AppBundle\Entity\Recipe;use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class RecipeController extends Controller{

public function indexAction(Recipe $recipe){

return $this->render('AppBundle:website:recipe.html.twig', ['recipe' =>→˓$recipe]);

}}

RouteDefaultsProvider

namespace AppBundle\Routing;

use AppBundle\Entity\Recipe;use AppBundle\Entity\RecipeRepository;use Sulu\Bundle\RouteBundle\Routing\Defaults\RouteDefaultsProviderInterface;

class RecipeRouteDefaultProvider implements RouteDefaultsProviderInterface{

protected $recipeRepository;

public function __construct(RecipeRepository $recipeRepository){

$this->recipeRepository = $recipeRepository;}

public function getByEntity($entityClass, $id, $locale, $object = null){

return ['_controller' => 'AppBundle:Recipe:index','recipe' => $object ?: $this->recipeRepository->find($id, $locale),

];}

public function isPublished($entityClass, $id, $locale){

return true;}

public function supports($entityClass){

return $entityClass === Recipe::class;}

}

Register this class as a service with the tag <tag name=”sulu_route.defaults_provider”/>.

After that the entity is ready to get a route. To create a route for the new entities simple call the save method of theservice sulu_route.manager.route_manager.

1.4. Bundles 199

Page 204: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Note: To update already existing entities you can run the command bin/console sulu:route:update AppBundle:Recipewhich updates or creates the route for all the entities of this type.

1.4.13 SearchBundle

The SuluSearchBundle is mainly an integration of the MassiveSearchBundle into Sulu. There is also the Mas-siveSearchBundle Documentation explaining this Bundle in more detail. This documentation is also valid for usingit with Sulu, although there are some extensions made by the SuluSearchBundle. It offers a controller to provide aweb API to search through the system, adds more fields - like creator and changer - to the search documents and alsohandles the security provided by Sulu. But the most important thing is that it also contains the administration userinterface.

Configuration

The configuration of this bundle contains some more metadata about each index created by the MassiveSearchBundle.There are two optional values, meaning both of them could be omitted:

• name: Can contain a name for the index, which will be used in the UI. Useful if the index represents somethinga non-translatable literal can describe.

• security_context: This setting is used to describe which security context (see SecurityBundle) the userhas to have view permission in, in order to search through this index.

So a sample configuration would look like this:

sulu_search:indexes:

contact:security_context: sulu.contacts.peoplecontexts: []

The values under indexes, namely contact in this example, result from the index tag in the mapping configu-ration. The value of security_context indicates that the user has to have the permission to view the sulu.contacts.people security context to see result from the contact index. The contexts configuration isoptional, and can be used to filter the indexes in an application. This is used e.g. for the search in the admin.

Note: It is also possible to use PrependExtensions or in more complicated cases to change the value of thesulu_search.indexes parameter in a CompilerPass. Actually that is what most of the Sulu bundles are do-ing to minimize the configuration effort of the application.

That would be enough for the user to retrieve some search results, but a click on these results would not lead to thecorresponding form. So the last missing step is to tell the system how to load this form when clicking on a search result.This has to be done in the main Javascript entry point of the bundle, which is located at Resources/public/js/main.js. A line like the following has to be added to the initialize method:

app.sandbox.urlManager.setUrl('contact','contacts/contacts/edit:<%= id %>/details'

);

200 Chapter 1. What’s in our documentation?

Page 205: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

This line tells the urlManager, which is responsible for creating the URLs for the search, how the entry from thesearch has to be resolved. The variables from the search document can also be used in the template, which is passedas the second argument.

For more complex cases there are two more parameters in the setUrl function, which are shown in the next example.

sandbox.urlManager.setUrl('page',function(data) {

return 'content/contents/<%= webspace %>/<%= locale %>/edit:<%= id %>/content→˓';

},function(data) {

return {id: data.id,webspace: data.properties.webspace_key,url: data.url,locale: data.locale

};},function (key) {

if (key.indexOf('page_') === 0) {return 'page';

}}

);

The first argument is again the name of the index, the second one a function, returning a string for the Javascripttemplate engine. The third is a handler returning a new object from the given data, which will passed to the templateengine in combination with the string being returned from the second argument. Finally, the fourth argument is thekey modifier, which takes the name of the index, and can modify it, before it is compared with the first argument fromall setUrl calls. This is especially useful if the ExpressionLanguage is used for the generation of the index name.

Templating

The SuluSearchBundle has a WebsiteSearchController, which loads the template from the currently loaded webspace.It therefore uses the RequestAnalyzer, and asks the webspace for its template of type search. This template can thenbe defined for every webspace in its XML configuration:

<templates><template type="search">ClientWebsiteBundle:views:search.html.twig</template>

</templates>

See Setup a Webspace for more details.

Reindexing

Re-indexing is the process of reading all of the documents in the system and regenerating their search records. This isnecessary when changes are made to the metadata and it is desirable to propagate these changes over all of the indexeddocuments / entities in the system – or when you import new data (e.g. from a backup) and need to index that data.

To re-index all entities (Contacts, Media, etc.) and documents (Pages, Snippets) simply run the following:

$ ./app/console massive:search:reindex --env=prod

1.4. Bundles 201

Page 206: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Warning: At the moment it is required to also execute ./app/webconsole massive:search:reindex –env=prod toreindex the pages also for the website.

This may take anywhere between a minute and several hours depending on how much data you have in your system.

To increase speed and reduce memory consumption:

• Use the --env=prod (see note below) switch to force the production settings: This will reduce logging andincrease speed and lead to lower memory consumption.

• Ensure that the document manager has the debug: false option. This reduces logging dramatically.

• Use PHP 7: This will increase indexing speed up to 10 times.

To recover if the process is interrupted:

• You may resume the task simply by running it again.

• Use the --provider option to limit the reindexing to a certain reindex provider, for example--provider=doctrine_orm.

Important: In recomending the prod environment we assume that you have not changed the default environmentconfiguration. The important point is that logging increases memory consumption and should be disabled.

1.4.14 SecurityBundle

The SuluSecurityBundle is responsible for protecting different data and areas of the application. Therefore it makesuse and enhances the standard security mechanisms of Symfony.

Structure

Every Sulu user is linked to a specific Sulu contact from the SuluContactBundle. In addition to that a user can havesome roles and groups assigned, whereby a group can consist of multiple other groups and roles.

Every role has to be part of a certain system. Different systems can be registered via the security contexts, which areexplained later. These systems correspond to different applications handled by Sulu. So by default there is only theSulu system. A user is only enabled to login into Sulu, if he has at least one role with the Sulu system assigned.

The users provide two different flags. The locked flag signalizes that a user has been locked by an administrator forsome reason. As soon as this flag is set, the user can’t login to the system anymore. The second flag is called enabled,and will be set to true by default. This flag is only important if you have implemented your own registration process.In the case you want to use a double opt-in mechanism you can set this flag to false on the registration, and toggle it,e.g. when the user clicks on a link in an email. As long as the enabled flag is set to false, the Sulu-Admin offersyou a button to enable the user.

Security contexts

Every application can define its own security contexts, which will then be available in the list of security contexts, onwhich access can be granted or denied. Have a look at Securing your application to see an example.

These contexts are used to control the access to different areas of the application, e.g. if the user is allowed to editcontent at all.

The following permissions are distinguished:

202 Chapter 1. What’s in our documentation?

Page 207: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

VIEW Permission to see data the given contextADD Permission to add new data to the given contextEDIT Permission to edit already existing data in the given contextDELETE Permission to delete data in the given contextARCHIVE Permission to archive data in the given contextLIVE Permission to publish data in the given contextSECURITY Permission to grant or deny access on data in the given context

All the permission values are encoded in a bitmask and saved in a permission object, which has a link to a role. Thisway it is easily possible to evaluate if a user has access to a security context by checking if one of his roles grantsaccess.

Access Control Manager

The AccessControlManager is responsible to set permissions on specific objects. Since this is not totally de-coupled from the entity being protected, there is the possibility to register multiple AccessControlProvider.This is simply a service implementing the AccessControlProviderInterface tagged with sulu.access_control.

The task of this class is to save the permission information into the correct database. This is important, be-cause otherwise it would not be possible to paginate lists considering permissions of these entities in an easy andperformant way. There are already two implementations of the AccessControlProviderInterface, thePhpcrAccessControlProvider handling the permission storing for PHPCR and therefore for our content sec-tion, and the DoctrineAccessControlProvider, which can be used in combination with any Doctrine en-tity. The entity only has to implement the SecuredEntityInterface to signalize that it can be used with theDoctrineAccessControlProvider.

Note: The AccessControlManager is used by some other components, especially by thePermissionController, which handles the requests from the reusable permission tab, and theSecurityContextVoter from Sulu.

Checking security

Sulu offers a SecurityChecker enabling the developer to easily check if a given SecurityCondition isgranted for the currently logged in user. The security condition consists of the already mentioned security context, anobject type and id for decoupling from real objects for performance reasons, and the locale to check the permission in.The SecurityChecker uses the Symfony AccessDecisionManager, which calls all security voters includingthe SecurityContextVoter.

This voter will check if the user is allowed to perform the given action (the permissions already listed above) in thegiven context in the given locale. If the object type and id are also passed the permissions of the security contextsfrom the role might be overridden by the permissions from this specific object (which are handled by the previouslymentioned AccessControlManager).

1.4.15 WebsiteBundle

The SuluWebsiteBundle is one of the most central parts of Sulu. It offers the functionality to show content on awebsite.

1.4. Bundles 203

Page 208: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Reference Store

The reference-store is a service which collects ids of entities/documents which are used to render a page. This ids willbe used for example in the caching component HttpCacheBundle.

Architecture

Each type of content register its own service which implements ReferenceStoreInterface or with the defaultimplementation Sulu\Bundle\ContentBundle\ReferenceStore\ReferenceStore.

The service sulu_website.reference_store_pool collects the services with the tag sulu_website.reference_store and use the alias attribute to identify them.

To register a loaded entity use the concrete store (e.g. sulu_content.reference_store.content or yourown service) and call the method add to append the id of the entity.

Example

<service id="app.reference_store.example"class="Sulu\Bundle\WebsiteBundle\ReferenceStore\ReferenceStore">

<tag name="sulu_website.reference_store" alias="example"/></service>

$exampleReferenceStore = $container->get('app.reference_store.example');$exampleReferenceStore->add(1);

$referenceStore = $container->get('sulu_website.reference_store');var_dump($referenceStore->getStore('example')->getAll());

// prints// array(1) {// [0] =>// int(1)// }

When you get in touch with another bundle of Sulu, which is not documented in this place. Tell us. We’ll provide thedocumentation.

1.5 Developer Guide

Here’s how we work at the Sulu team. We are really open to Pull Requests. It is also a aim to us to ensure code qualityand guarantee some consistency in the project. So if you want to contribute to the Sulu project keep our developersguide in mind.

Developers need to know:

1.5.1 Contributing

All contributions are welcome!

We try and stand on the shoulders of giants, this means that we generally copy the existing coding and documentationstandards of the Symfony project (with some minor differences).

204 Chapter 1. What’s in our documentation?

Page 209: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.5. Developer Guide 205

Page 210: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Contributing code

All contributed code must abide by the Symfony coding standards. But don’t let this be an obstacle to contributing.When in doubt, copy the standards of the code that you are changing.

After you have submitted your PR our team will review the code.

When making a pull request please refer to the Creating a Pull Request article.

Contributing documentation

Again see the Symfony documentation standard as a guide, but when in doubt just submit the PR and we will reviewit.

Creating a Pull Request

When creating a pull request:

• Clone the Sulu repository from git.

• Add a branch and name it after the following format: <type>/<description>, where type is one offeature, bugfix, hotfix or enhancement. For example: feature/what-my-pr-does. Note thatdashes should be used instead of spaces (not underscores).

• Add your feature/bugfix/enhancement.

• Create atomic and logically separate commits (use the power of git rebase to have a clean and logicalhistory).

• Write good commit messages (see the tip below).

• Write tests.

• Test your Code.

• Add a line in the format of “<type> <pr-number> [<affected bundle or component>] <description>” to theCHANGELOG.md file in the root directory.

• In case you do changes that break backwards compatibility you also have to add a description to the UPGRADE.md file in the root directory.

• Use a meaningful name for the pull request (see the tip below).

• Create the pull request as soon as possible.

• In addition to a pull request to the code repository, you should also send a pull request to the documentationrepository to update the documentation when appropriate.

Tip: A good commit message is composed of a summary (the first line), optionally followed by a blank line and amore detailed description. Use a verb (Fixed ..., Added ..., . . . ) to start the summary and don’t add a periodat the end.

If you are a member of the Sulu organization you should also:

• Add a label for the type of the PR:

• feature: provides a new feature

• enhancement: the PR improves existing features

206 Chapter 1. What’s in our documentation?

Page 211: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• bugfix: provides a bug fix

• hotfix: the PR is a bugfix that should be made against a stable branch (e.g. master)

Template

When creating a pull request on GitHub, there is already a pre filled template for you. The template is divided inseveral parts.

Checklist

The pull request must include the following checklist at the top to ensure that contributions may be reviewed withoutneedless feedback loops and that your contributions can be included into Sulu as quickly as possible:

An example submission could now look as follows:

| Q | A| ------------------ | ---| Bug fix? | yes| New feature? | no| BC breaks? | no| Deprecations? | yes| Fixed tickets | fixes #1243, fixes #1443| Related issues/PRs | -| License | MIT| Documentation PR | sulu/sulu-docs#153

Some answers to the questions trigger some more requirements:

• If you answer yes to “Bug fix?”, check if the bug is already listed in the Sulu issues and reference it/them in“Fixed tickets”. These should be of the form fixes #123, fixes #321.

• If you answer yes to “New feature?”, you must submit a pull request to the documentation and reference it underthe “Documentation PR” section.

• If you answer yes to “BC breaks?”, the pull request must contain updates to the UPGRADE.md file.

• If you answer yes to “Deprecations?”, the pull request must contain updates to the UPGRADE.md file.

• If the “license” is not MIT, just don’t submit the pull request as it won’t be accepted anyway.

Note: When an issue number is prefixed with fixes it tells GitHub to automatically close the referenced ticket whenthe pull request is merged

What’s in this PR?

Give as much details as possible about your changes (don’t hesitate to give code examples to illustrate your points).The pull request description helps the code review and it serves as a reference when the code is merged.

Why?

If your pull request is about adding a new feature or modifying an existing one, explain the rationale for the changes.Why did you add this feature, which problem does the PR fix?

1.5. Developer Guide 207

Page 212: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Example usage

If you add a new feature or make breaking changes, please give us a example usage (code examples, screenshots, etc.),so we understand what you try to solve.

Remove this section if not needed.

BC Breaks/Deprecations

If you have done BC breaks or deprecations, please describe them shortly here and add them to the UPGRADE.md fileas well.

Remove this section if not needed.

To Do

If some of the requirements in the checklist are not met, use the “To Do” section and add the relevant items:

- [ ] Submit changes to the documentation- [ ] Document the BC breaks

If the code is not finished yet because you don’t have time to finish it or because you want early feedback on yourwork, add an item to to-do list:

- [ ] Finish the code- [ ] Add tests as they have not been updated yet- [ ] Gather feedback for my changes

Remove this section if not needed.

Test your Code

Sulu has three types of tests: Unit, Functional and Scenario (i.e. “Behat”).

If your tests require external dependencies (e.g. a database connection) then they are Functional tests, otherwise theywill be a Unit Test.

One key quality of Unit tests is that they execute very fast, whereas functional tests tend to be slower.

Functional Tests

To run the tests, follow these steps:

1. Install Composer dependencies with composer install.

2. Run the tests in one of the following ways.

Bundle Testing

The test runner script is a php script which automates the execution of Bundle tests. It is used by the continuousintegration server and can be useful to quickly get the tests running.

208 Chapter 1. What’s in our documentation?

Page 213: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

$ ./bin/runtests -i -C

The above will initialize the database(i) and run all the tests excluding the component tests (C).

runtests has the following options:

• -i: Initialize the test setup (e.g. creating database).

• -t [Bundle]: Run the tests only for the specific bundle.

• -a: Run all tests.

• -B: Don’t run the bundle tests

• -C: Don’t run the component tests

• -r: Restart jackrabbit between each bundle when running all tests.

Subsequently you will only need to run the tests, so you can omit the -i option.

$ ./bin/runtests -a

You may also specify a specific bundle for which to run the tests:

$ ./bin/runtests -C -t SuluSearchBundle

After the bundles have been initialized you may also simply change to the bundle root directory and use phpunit asnormal:

$ cd src/Sulu/Bundle/SuluSearchBundle$ phpunit

Component Testing

The component tests may be executed using the runtests script or PHPUnit from the root directory:

$ ./bin/runtests -B$ phpunit

You can test a specific component with PHPUnit by specifying the path:

$ phpunit src/Sulu/Component/Content

Jackrabbit installation

By default Sulu uses the Doctrine DBAL implementation for PHPCR in your local test environment. If you need totest against the Jackrabbit backend, you can install it with the following bash snippet:

JACKRABBIT_VERSION=2.12.0if [ ! -f downloads/jackrabbit-standalone-$JACKRABBIT_VERSION.jar ]; then

cd downloadswget http://archive.apache.org/dist/jackrabbit/$JACKRABBIT_VERSION/jackrabbit-

→˓standalone-$JACKRABBIT_VERSION.jarcd -

fi

To start your jackrabbit installation run

1.5. Developer Guide 209

Page 214: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

java -jar downloads/jackrabbit-standalone-2.12.0.jar > /dev/null &

Now you have to run your tests with the jackrabbit backend enabled (omit the initialization step [-i] after thefirst run):

$ SYMFONY__PHPCR__TRANSPORT=jackrabbit ./bin/runtests -i -a

Test Environment Configuration

Sulu uses the SuluTestBundle to simplify testing. This bundle also takes care of configuration for your testenvironment. For example, if you changed the port for your Jackrabbit server to 8888, you can use environmentvariables to let Symfony override default parameters:

$ SYMFONY__PHPCR__TRANSPORT=jackrabbit SYMFONY__PHPCR__BACKEND_URL=http://→˓localhost:8888/server/ ./bin/runtests -a

More information in the Symfony docs. For a list of available parameters take a look into the parameter.yml.

Adding translations

For many developers adding translations is the first step in becoming an open source contributor. Apart from that, italso adds value to the project, so we are explaining the steps here, to make the work for translators as easy as possible.

Understanding the translation process

First of all it is important to actually understand the translation process of Sulu.

Every bundle requiring translations stores them in their Resources/translations/sulu folder using the xliff format. Thesefiles are named using the scheme backend.[language].xlf and look something like this:

<?xml version="1.0" encoding="utf-8"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">

<file source-language="en" datatype="plaintext" original="file.ext"><body>

<trans-unit id="a50af522ac181350142c0b4720e29a3f" resname="translation.→˓code">

<source>translation.code</source><target>Some translation</target>

</trans-unit></body>

</file></xliff>

If the SuluTranslateBundle is registered in the Kernel, there is the command sulu:translate:exportavailable, which exports the translations from all bundles into a single JSON file located in web/admin/translations.From there the Sulu-Admin loads the translations for the required localization.

Add a new translation

The following guide explains how to add a complete new translation for the Sulu-Admin.

These steps have to be executed for every single bundle:

210 Chapter 1. What’s in our documentation?

Page 215: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1. Copy the file Resources/translations/sulu/backend.en.xlf (or any other language you might be more comfortablewith) into Resources/translations/sulu/backend.[language].xlf (replace [language] with the two-letter ISOcode of the new language).

2. Replace the translations in all target xml tags with the translation for the new language.

Afterwards the next steps have to be executed once for introducing the language in the standard edition of Sulu:

1. Add the language in the file app/config/sulu.yml to sulu_core.locales, with the 2-letter code as the key,and the name of the new language (in this language) as the value.

2. Add the 2-letter code to the array sulu_core.translations

3. Build the new language using app/console sulu:translate:export [language]. If you updatedmultiple languages at once, you can leave off the language part.

Afterwards commit all the changes and create Pull Requests as described in Creating a Pull Request.

1.5.2 Backwards Compatibility Promise

Sulu is a stable software used in production. It is however still under heavy development and therefore a full backwardscompatibility can not be guaranteed at the current stage.

We are do our best to keep backwards compatibility for the most used extension points and services of Sulu. Theseare listed in this document.

The promises given in this document are only valid during a single major release. When a new major version isreleased, these promises might be broken.

PHP

Twig

The most important extension point is twig as its templates are used in any project using Sulu for content management.We guarantee that the variables passed to the twig template as described in Rendering Pages with Twig will keep theirstructure and that all the twig extensions as described in Twig Extensions will continue to work using the same calls.

Configuration

There are several configuration files responsible for Sulu’s behaviour for which backwards compatibility is promised:

• Webspace (see Setup a Webspace)

• Template (see Creating a Page Template)

• Image formats (see Adding a theme)

• MassiveSearch (see the MassiveSearchBundle Mapping)

• Bundle configurations

Events

Using events for extending Sulu is quite common so we will keep backwards compatibility here. There might be newdata added to some events but the current data will not be removed. The events are also guaranteed not to changenames.

1.5. Developer Guide 211

Page 216: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Sulu-Admin

Using the Admin class together with its navigation is guaranteed not to break. The same is valid for extending someforms with custom tabs as described in Using tab navigation.

Content Types

It is safe to create custom content types by implementing the ContentTypeInterface or by inheriting one of theabstract SimpleContentType or ComplexContentType classes.

We also make sure that the content types delivered with Sulu will save the content in the same way so that there willnot be any regressions with the content on an upgrade.

There will be migrations provided in case the structure of the content has to be changed in order to fix some bugs andthe previous promise cannot be kept.

Sulu Classes and Interfaces

The following classes and interfaces are guaranteed to keep backwards compatibility:

• DocumentManagerInterface

• WebsiteController

• RequestAnalyzerInterface

• SecurityCheckerInterface

• User

• Contact

• Category

• Tag

JavaScript

Structure

The main entry point will continue to exist at the same location (Resources/public/js/main.js of the bun-dle). Adding routes to the system via this file will be continuing to work the same way. We will also continue to useRequireJS and the components model including the sandbox.

Hooks

We have introduced different hooks (see Javascript-Hooks) in our JavaScript environment. The hooks for events,defaults, loadComponentData and the header will keep backwards compatibility.

212 Chapter 1. What’s in our documentation?

Page 217: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.5.3 Creating a bundle

Frontend structure

A lot of the folder structure on the backend’s side of Sulu is determined by Symfony2. In the frontend however, itleaves most things open. So here’s introduction on how to arrange your frontend files and best-practice concepts forthe frontend everything takes place in your bundles Resources/public folder.

Resources/public folder

• css - holding the built css files

• dist - holding the minified/uglified javascript files

• img - for putting image files

• js - here goes all of your javascript-code

• scss - the sass styling-files from which the css is built

Let’s have a closer look on the js folder. Sulu tries to be convenient, fast and flexible in the user-interface. As youprobably have already noticed the page only loads once after the login. To provide such a interface beside still beingmodular and preventing code duplication we must agree on a common architecture for the javascript code.

Resources/public/js folder

• main.js - This is the main entry point to your bundle’s frontend. The bundle-extension file.

• collections - The folder where your backbone-collections go.

• models - Here are your backbone-models.

• vendor - If you need some vendors specific for your bundle, put them in this folder.

• components - In this folder you’ll find the view- and the tab-components.

• services - In here go require-components which provide methods you’ll need in your components. Like e.g.saving data or fetching data from the sever.

• extensions - Here you’ll find files which are mainly required in your bundle-extension file to extend the frontendframework. Like for e.g. sulu-buttons.

• validation - If you are validating forms and have a custom element (not a standard input) which also should bevalidated, this is the place to put your validation files.

Bundle-extension file

The bundle-extension file can be found within your bundle under Resources/public/js/main.js. Essentially this file isan aura-extension which gets called right at the beginning when reloading the page. the bundle-extension is used toregister backbone-routes and define which route corresponds to which view.

Frontend routing

In Sulu’s backend UI it is not like you click on a link and get redirected to a whole another page. Basically there areonly two main pages which you can redirect to in that sense that they load everything from start, namely the admin-and the login-page.

1.5. Developer Guide 213

Page 218: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

For your own bundle you have to tell the frontend framework which route corresponds to which view. If a user thennavigates to the route you have configured, the frontend framework grabs your view-component and renders it in thecontent-column. For defining routes we use backbone.js.

The whole registering process is done in your bundle-extension file.

Lets say we are a spaceship manufacturer and have written view-component in Re-sources/public/js/components/list/main.js which renders a list of all spaceships contained in our system. Tomake our view appear when a user navigates to admin/#vehicles/spaceships we’d write the following intothe bundle-extension:

// list all spaceshipssandbox.mvc.routes.push({

route: 'vehicles/spaceships',callback: function() {

return '<div data-aura-component="list@suluvehicles"/>';}

});

Views and tabs

Views and tabs are stored under Resources/public/js/components. A view is a javascript-component which gets regis-tered in combination with a route.

Main-views

Essentially you’re free to render and do whatever you want in a view-component. Yet you’ll most often find yourselfdoing the following things with a view-component.

1. Using the header-hook to get a neat little header rendered into your view component

2. Writing methods which implements behaviour which stays the same over all tabs

3. Communicating with your tab-components via events.

Tab-views

When using the header-hook you’ll also have the possibility to register tabs. Tabs are nothing more and nothing lessthan again javascript-components. The files for the tab-components get created in sub-folders of the main-view. Solets say we have a main view under Resources/public/js/components/list. After adding a details-tab and a settings tab,the structure in the list folder looks the following

• main.js - This is the main-view where the header is rendered via the header-hook

• details/main.js - The js-component of the details-tab

• settings/main.js - The js-component of the settings-tab

214 Chapter 1. What’s in our documentation?

Page 219: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

1.5.4 Behat (Functional) Testing

Getting started

Choosing Driver

Selenium2 (with a browser)

It is recommended to run tests locally with the Selenium driver. Using the Selenium driver on a real browser allowsyou to see the tests running (as opposed to using a headless browser as detailed in the following sections).

You can download Selenium server from here, or install it as follows:

$ mkdir ~/jars$ cd jars$ wget http://selenium-release.storage.googleapis.com/2.46/selenium-server-standalone-→˓2.46.0.jar

You can then run it:

$ java -jar selenium-server-standalone-2.46.0.jar -browserSessionReuse -singleWindow

The -browserSessionReuse tells selenium NOT to close the window after every test and -singleWindowwill cause Selenium not to use more than one window.

Note: You will need to install the Java Runtime Environment. On a debian based system you could do apt-getinstall default-jre

Selenium2 (headless, with PhantomJs)

Note: It is not currently recommended to run tests on PhantomJS - the results may be unpredictable.

PhantomJS can be installed with npm:

$ npm install phantomjs

You can then run it as follows:

$ phantomjs --webdriver=8643

Then you will need to copy the default behat.yml.dist to behat.yml and add the wd_host option as follows:

defaults:# ...extensions:

# ...Behat\MinkExtension:

sessions:default:

selenium2:wd_host: "http://localhost:8643/wd/hub"

1.5. Developer Guide 215

Page 220: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

Running the tests

You can run all the tests as follows:

$ ./vendor/behat/behat/bin/behat -p <profile>

Where <profile> is one of:

• selenium: Tests on your local machine via Selenium using a real browser above)

• travis: Run configuration for travis

Note: If you want to run the tests on your local machine with the default configuration you have to make sure that aSulu instance is running on 127.0.0.1:8000. You can use the command ./app/console server:run for that.

The tests are split up into a number of suites. There is one suite for each bundle, named after the bundle in lowercase,for example SuluContactBundle has the suite named contact.

Run specific suites as follows:

$ ./vendor/behat/behat/bin/behat --suite=contact -p <profile>

Further more you can filter for specific tests using the name option:

$ ./vendor/behat/behat/bin/behat --suite=contact --name="Create" -p <profile>

The above will run all scenarios in the contact suite which contain the word Create.

Context Reference

Behat matches sentences to methods in Context classes in order to execute things. For example the sentence Iclick on foo might map to the method DefaultContext#iClickFoo.

Context classes can be found in the namespace Sulu\Bundle\<BundleName>\Behat. Each one contains thelogic which applies directly to the bundle it belongs to, as such you will probably never need all of them.

This chapter will outline the contexts which you will want to use frequently.

AdminContext

Bundle: SuluAdminBundle

This context contains all actions which apply to the backoffice, including all actions which apply to Husky components.

The following is not a fully comprehensive list:

• I expect a success notification to appear: Wait and assert that a success notification willappear.

• I expect a data grid to appear: Wait for and assert that a husky datagrid appears

• I expect a form to appear: Wait for any <form/> to appear on the page.

• I expect an overlay to appear: Wait and assert that an overlay appears on the page.

• I click the tick button: Click the tick button in a dialog

• I confirm: Confirm a dialog

216 Chapter 1. What’s in our documentation?

Page 221: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• I click the search icon: Click the search icon (.btn .fa-search)

• I click the row containing ":text": Click on the husky table row containing “text”.

• I select :value from the husky :name: Select the given value from a husky drop-down selectoras identified by selector.

• I fill in husky field ":name" with ":value": Fill in a husky field (husky fields do not havename elements preventing us from using the default mechanism).

• I click the button ":text": Click the button containing the text :text

• I click toolbar item ":id": Click on the toolbar item with given id (also dropdown)

• I expect the ":event" event: Wait for the named aura event

• I expect the following events: Wait until all of the given events have been emitted.

• There should be errors: Assert that there are errors on the page.

• I wait for the ajax request: Wait until all underlying AJAX requests have finished.

• I click the close icon: Click the close icon (.fa-times)

• I click the close icon in container ":selector": Click the close icon (selector + '.fa-times')

• I wait for the column navigation column :index: Wait for the column navigation columnwith given id.

• I expect a data-navigation to appear or I expect wait for data-navigation toappear: Wait and assert that a data-navigation will appear.

• I expect an overlay to appear: Wait and assert that a overlay will appear.

ContentContext

Bundle: SuluContentBundle

You will use the content context frequently when testing content types.

• there exists a page template ":name" with the following propertyconfiguration: Create a page template with a given content type configuration:

Background:Given there exists a page template "checkbox_page" with the following property

→˓configuration"""<property name="checkbox" type="checkbox">

<meta><title lang="de">Checkbox</title>

</meta></property>"""

• the following pages exist: Create pages from the given data, for example:

And the following pages exist:| template | url | title | parent | data || article | /articles | Articles | | {"body": "This is article 1"} || article | /articles/foo | Foo | /articles | {"body": "This is article Foo"} |

1.5. Developer Guide 217

Page 222: Sulu 2.0 Documentation - Read the Docs · Sulu is based on Symfony and structured according to the Symfony standards. If you don’t know how to work within the Symfony framework

Sulu 2.0 Documentation, Release 1.6

• I am editing page of type :type: Create a simple page with the given type and go to that page:

Given I am editing a page of type "color_page"

• I expect aura component ":type" to appear: Wait for the aura component to appear. Waits fora <div/> with the aura-instance-name property equal to the given name to appear and have more thanzero children.

SecurityContext

Bundle: SuluSecurityBundle

• Given I am logged in as an administrator: Login to Sulu using admin/admin.

DefaultContext

Bundle: SuluTestBundle

The Default context is the most used context and provides lots of “primitive” actions.

• I click the selector ":selector": Click the element identified by the given CSS selector (willwait until it appears).

• I click on "":selector" in ":container": Click the element identified by the given CSS se-lector container within a container CSS selector. (will wait until it appears).

• pause: Pause the test forever – for debugging.

• wait a second: Wait 1 second. If you use this you are a bad person.

• I expect to see ":text". Wait until text appears and then assert that it did.

• I expect to see ":count" ":text" elements. Wait until text appears and then assert that thereare a specified number of them.

• I fill in the selector :selector with :value: Set the value on elements identified by thegiven CSS selector.

• Press enter on ":selector": Simulate an “enter” key being pressed on the given CSS selector.

No matter what, we love every contribution and will honor it.

If you got any questions, feel free to contact us. We are more than happy to help.

We are also on Github. Pull requests and contributions are very welcome.

Have fun with Sulu.

218 Chapter 1. What’s in our documentation?