NAV Navbar

Introduction

Photon is a PHP library built by Jadu to power templates for Jadu CMS.

New template sets are implemented as Symfony bundles and registered with the CMS by either amending the bundles.xml configuration file or running the register theme terminal command.

Templates are extensible and can be overridden easily. In common with many Symfony applications, routes are defined in the configuration file routes.yml, controller dependencies are set in the configuration file services.yml.

Photon is built upon the Symfony framework, so having a good knowledge of the framework and Twig template language is beneficial. The following guides from the Symfony documentation are a good starting point:

Extensive documentation is also provided for Twig:

Themes

Each theme within Photon is defined as a Symfony bundle.

Whats in a theme bundle?

Each theme bundle contains a single theme.

Themes extend one another, so a theme bundle need not be exhaustive.

Typically a theme will contain:

Further information on the Bundle system is available in the Symfony documentation - The Bundle System.

Registering a theme

A command is provide to easily register Galaxies site themes with the CMS.

php cli.php cms:theme-register /absolute/path/to/theme

Themes must be registered with the CMS so that they are loaded by the Symfony kernel when it is booted. This can be done via running the cms:theme-register command.

Limiting themes for Galaxies sites

When developing a theme for use within a Galaxies site, you may want to limit the loaded theme bundles to prevent unwanted resources being available for that Galaxies site (e.g. a custom theme route).

This is possible by adding an XML element galaxy<GALAXY_DATABASE_NAME> to your bundles.xml configuration, which will limit the theme bundles loaded by the Symfony kernel for that Galaxies site.

For example, to ensure galaxy with database 'ms_jadudb_1' only loads 'MyGalaxyThemeBundle'.

<?xml version="1.0" encoding="utf-8" ?>
<bundles xmlns:config="http://www.jadu.co.uk/schema/config">
  <!-- ... -->

  <galaxyms_jadudb_1 config:type="array">
      <item key="my-galaxy-theme">MyGalaxyTheme\MyGalaxyThemeBundle</item>
  </galaxyms_jadudb_1>
</bundles>

Controllers

Page controllers

Pages are registered within the theme services.yml configuration file.

photon_cms_project.page.document_category:
    class: DocumentCategoryController
    parent: photon.controller.page
    arguments:
        - '@photon.repository.category'
        - '@photon.repository.document'
        - '@photon.repository.homepage'
        - '@photon.repository.homepage_category_default'
        - '@photon.repository.supplement'
        - '@sdk.configuration.constant_repository'
        - '@router'
    tags:
        - { name: photon.page, page_name: document_category }

Page services must be tagged with photon.page.

Page controllers are responsible for fetching the content from a respository and rendering a template. A simple invokable controller is defined per Page class. Page components are invoked by a route.

Each page controller extends the Photon\Core\Controller\Page class of the PhotonCoreBundle project.

Photon\Core\Controller\Page itself extends the Symfon framework Controller class. Further details are available in the Symfony documentation - Controller.

Component controllers

Components are registered within the theme services.yml configuration file.

photon_cms_project.component.category_navigation:
    class: CategoryNavigationController
    parent: photon.controller.component
    arguments:
        - '@ohoton.repository.category'
        - '@router'
        - '@sdk.configuration.constant_repository'
    tags:
        - { name: photon.component, component_name: category_navigation }

Component services must be tagged with photon.component.

Component controllers, like page controllers, are responsible for fetching the content from a respository and rendering a template.

A simple invokable controller class is defined per component.

Page controllers have no knowledge of the action of the component controller, components are rendered within a page template as sub-requests.

As with Pages, Components can be extended and overridden by other themes.

Additional information on sub-requests is provided in the Symfony documentation - Sub Requests.

Invoking components

{{ component('announcement') }}
{{ component('announcement', {name: 'Tom'}) }}

Components are not invoked by a route, but instead by using the component() Twig function

Parameters can be passed to components at render time, which are passed to the __invoke() method of the Component.

<?php

namespace Spacecraft\DemoProject\Component;

use Photon\Core\Controller\Component;
use Symfony\Component\HttpFoundation\Response;

class CategoryNavigationController extends Component
{
    /**
     * @param string $name
     * 
     * @return Response
     */
    public function __invoke($name)
    {
        return $this->render(
            'announcement.html.twig',
            [
                'name' => $name,
            ]
        );
    }
}

Widget controllers

Widgets are registered within the theme services.yml configuration file.

photon_cms_project.widget.website_statistics:
    class: WebsiteStatisticsWidget
    parent: photon.controller.widget
    arguments:
        - '@photon.repository.website_statistics'
    tags:
        - { name: photon.widget, widget_id: 31 }

Widget services must be tagged with photon.widget and with the parameter widget_id. The id must match the corresponding database entry in JaduHomepageWidgets.

Widget controllers are specialised component controllers. They interact with the Homepage designer feature of Jadu CMS.

Invoking widgets

{{ widget(widgetRecord) }}

Widgets are not invoked by a route, but instad by using the widget() Twig function.

A widget record must be passed as a parameter to the widget() function.

Interacting with the request

<?php

if (
    $this->getRequestStack()->request->has('period')
    && $this->getRequestStack()->request->get('period') == 'today'
) {
    // ...
}

The Page class within PhotonCoreBundle initialises the Symfony Request object in its constructor.

The Request stack object can be retrieved in classes that extend Page by calling getRequestStack() method.

Further details on how to interact with data held in the Request object are provided in the Symfony documentation - Accessing Request Data.

Returning a response


<?php

return $this->render(
    'article.html.twig', 
    [
        'page' => $page,
        'page_categoryId' => $categoryId,
        'page_contentType' => 1,
        'page_translationItem' => $document->getId(),
        'page_translationType'=> 'document',
        'page_structure' => $pageStructure,
    ]
);

All controller classes contain an __invoke() method that is called when a request terminates at that controller. The __invoke() method must return a Response object.

The Photon\Core\Controller\Page class of the PhotonCoreBundle project that all Photon Page controllers extend provides a method render() that returns a formatted Response object.

render() generally takes two arguments:

Further details on Response objects are provided by the Symfony documentation - Response.

Core Page controller methods

The Photon\Core\Controller\Page class provides a number of useful functions to the controllers that extend it.

Method Description
render() Render a twig template and return a Response
getMetaElement() Create and return a Meta object
getRequestStack() Get the Symfony Request object for this request
getMetadata() Populate and return a Metadata object for this page
isPreview() Check if this page is currently being accessed as a preview
getTranslatedString() Get the translated string for a given key based on the site's locale value
isPasswordProtected() Check if the site is currently behind password protection
isAuthenticated() Check if the user has a current session for viewing a site behind password protection

Routes

Routes are defined in the theme routing.yml config file.

homepage:
    path: /homepage/{homepageId}/{homepageSlug}
    defaults:
        _controller: photon.page.homepage

atoz_list:
    path: /a_to_z/{startsWith}
    defaults:
        _controller: photon.page.atoz_list
        startsWith: ~

atoz_article:
    path: /a_to_z/service/{serviceId}/{serviceSlug}
    defaults:
        _controller: photon.page.atoz_article
    requirements:
        serviceId: \d+

Routes within Photon are based on the Symfony router.

Where a {placeholder} is placed in the route path, that portion becomes a wildcard. The corresponding controller must now have an argument called $placeholder.

Each route has an internal name. For example homepage, atoz_list and atoz_article. These must be unique and are used to generate URLs in controllers and templates.

Further information on routing is available in the Symfony documentation - Routing.

Generating URLs

<?php

$link->setUrl($this->generateUrl(
    'news_article',
    [
        'articleId' => $newsArticle->getId(),
        'articleSlug' => $newsArticle->getSlug(),
    ]
));

To generate a URL, you must specify the name of the route, and any parameters used in the path for that route.

Instead of hardcoding URLs into templates, the path Twig function can be used to generate URLs based on the routing configuration. Changing the route will then update all template instances where that route is used.

<a href="{{ path('welcome') }}">Home</a>

Further information on generating URLs is provided in the Symfony documentation - Generating URLs

Redirecting

<?php

// redirect to the "homepage" route
return $this->redirectToRoute('homepage');

// do a permanent - 301 redirect
return $this->redirectToRoute('homepage', [], 301);

// redirect to a route with parameters
return $this->redirectToRoute(
    'blog_show',
    [
        'slug' => 'my-page',
    ]
);

// redirect externally
return $this->redirect('http://symfony.com/doc');

A number of redirect options are provided via Symfony.

Further information on redirecting is provided in the Symfony documentation - Redirecting.

Models

<?php

namespace Photon\CmsEngine\Model\Element;

class Article
{
    /**
     * @var Link
     */
    private $link;

    /**
     * @param Link $link
     * 
     * @return Article
     */
    public function setLink($link)
    {
        $this->link = $link;

        return $this;
    }

    /** 
     * @return Link
     */
    public function getLink()
    {
        return $this->link;
    }
}

You can use a dot (.) to access attributes of a model variable in twig

{{ article.title }}
{{ article.link.url }}

A number of model classes are provided to format data for pages and ensure a consistent interface when accessing data from different areas of the CMS.

The attributes of the model are accessed using getter and setter functions in PHP. Calls to setter methods can be chained for ease of use.

Article

<?php

use Photon\CmsEngine\Model\Element\Article;

$article = new Article();
$article->setTitle($item->getTitle());
$article->setSummary($item->getSummary());
$article->setBody($item->getDescription());
$article->setImage($item->getImage());

$article->addMeta(
    $this->getTranslatedString('date-label'),
    $this->getMetaElement(
        $this->getTranslatedString('date-label'),
        $item->getEventDate(),
        'string'
    )
);

This model defines a generic article page.

Classname

Photon\CmsEngine\Model\Element\Article

Attributes

Attribute Type Description
$image Image Featured image of the article
$title string Title of the article
$subTitle string Subtitle of the article
$summary string A brief summary of the article
$body string Rich text content that makes up the body of the article
$meta array Array of metadata on this article
$supplements array Array of supplements associated with this article
$passwordProtected boolean Whether this content is password protected
$restricted boolean Whether access to this content is restricted
$navigation array Array of navigation links
$pagination array Array of pagination links
$link Link Link to this article

Form

<?php

use Photon\CmsEngine\Model\Element\Form;
use Photon\CmsEngine\Model\Element\FormControl;

$form = new Form();
$form->setTitle($title);
$form->setMethod('post');

$control = new FormControl();
$control->setName('_token');
$control->setType('hidden');
$form->addControl($control);

$control = new FormControl();
$control->setLabel($emailLabel);
$control->setName('email');
$control->setType('email');
$control->setValue('');
$control->setMandatory(true);
$control->setAutocomplete(false);
$form->addControl($control);

$control = new FormControl();
$control->setLabel($passwordLabel);
$control->setName('password');
$control->setType('password');
$control->setValue('');
$control->setMandatory(true);
$control->setAutocomplete(false);
$form->addControl($control);

$control = new FormControl();
$control->setLabel($signinButtonLabel);
$control->setName('jaduSignInButton');
$control->setType('submit');
$form->setPrimaryAction($control);

This model defines a form, including a list of form controls to submit data to the page.

Classname

Photon\CmsEngine\Model\Element\Form

Attributes

Attribute Type Description
$id integer The id of this form in the database, where the form is content managed
$title string The title of the form
$controls array An array of form controls that make up this form page
$errors array An array of errors found in the form following validation
$action string The content of the action attribute of the form HTML element
$method string The content of the method attribute of the form HTML element
$formHeading string The title of this form page
$instructions string Rich text content that makes up the instructions of the form page
$primaryAction FormControl The form control that forms the primary action of the form, often a submit button
$secondaryAction FormControl The form control that forms the secondary action of the form, often a cancel or back button

FormControl

<?php

use Photon\CmsEngine\Model\Element\FormControl;

$control = new FormControl();
$control->setLabel($passwordLabel);
$control->setName('password');
$control->setType('password');
$control->setValue('');
$control->setMandatory(true);
$control->setAutocomplete(false);

A single form control and its associated values, such as label and help text.

Classname

Photon\CmsEngine\Model\Element\FormControl

Attributes

Attribute Type Description
$id integer The id of this form control in the database, where the form is content managed
$label string The label of the form control
$options array An array of form control options, for example in a select or radio button group
$name string The content of the name attribute of the form control's HTML element
$value string The content of the value attribute of the form control's HTML element
$type string The type of form control HTML element to use in the template
$error boolean Whether validation found an error in this control
$autocomplete boolean Whether the autocomplete attribute should be disabled for this control
$disabled boolean Whether this control is disabled in the user interface
$help string The help text to display with this form control
$maxlength integer The content of the maxlength attribute of the form control's HTML element
$mandatory boolean Whether this control must be completed prior to successful submission
$className string Additional CSS class name data associated with this control
$placeholder string The content of the placeholder attribute of the form control's HTML element

FormControlOption

<?php

use Photon\CmsEngine\Model\Element\FormControlOption;

$option = new FormControlOption();
$option->setValue('')
    ->setText('All Categories');
$options[] = $option;

foreach ($topLevelCategories as $topLevelCategory) {
    $option = new FormControlOption();
    $option->setValue($topLevelCategory->getId())
        ->setText($topLevelCategory->getTitle());
    $options[] = $option;
}

A single option for a form control, such as a select or radio button group.

Classname

Photon\CmsEngine\Model\Element\FormControlOption

Attributes

Attribute Type Description
$value string The content of the value attribute of the form control's HTML element
$text string The label for this option
<?php

use Photon\CmsEngine\Model\Element\Link;

$azLink = new Link();
$azLink->setTitle($char);
$azLink->setUrl(
    $this->urlGenerator->generate(
        'atoz_list',
        [
            'startsWith' => $char,
        ]
    )
);
$azLink->setDisabled($count > 0);

A link element, including the link destination and label text.

Classname

Photon\CmsEngine\Model\Element\Link

Attributes

Attribute Type Description
$url string The content of the href attribute of the link's HTML element
$title string The text the link will be applied to
$subtitle string Additional text that the link will be applied to, commonly used to split the title to allow additional HTML elements to be applied and styled differently
$active boolean The link has been selected
$disabled boolean The link should be displayed as disabled in the user interface

Listing

<?php

use Photon\CmsEngine\Model\Element\Listing;

$listing = new Listing();
$listing->setTitle($azPageTitle);

$listItems = $this->getRecordList($items, $aliasitems);
if ($listItems) {
    $listing->addList($listItems);
}

$listing->setAZLinkList(
    $this->buildAZLinkListBar()
);

A generic list page.

Classname

Photon\CmsEngine\Model\Element\Listing

Attributes

Attribute Type Description
$filterBar Form A form for filtering the content of this list
$pagination array Array of pagination links
$feature Article Featured article from the list on this page
$title string The title of the page
$azLinkList array Array of A to Z navigation links for filtering the content of this list
$lists array Array of lists to display on this page

ListItem

<?php

use Photon\CmsEngine\Model\Element\ListItem;

$list = new ListItem();
$list->setType('record');
$list->setItems($records);

An array of records or articles.

Classname

Photon\CmsEngine\Model\Element\ListItem

Attributes

Attribute Type Description
$title string The title of the list
$items array Array of article or link objects that make up this list
$type string The type of list, for example "record" or "article"

Meta

<?php

use Photon\CmsEngine\Model\Element\Meta;

$meta = new Meta();
$meta->setTitle($title);
$meta->setValue($value);
$meta->setType($type);

A snippet of content related to an article, for example the published date.

Classname

Photon\CmsEngine\Model\Element\Meta

Attributes

Attribute Type Description
$value string The value of the metadata defined
$title string The label for the metadata defined
$type string The type of the value, for example "string" or "date"

Modular

<?php

use Photon\CmsEngine\Model\Element\Modular;

$modular = new Modular();
$modular->setHomepage($homepage);
$modular->setTitle($homepage->getTitle());
$modular->setSupplements($this->getSupplements($homepage));

Generic modular page for use with Jadu CMS Homepage content type.

Classname

Photon\CmsEngine\Model\Element\Modular

Attributes

Attribute Type Description
$title string The title of the page
$homepage Homepage The modular content block of the page
$supplements array Array of supplements associated with this article
$navigation array Array of navigation links
$documents array Array of document records associated with this page

Page

<?php

use Photon\CmsEngine\Model\Element\Page;

$page = new Page();
$page->setContent($article);
$page->setBreadcrumb($this->getBreadcrumb());
$page->setMetadata($this->getMetadata());
$page->setBodyClass('councillor-article');

A generic page, including elements such as metadata and breadcrumb.

Classname

Photon\CmsEngine\Model\Element\Page

Attributes

Attribute Type Description
$breadcrumb array Array of breadcrumb links
$navigation array Array of navigation links
$content Modular/Listing/Article/Form The main content block of the page
$metadata array HTML metadata values
$attributes array Array of key value pairs associated with the page
$bodyClass string Additional CSS class name to be printed on the body HTML element

Default theme

The default theme implements all functionality provided with Jadu CMS, and provides a base from which to extend when creating a custom theme.

The default theme is maintained, and any updates available will be added either:

CSS classes

The default theme uses Block Element Modifier (BEM)naming conventions for its class names.

Uniquely classes are set on each page of the default theme.

Partials

The partials in the default theme are organised according to atomic design principles.

Further information on atomic design is available in the Atomic design blog post.

Hard coded text

{{ 
    getTranslatedString(
        'password-reset-email-ip-statement', 
        [app.request.getClientIp()]
    ) 
}}

Within page and component controllers, translated text can be retrieved using the function getTranslatedString() of Photon\Core\Controller\Page.

<?php

$crumb->setTitle($this->getTranslatedString('az-page-link'));

Hard coded text in templates is managed using the Language Pack module in Jadu CMS.

A non-technical user interface is provided to add, update and remove text in the language pack.

Photon provides functions to retrieve the appropriate text based on the locale associated with the site.

The function to retrieve text takes two arguments:

Site locale

The locale of a site is set within the JaduLanguagePacksToSites database table. Where no matching record is found, requests for text fallback to the language pack installed with the CMS.

New language packs can be defined within the CMS and are associated with a locale. Available locales are defined within the JaduLanguageLocales database table.

Layouts

The default template set makes use of template inheritance in Twig to reduce repetition and ease maintenance.

The primary template is base.html.twig. The base template defines the HTML skeleton of the page and a number of block tags that a child template may override or extend.

Further information on extending templates can be found in Twig documentation.

Article

A page on a single topic with a title and body of content.

List

A list of records or articles, that may be paginated or filterable.

Modular

A page of content made up of blocks of widgets.

Form

A HTML form.

Default routes & pages

The default theme consists of the following routes and associated page controllers that output their response in a template. Page controllers are found in the theme Page directory.

A to Z article

A single A to Z of services entry and any associated contact details.

Route name

atoz_article

URL pattern

/a-to-z/service/{serviceId}/{serviceSlug}

Layout

Article

Module

EGov

Page class

atoz-article

Acceptance criteria

A to Z list

Filtered list of A to Z entries and aliases.

Route name

atoz_list

URL pattern

/a-to-z/{startsWith}

Layout

List

Module

EGov

Page class

atoz-list

Acceptance criteria

Accessibility settings

Form to customise the appearance of the site in line with a user's accessibility needs.

Route name

accessibility_settings

URL pattern

/accessibility/settings

Layout

Form

Module

Utilities

Page class

accessibility-form

Accessibility criteria

API apply

Form to apply for an API key linked with a user's account.

Route name

user_account_api_apply

URL pattern

/account/api/apply

Layout

Form

Module

MyAccount

Page class

api-form

Acceptance criteria

Category

Landing page of a category, including navigation to subcategories and documents relating to a single category of the site's taxonomy.

Route name

document_category

URL pattern

/{categorySlug}

Layout

Modular

Module

Publishing

Page class

category-modular

Acceptance criteria

Change details

Form to update the details of a user.

Route name

user_account_change_detail

URL pattern

/account/change-detail

Layout

Form

Module

MyAccount

Page class

change-details-form

Acceptance criteria

Change password

Form to update the password of a user.

Route name

user_account_change_password

URL pattern

/account/change-password

Layout

Form

Module

MyAccount

Page class

password-reset-form

Acceptance criteria

Councillor article

Details of a single Councillor, including their contact details and associated image.

Route name

councillor_article

URL pattern

/councillors/{councillorId}/{councillorSlug}

Layout

Article

Module

EGov

Page class

councillor-article

Acceptance criteria

Councillor list

Filtered list of councillors.

Route name

councillor_list

URL pattern

/councillors

Layout

List

Module

EGov

Page class

councillor-list

Acceptance criteria

Directory article

Directory home page including description and links to A to Z of records.

Route name

directory_article

URL pattern

/directory/{directoryId}/{directorySlug}

Layout

Article

Module

Publishing

Page class

directory-article

Acceptance criteria

Directory A to Z list

List of directory records begining with a particular letter.

Route name

directory_atoz

URL pattern

/directory/{directoryId}/a-to-z/{startsWith}

Layout

List

Module

Publishing

Page class

directory-record-list

Acceptance criteria

Directory record article

Details of a particular directory record article.

Route name

directory_record_details

URL pattern

/directory-record/{recordId}/{recordSlug}

Layout

Article

Module

Publishing

Page class

directory-record-article

Acceptance criteria

Directory search result list

List of records matching a search term in a directory.

Route name

directory_search

URL pattern

/directory/search

Layout

Article

Module

Publishing

Page class

directory-result-list

Acceptance criteria

Directory record list

List of directory records in a particular category.

Route name

directory_category

URL pattern

/directory/{directoryId}/{directorySlug}/category/{categoryId}/{pageNumber}

Layout

List

Module

Publishing

Page class

directory-record-list

Acceptance criteria

Directory record submission

Form for public submission of a new record to a directory.

Route name

directory_submit

URL pattern

/directory/{directoryId}/{directorySlug}/submit

Layout

Form

Module

Publishing

Page class

directory-submit-form

Acceptance criteria

Document article

A single page of a document with navigation to all other pages in that document.

Route name

document_page

URL pattern

/{categorySlug}/{documentSlug}/{pageNumber}

Layout

Article

Module

Publishing

Page class

document-article

Acceptance criteria

Download article

Details of a single download, with a links to download all associated files.

Route name

download_article

URL pattern

/downloads/download/{downloadId}/{downloadSlug}

Layout

Article

Module

Publishing

Page class

download-article

Acceptance criteria

Download file

Stream a file.

Route name

file_download

URL pattern

/downloads/file/{fileId}/{fileSlug}

Layout

n/a - no HTML output

Module

Publishing

Event article

A single event and associated image.

Route name

event_article

URL pattern

/events/event/{eventId}/{eventSlug}

Layout

Article

Module

Publishing

Page class

event-article

Acceptance criteria

Event list

Filtered and paginated list of events.

Route name

event_list

URL pattern

/events

Layout

List

Module

Publishing

Page class

event-list

Acceptance criteria

Event submission

Form for public submission of a new event.

Route name

event_submission

URL pattern

/event/new

Layout

Form

Module

Publishing

Page class

event-submit-form

Acceptance criteria

Forgot password

Form to request a password reset link.

Route name

user_account_forget_password

URL pattern

/account/forgot-password

Layout

Form

Module

MyAccount

Page class

forgot-password-form

Acceptance criteria

Index

The main homepage of the site.

Route name

index

URL pattern

/

Layout

Modular

Module

Publishing

Page class

index-modular

Acceptance criteria

Homepage

A single independent landing page.

Route name

homepage

URL pattern

/homepage/{homepageId}/{homepageSlug}

Layout

Modular

Module

Publishing

Page class

homepage-modular

Acceptance criteria

Meeting article

Details of a single meeting, including any agendas, minutes and other attachments.

Route name

meeting_article

URL pattern

/meetings/meeting/{meetingId}/{meetingSlug}

Layout

Article

Module

EGov

Page class

meeting-article

Acceptance criteria

Meeting download

Stream a meeting file attachment.

Route name

meeting_download

URL pattern

/download/meetings/id/{attachmentId}/{attachmentSlug}

Layout

n/a - no HTML output

Module

EGov

Meeting list

Filtered and paginated list of meetings.

Route name

meeting_list

URL pattern

/meetings

Layout

List

Module

EGov

Page class

meeting-list

Acceptance criteria

News article

A single news article and associated image.

Route name

news_article

URL pattern

/news/article/{articleId}/{articleSlug}

Layout

Article

Module

Publishing

Page class

news-article

Acceptance criteria

News list

Filtered and paginated list of news articles.

Route name

news_list

URL pattern

/news

Layout

List

Module

Publishing

Page class

news-list

Acceptance criteria

Offline

Holding page shown when the site has been taken offline.

Route name

site_offline

URL pattern

/offline

Layout

Article

Module

Utilities

Page class

offline-article

Acceptance criteria

Password reset

Form to input a new password.

Route name

user_account_password_reset

URL pattern

/account/password-reset/{resetCode}

Layout

Form

Module

MyAccount

Page class

password-reset-form

Acceptance criteria

Register

Form to create a new user account.

Route name

user_registration

URL pattern

/register

Layout

Form

Module

MyAccount

Page class

registration-form

Acceptance criteria

Restricted content login

Form used to access a restricted Galaxies site.

Route name

restricted

URL pattern

/restricted

Layout

Article

Module

Utilities

RSS

An RSS feed of recently published content

Route name

content_rss

URL pattern

/rss/{contentType}

Layout

n/a - no HTML output

Module

Publishing

Search list

List of search results provided by the site's search provider.

Route name

search_list

URL pattern

/site-search/results/

Layout

List

Module

Utilities

Page class

search-list

Sign in

Form to sign in to an existing user account.

Route name

user_account_signin

URL pattern

/account/signin

Layout

Form

Module

MyAccount

Page class

signin-form

Acceptance criteria

Unsubscribe

Form to unsubscribe from marketing newsletters sent by the site.

Route name

unsubscribe

URL pattern

/account/unsubscribe

Layout

Form

Module

MyAccount

Page class

unsubscribe-form

Acceptance criteria

User home

User's account page detailing interactions with the site.

Route name

user_home

URL pattern

/account

Layout

List

Module

MyAccount

Page class

user-home-list

Acceptance criteria

Non-HTML response routes

For the following routes, output comes directly from the controller and is not rendered in a template:

Template differences

There are a number of differences between the default template set provided with Photon, and the Jadu CMS legacy template set.

Events

Events templates have been condensed into two pages: events list and event article. A separate page to view events in a category is no longer available, events related to a particular category can be added to document category and document article templates where desired.

The event list does not include a calendar navigation element, events are shown in chronological order with next/previous pagination. Filtering by time period and location now interact, where as previously they were independent.

The event calendar is still available as a widget.

News

News templates have been condensed into two pages: news list and news article. A separate page to view news article in a category is no longer available, news related to a particular category can be added to document category and document article templates where desired.

The news archive is now combined into news list, the navigation of the news archive is provided as a filter form rather than a list of links.

The news list shows news articles in chronological order with next/previous pagination.

Downloads

Downloads templates have been condensed in a singled page: download article. Separate pages to view downloads in a category are no longer available. By default, downloads related to a particular category are listed on the document article template.

The download article template defaults to showing all files associated with a download record, and then providing a direct link to download the file, rather than then providing a link to the file specific page. The breadcrumb of the download article now links to document category tree, rather than the parallel download category tree.

As there is no downloads index page, there is no listing of popular downloads on the site.

A to Z

The A to Z home page, with separate tag cloud based navigation is no longer provided. A single list of A to Z items with alphabetical links to filter the list by letter is available, along with an A to Z article page. The A to Z article page no longer lists all content assigned to a category related to the A to Z article.

Councillors

The councillor index page, with hard coded statement, is no longer provided, a single list of councillors with filters for ward and party is provided instead.

The other councillors for a ward are no longer listed on the councillor article page. The "view by name", "view by party" and "view by ward" navigation links are no longer listed on the councillor article page.

Meetings & Minutes

Meeting templates have been condensed to remove the meeting specific category navigation pages. Meetings related to a particular category can be added to document category and document article templates where desired.

The meeting list page now shows all meetings chronologically with a filter form provided to filter by meeting heading or archive status.

Separate templates are no longer provided to view all archived meeting committees, or meetings related to a single committee.

The content of a meeting article page is now shown on load, rather than after clicking to view more as previously.

Directories

Directory templates have been condensed to remove the directory specific category navigation pages. By default, directories related to a particular category are listed on the document article template.

The breadcrumb of directory pages now links to document category tree, rather than the parallel directory category tree.

Sitemap

A separate, hardcoded sitemap template is no longer provided. A sitemap widget that outputs the document category tree is provided in its place.

Feedback form

A hardcoded feedback form is no longer provided. We recommend a content managed form is used where required.

Email a friend form

An email a friend form is no longer provided due to security concerns with this feature.

Terms and conditions

A separate terms and conditions content type is no longer provided, we recommend a content managed document or homepage is used where required.

Accessibility statement

A separate accessibility statement content type is no longer provided, we recommend a content managed document or homepage is used where required.

Location

A separate location content type is no longer provided, we recommend a content managed document or homepage is used where required.

API

A separate page to view an account's API key is no longer provided. The API key is instead shown directly on the user's account homepage.

Customisation

Creating a theme

Adding a new theme requires you to:

Creating a theme bundle

AlphaTheme 
    |- AlphaThemeBundle.php
    |- Component
    |    |- CategoryNavigationController.php
    |- DependencyInjection
    |    |- AlphaThemeExtension.php
    |- Page
    |    |- UtilitiesContent
    |        |- ThemeDemoController.php
    |- Resources
    |    |- config
    |    |   |- routing.yml
    |    |   |- services.yml
    |    |   |- theme.yml
    |    |- public 
    |    |   |- dist
    |    |   |   |...
    |    |   |- images
    |    |   |   |...
    |    |   |- js
    |    |       |...
    |    |- templates
    |        |- layouts
    |        |   |...
    |        |- pages
    |        |   |...
    |        |- partials
    |        |   |...
    |        |- widgets
    |            |...
    |- Theme
        |- AlphaTheme.php

A bundle is in essence a folder of files laid out in a consistent way.

It must contain:

For Galaxies theme bundles, the following addtions are also required:

An example theme bundle is available on GitHub: ExampleTheme

Creating a theme definition

<?php

namespace Spacecraft\DemoProject\Theme;

use Photon\Core\Theme\Theme;

class DemoTheme implements Theme
{
    /**
     * @return string
     */
    public function getName()
    {
        return 'demo';
    }

    /**
     * @return string
     */
    public function getParent()
    {
        return 'base';
    }

    /**
     * @return string
     */
    public function getPath()
    {
        return __DIR__ . '/../Resources/templates';
    }
}

The example defines the demo theme, which extends base. By convention, the theme file sits within the Theme directory of the project.

The machine name of the theme should be defined in the getName() method. If the theme is extending an existing theme, this should be defined in the getParent() method. base is the machine name of the CMS default theme.

The path to the twig templates provided by this theme should be defined within the getPath() method.

Defining a theme service

spacecraft_demo_project.theme:
    class: Spacecraft\DemoProject\Theme\DemoTheme
    tags:
        - { name: photon.theme }

To define a theme service service, create a service in services.yml that points to the theme definition file. The service should be tagged with photon.theme.

Using a custom theme

photon_core:
    theme: demo

To use a theme, the configuration of your Symfony application should be updated. The photon_core element should have a child, theme. The value of theme should be the machine name of the theme, eg. demo.

By default, the CMS stores the Symfony application for the main site in /path/to/jadu/config/frontend.yml.

Twig namespaces

{% extends '@theme:base/components/announcement.html.twig' %}

{% block title_outer %}
    <h2>{{ block('title') }}</h2>
{% endblock %}

The selected theme will always be the main namespace for Twig, i.e. pages/demo.html.twig will be loaded from the selected theme's templates.

If a theme has a parent theme, then the parent theme will also be within the main namespace.

Twig templates of a particular theme can be referenced using the theme namespace.

Customising pages

Defining the controller:

<?php

namespace Spacecraft\DemoProject\Page;

use Photon\Core\Controller\Page;
use Symfony\Component\HttpFoundation\Response;

class DemoController extends Page
{
    /**
     * @return Response
     */
    public function __invoke()
    {
        return $this->render('pages/demo.html.twig');
    }
}

Defining the service:

spacecraft_demo_project.page.demo:
    class: Spacecraft\DemoProject\Page\DemoController
    parent: photon.controller.page

When adding a new page, simply define the route and specify the new controller service

demo:
    path: /demo
    defaults:
        _controller: spacecraft_demo_project.page.demo

Pages can be added in Photon by creating a controller class, defining a service and route.

The controller class is a standard Symfony controller that takes a Request and returns a Response. Generally, each page will have it's own controller and be an invokable class.

In this example the Twig template being rendered needs to be available in the current theme.

Customising components

Defining the controller:

<?php

namespace Spacecraft\DemoProject\Component;

use Photon\Core\Controller\Component;
use Symfony\Component\HttpFoundation\Response;

class AnnouncementController extends Component
{
    /**
     * @return Response
     */
    public function __invoke()
    {
        return $this->render('components/announcement.html.twig');
    }
}

Defining the service:

spacecraft_demo_project.page.demo:
    class: Spacecraft\DemoProject\Component\AnnouncementController
    parent: photon.controller.component
    tags:
        - { name: photon.component, component_name: announcement }

Invoking the component:

{{ component('announcement') }}

Components can be added in Photon by creating a controller class and defining a service tagged as photon.component.

The controller class for a component is a standard Symfony controller that takes a Request and returns a Response. The controller must be an invokable class to be compatible with the component system.

The service must be given a name. This name will be used when referencing the component within a Twig template.

Once the component service is defined it can be rendered within any Photon template using the component() Twig function.

Customising widgets

Defining the controller:

<?php

namespace Photon\CmsProject\Widget;


use Photon\CmsEngine\Model\PublishingContent\Homepage\HomepageWidget;
use Photon\Core\Controller\Widget;
use Symfony\Component\HttpFoundation\Response;

class ContentWidget extends Widget
{
    /**
     * @param HomepageWidget $widget
     * @param bool $isPreview
     *
     * @return Response
     */
    public function __invoke(HomepageWidget $widget, $isPreview = false)
    {
        if ($isPreview && empty($widget->getSettingValue('title')) && empty($widget->getSettingValue('content'))) {
            return new Response('Please apply some settings to this widget.');
        }
        return $this->render('widgets/content.html.twig', [
            'title' => $widget->getSettingValue('title'),
            'content' => $widget->getSettingValue('content'),
        ]);
    }
}

Defining the service:

photon_cms_project.widget.website_statistics:
    class: Photon\CmsProject\Widget\WebsiteStatisticsWidget
    parent: photon.controller.widget
    arguments:
        - '@photon_cms_engine.repository.website_statistics'
    tags:
        - { name: photon.widget, widget_id: 31 }

Widgets can be added in Photon by creating a controller class, registering the widget in the JaduHomepageWidgets database table and defining a service tagged as photon.widget and the id of the widget in the CMS database.

The controller class for a widget is a standard Symfony controller that takes a request and returns a response. The controller must be an invokable class to be compatible with the widget system.

The widget service must be tagged with photon.widget and given an id. The id is used when the CMS identifies the controller to invoke, where a matching controller can not be found, the default widget controller is reverted to.

Customising supplements

<?xml version="1.0" encoding="utf-8"?>
<system xmlns:config="http://www.jadu.co.uk/schema/config">
    <supplements config:type="array">
        <item key="spacecraft-popular-pages">PopularPagesPageSupplementManager</item>
    </supplements>
</system>

Supplements can be added in Photon by creating a manager class, administration interface file and associated class.

The new supplement class must be registered with the CMS in the database table JaduPageSupplements. The supplement manager class must be registered in page_supplements.xml.

Linking to theme assets

`{{ asset('images/info.png') }}`

This would generate a link to an image called info.png within the directory Resources/public/images/

Theme assets should be placed in a directory named public in the Resources directory of the bundle.

Theme assets can then be embedded in templates by calling the asset() function.

Customising date format

Default date format in Photon is jS F Y. This format can be changed by adding a date_format value to the config.yml file.

photon_core:
    theme: base
    fixture_set: base
    date_format: Y-m-d
photon_core:
    date_format: Y-m-d

{JADU_HOME}/config/galaxyms_jadudb_{SITE-ID}/config_prod.yml: yaml imports: - { resource: config.yml }

All blogs follow the date format defined in the database JaduBlogs.dateFormat, which is set to d/m/Y by default. If no value is set in the database, the default jS F Y is used unless a value is defined in {JADU_HOME}/config/blog/config.yml:

photon_core:
  theme: blogbase
  fixture_set: blogbase
  date_format: Y-m-d

{JADU_HOME}/config/blog/config_prod.yml:

imports:
    - { resource: config.yml }

Troubleshooting

The site is complaining that the theme doesn't exist

This is commonly down to either the theme service not being defined within the theme bundle services.yml, or the DependencyInjection folder and it's related files being missing from the theme bundle.

My template file isn't overriding the original template file

Generally this is down to differences in the path to the file. Double check that the files names match exactly, as this is case sensitive. Check that the directory path to the two templates is an exact match.