Festi Theme

The Festi Framework Theme package is designed for projects that have a frontend component. It allows developers to structure and manage their project's themes efficiently, offering flexibility and modularity in how layouts, styles, and UI components are handled.

The following sections will provide details on the structure, available events, UI components, and how to test the theme functionality.

Theme Structure

The theme is typically stored in the following directory: project_directory/themes/[ThemeName].

  • main.phtml - The main layout file. The scope of this file is a class that implements the ITheme interface. By default, the class used is DefaultTheme. You can extend DefaultTheme by creating a custom theme class and placing it in the main theme folder as Theme.php:
    class Theme extends DefaultTheme
    {
    
    }
  • head.phtml - This file defines the content that will be included in the <head> section. Here are some common variables:
  • jsVersion - js_version from the settings table
  • siteCaption - site_caption from the settings table
  • host - host from the settings table
  • css - A directory where all the theme-specific CSS files are stored.
  • js - A directory where all JavaScript files for the theme are stored.
  • templates - A directory containing the PHTML templates used by the theme.
  • Theme.php - (Optional) This file allows you to extend the default theme class. You can implement custom logic or override the default behavior of the DefaultTheme class.
  • tests - A directory for unit tests related to the theme. This is where you can write automated tests to ensure the functionality of the theme works as expected.

Theme Class

By default, the DefaultTheme class is used for theme functionality. If you want to add custom logic or override the existing behavior, you need to create a Theme.php file in the root of your theme directory. Inside this file, you will define a Theme class that either implements the ITheme interface or extends the DefaultTheme class.

If you want to create a theme class with a different name (e.g., YourThemeClass), you must return that class at the end of the Theme.php file:

class YourThemeClass extends DefaultTheme
{
    public const EVENT_THEME_HEADER_CONTENT_RIGHT = 'ThemeHeaderContentRight';

    public function fetchRightHeaderContent(): string
    {
        $list = $this->doThemeEvent(static::EVENT_THEME_HEADER_CONTENT_RIGHT);

        return join('', $list);
    }
}

return YourThemeClass::class;

Theme-Triggered Events

The theme triggers several events that allow for dynamic content injection in specific areas, such as the header or sidebar. You can listen for these events and use them to customize various parts of the theme.

ThemeHeaderUserActivity

This event is triggered when rendering the user activity block in the header, right next to the logo.

Theme Event User Activity

Example of attaching an event listener:

$core->addEventListener(
    "ThemeHeaderUserActivity",
    array(&$core->getPluginInstance('Notifications'), 'onFetchUserActivity')
);

In the NotificationsPlugin, you can handle this event and return a fetched template:

<?php

class NotificationsPlugin extends DisplayPlugin
{
    public function onFetchUserActivity(FestiEvent &$event)
    {
        $search = array(
            'id_user' => $this->getUserID(),
            'status'  => 'new'
        );

        $this->notifications = $this->object->get($search);

        return $this->fetch('notifications.phtml');
    }
}

The results returned by all event listeners are concatenated and rendered in the appropriate location.

ThemeHeaderProjectsList

Triggered when rendering the project list block in the header, right next to ThemeHeaderUserActivity.

Theme Event Projects List

Example of attaching an event listener:

$core->addEventListener(
    "ThemeHeaderProjectsList",
    array(&$systemPlugin, 'onThemeHeaderProjectsList')
);

ThemeFetchMenu

This event is triggered before rendering the left menu. If a value is returned by the event listener, the default menu will not be displayed.

$core->addEventListener(
    "ThemeFetchMenu",
    array(&$systemPlugin, 'onThemeFetchMenu')
);

ThemeFetchGoogleAnalytics

This event is triggered before rendering the Google Analytics code. If a value is returned by the event listener, the default Google Analytics code will not be displayed.

$core->addEventListener(
    "ThemeFetchGoogleAnalytics",
    array(&$systemPlugin, 'onThemeFetchGoogleAnalytics')
);

ThemeHeadBottom

Triggered before the </head> tag is closed. You can use this event to inject additional code into the head section.

$core->addEventListener(
    'ThemeHeadBottom',
    array(&$chatraPlugin, 'onThemeHeadAddChat')
);

Example of adding a Google Snippet Tag in the head section:

$this->core->addEventListener(
    ITheme::EVENT_THEME_HEAD_BOTTOM,
    function (ThemeEvent &$event) {
        return $this->fetch('snippet_code_submit.phtml');
    }
);

Custom Theme Events

You can also trigger your own custom theme events:

echo $this->doThemeEvent('ThemeHeaderProjectsList');

ISystemPlugin::EVENT_ON_PREPARE_MENU_ITEMS

The ISystemPlugin::EVENT_ON_PREPARE_MENU_ITEMS allow you to override menu items. You could subscribe to the event throw the core or the system plugin.

Target Items:

  • currentItem - current url
  • name - menu name
  • items - array of menu items
  • activeItem - name of current active item

ISEODataProvider::EVENT_TITLE

The ISEODataProvider::EVENT_TITLE event allows you to override the title of a page dynamically within your theme or project. This event can be subscribed to in order to modify the default title that would otherwise be displayed.

Target Items:

  • title - the title of the page

Example:

use core\theme\seo\ISEODataProvider;

$this->core->addEventListener(ISEODataProvider::EVENT_TITLE, function(ThemeEvent &$event) use ($data) {
    $event->target['title'] = $data['meta_title'];
});

ISEODataProvider::EVENT_DESCRIPTION

See ISEODataProvider::EVENT_TITLE

ISEODataProvider::EVENT_KEYWORDS

See ISEODataProvider::EVENT_TITLE

Unit Test for a Theme

Unit testing a theme helps ensure that the theme behaves as expected when interacting with events, rendering content, and applying custom logic. Below is a sample unit test that verifies if content is correctly injected into the right section of the header using the Theme::EVENT_THEME_HEADER_CONTENT_RIGHT event.

class HeaderThemeTest extends ThemeTestCase
{
    public function testAddContentRight(): void
    {
        $testContent = 'testAddContentRight';

        $this->core->addEventListener(
            Theme::EVENT_THEME_HEADER_CONTENT_RIGHT,
            function (FestiEvent &$event) use ($testContent) {
                return $testContent;
            }
        );

        $content = '';
        $displayPlugin = new DisplayPlugin();

        $result = $displayPlugin->fetchMain($content);

        $this->assertTrue(is_int(strpos($result, $testContent)));
    }

}

How to Work with SEO

In the Festi Framework, you have multiple ways to manage and modify SEO data such as titles, descriptions, and keywords. This can be done through the core's seo array, by using events, or by extending the DisplayPlugin class to handle SEO data more dynamically.

Here’s how you can work with SEO data:

  1. Set SEO Data in Core: You can define SEO attributes like title, description, keywords, og:type, and og:image by assigning them to the core's seo array:
    $this->core->seo = array(
        'title'       => $data['meta_title'],        // Meta title of the page
        'description' => $data['meta_description'],  // Meta description of the page
        'keywords'    => $data['meta_keywords'],     // Meta keywords for SEO
        'type'        => $data['og_type'],           // Open Graph type (e.g., article, website)
        'image'       => $data['og_image']           // Open Graph image (for social media sharing)
    );
  2. Override SEO Title Using Event: If you need to dynamically change the title (or any other SEO attributes) based on certain conditions or logic, you can listen to the ISEODataProvider::EVENT_TITLE event and modify the title:
    $this->core->addEventListener(ISEODataProvider::EVENT_TITLE, function(ThemeEvent &$event) use ($data) {
        // Overriding the page title
        $event->target['title'] = $data['meta_title'];
    });
  3. Override onMeta() in DisplayPlugin: If your plugin extends the DisplayPlugin, you can override the onMeta() method to further customize or extend SEO functionality. This method allows you to modify the seo data and merge it with other fields if needed.
    protected function onMeta(array &$seo): void
    {
        // If SEO data exists in core, use it
        if (isset($this->core->seo)) {
            $seo = $this->core->seo;
        }
    
        // Define additional meta fields
        $metaFields = array(
            'title'       => Entity::FIELD_TYPE_STRING_NULL,
            'description' => Entity::FIELD_TYPE_STRING_NULL,
            'keywords'    => Entity::FIELD_TYPE_STRING_NULL,
            'image_src'   => Entity::FIELD_TYPE_STRING_NULL
        );
    
        // Merge and extend existing SEO data with additional fields
        $seo = $this->getExtendData($seo, $metaFields);
    }