Building extensions - examples

This page contains a collection of useful examples, of how to write extensions and content page extensions, and how to interact with other extensions. Use the links below, to jump down to a specific example. The examples are also available as downloads: Simple extension, Extension with multiple pages and menu items.




Ordinary extension

Simple example

An ordinary extension is very simple. Follow these steps to create your first extension.

  • Create a folder in the extensions folder with the name of the extension.
    In this example we name the folder MyTest.
  • Create a file within the new folder called Main.class.php
  • Insert the code from the snippet below in the Main.class.php file
Main.class.php

<?php

// Class must use the name of the extension folder (MyTest)
class MyTest extends SMExtension
{
    public function Render()
    {
        $content = "";
        $content .= "<h1>Welcome to the ";
        $content .= $this->context->GetExtensionName();
        $content .= " extension</h1>";

        return $content;
    }
}

?>
The example above demonstrates how easy it is, to create an extension. The code above defines a class with the name of the extension/folder. The class extends the SMExtension class from the framework. Finally we define a function called Render that returns some content.

Try executing the new extension by browsing to index.php?SMExt=MyTest
You should now see the following text: Webcome to the MyTest extension.

The context object is an instance of SMContext, and is defined on the base class SMExtension.

It is possible to use all the PHP code you usually work with in the Render function, but it is also possible to use classes and functions from the framework and the GUI library.

Before proceeding with further enhancements to the MyTest extension, a file called metadata.xml is created in the extension folder. This file should always be present, as code from both the framework and some extensions expect this file to exist. The announcement extension will fail if this file is not present.

metadata.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<entries>
    <entry key="Title" value="My Text extension" />
    <entry key="Description" value="Sample extension" />
    <entry key="Author" value="Your name" />
    <entry key="Company" value="Company name" />
    <entry key="Website" value="http://your-company.com" />
    <entry key="Email" value="mail@your-company.com" />
    <entry key="Version" value="1" />
    <entry key="Dependencies" value="" />
    <entry key="Notes" value="" />
</entries>
The metadata.xml file provides information about an extension that is useful in different situations. The announcement extension uses the information to check for updates to standard Sitemagic CMS extensions.


Add extension to menu (SMMenu)

It is not very intuitive to execute the extension by typing the name in the browser, as described in the previous section. The following snippet shows us how to add the extension to the Admin menu. Add the function to the Main.class.php file in the extension folder.

public function PreTemplateUpdate()
{
    $smMenuExists = SMExtensionManager::Import("SMMenu", "SMMenu.classes.php");

    if ($smMenuExists === true)
    {
        if (SMAuthentication::Authorized() === true)
        {
            $adminItem = SMMenuManager::GetInstance()->GetChild("SMMenuAdmin");
            $newChild = new SMMenuItem("MyTestLink", "MyTest", SMExtensionManager::GetExtensionUrl("MyTest"));
            $adminItem->AddChild($newChild);
        }
    }
}
The first line imports a file from the SMMenu extension, which contains the functionality to add items to the menu. This function will return True if the file and extension exists, otherwise False. We want the MyTest extension to behave properly, if the menu extension does not exist. It may have been removed by the owner of the installation.

Furthermore we make sure that the user is authorized (logged in). Otherwise it does not make much sense adding the extension to the Admin menu. Actually the Admin item does not even exist, if the user has not been authorized.

Finally we extract the Admin menu item ($adminItem), which is an instance of SMMenuItem. We create a new SMMenuItem, which is our new link to the MyTest extension, and add it to the Admin menu item.

Notice that the link added is not permanent - it is not added to the data source, since no Commit function is invoked.

It is also possible to create a new menu item in the root of the menu - see snippet below

$menu = SMMenuManager::GetInstance();
$newChild = new SMMenuItem("MyTestLink", "MyTest", SMExtensionManager::GetExtensionUrl("MyTest"));
$menu->AddChild($newChild);
Extension with multiple pages and menu items

The following example is a bit more complicated, but will demonstrate how flexible extensions are. It will also demonstrate the use of some of the GUI components. Furthermore it will demonstrate how a link can be made available to the link picker in the menu extension, allowing the ordinary user to create a permanent link to some functionality.


Main.class.php

<?php

require_once(dirname(__FILE__) . "/SettingsPage.class.php");
require_once(dirname(__FILE__) . "/SettingsDisplayPage.class.php");

class MyTest extends SMExtension
{
    private $smMenuExists = false;

    public function PreInit()
    {
        // We only interact with the menu extension if it is installed
        $this->smMenuExists = SMExtensionManager::Import("SMMenu", "SMMenu.classes.php");
    }

    public function InitComplete()
    {
        // During InitComplete it is possible to add links to the link picker in the menu
        // extension. We only add the link if it is needed though (if the link picker is
        // being executed).

        if (SMMenuLinkList::GetInstance()->GetReadyState() === true)
        {
            // Notice the unique URL parameter MyTestPage (prefixed with the name of the extension).
            $menuLinkList = SMMenuLinkList::GetInstance();
            $menuLinkList->AddLink("Developing extensions", "My Test extension", SMExtensionManager::GetExtensionUrl("MyTest") . "&MyTestPage=DisplaySettings");
        }
    }

    public function Render()
    {
        $page = null;

        // Get the MyTestPage parameter from the URL ($_GET may be used instead if prefered).
        // An instance of a page is created, depending on the MyTestPage value from the URL.

        if (SMEnvironment::GetQueryValue("MyTestPage") === "Settings")
            $page = new MyTestSettingsPage($this->context);
        else if (SMEnvironment::GetQueryValue("MyTestPage") === "DisplaySettings")
            $page = new MyTestSettingsDisplayPage($this->context);

        $content = "";

        if ($page !== null)
            $content = $page->Render();

        return $content;
    }

    public function PreTemplateUpdate()
    {
        // During PreTemplateUpdate it is possible to add a link to the menu,
        // if the menu extension is installed, and the user is authorized (logged in).

        if ($this->smMenuExists === true)
        {
            if (SMAuthentication::Authorized() === true)
            {
                // Notice the unique URL parameter MyTestPage (prefixed with the name of the extension).
                $adminItem = SMMenuManager::GetInstance()->GetChild("SMMenuAdmin");
                $settingsLink = new SMMenuItem("MyTestSettings", "MyTest", SMExtensionManager::GetExtensionUrl("MyTest") . "&MyTestPage=Settings");
                $adminItem->AddChild($settingsLink);
            }
        }
    }
}

?>
SettingsPage.class.php

<?php

// Notice how the class is prefixed with the name of the extension (MyTest).
// This is very important in order to avoid conflicts with other extensions.

class MyTestSettingsPage
{
    private $context;

    // GUI controls
    private $txtName;
    private $txtAge;
    private $cmdSave;

    private $Saved;

    public function __construct(SMContext $context)
    {
        $this->context = $context;
        $this->saved = false;

        $this->createControls();
        $this->handlePostBack();
    }

    public function createControls()
    {
        // Notice the unique ID MyTestName (prefixed with the name of the extension)
        $this->txtName = new SMInput("MyTestName", SMInputType::$Text);
        $this->txtName->SetAttribute(SMInputAttributeText::$Style, "width: 150px");
        $this->txtName->SetAttribute(SMInputAttributeText::$MaxLength, "250");

        // Restore value from global attribute collection,
        // if post back (submit) has not yet been performed.
        // Notice the unique attribute name MyTestName (prefixed with the name of the extension).

        if ($this->context->GetForm()->PostBack() === false && SMAttributes::GetAttribute("MyTestName") !== null)
            $this->txtName->SetAttribute(SMInputAttributeText::$Value, SMAttributes::GetAttribute("MyTestName"));

        $this->txtAge = new SMInput("MyTestAge", SMInputType::$Text);
        $this->txtAge->SetAttribute(SMInputAttributeText::$Style, "width: 150px");
        $this->txtAge->SetAttribute(SMInputAttributeText::$MaxLength, "250");

        // Restore value from global attribute collection,
        // if post back (submit) has not yet been performed.
        // Notice the unique attribute name MyTestAge (prefixed with the name of the extension).

        if ($this->context->GetForm()->PostBack() === false && SMAttributes::GetAttribute("MyTestAge") !== null)
            $this->txtAge->SetAttribute(SMInputAttributeText::$Value, SMAttributes::GetAttribute("MyTestAge"));

        // Notice the unique ID MyTestSave (prefixed with the name of the extension).
        $this->cmdSave = new SMLinkButton("MyTestSave");
        $this->cmdSave->SetIcon(SMImageProvider::GetImage(SMImageType::$Save));
        $this->cmdSave->SetTitle("Save");
    }

    private function handlePostBack()
    {
        // Check whether a post back (page submit) was made on the form.
        // Since we also check whether the button performed a post back, this
        // check could be skipped.

        if ($this->context->GetForm()->PostBack() === true)
        {
            // Check whether save button was clicked (performed post back)
            if ($this->cmdSave->PerformedPostBack() === true)
            {
                // Save the data in the global attribute collection. Creating a data
                // source just to save two small values is a bit too much.
                // Notice the unique names MyTestName and MyTestAge (prefixed with the name of the extension).

                SMAttributes::SetAttribute("MyTestName", $this->txtName->GetValue());
                SMAttributes::SetAttribute("MyTestAge", $this->txtAge->GetValue());

                $this->saved = true;
            }
        }
    }

    public function Render()
    {
        $content = "<h1>Settings</h1>";

        if ($this->saved === true)
            $content .= "Settings successfully saved <br><br>";

        $content .= " Name: " . $this->txtName->Render();
        $content .= " Age: " . $this->txtAge->Render();
        $content .= " " . $this->cmdSave->Render();

        return $content;
    }
}

?>
SettingsDisplayPage.class.php

<?php

// Notice how the class is prefixed with the name of the extension (MyTest).
// This is very important in order to avoid conflicts with other extensions.

class MyTestSettingsDisplayPage
{
    private $context;

    public function __construct(SMContext $context)
    {
        $this->context = $context;
    }

    public function Render()
    {
        $content = "<h1>Settings</h1>";

        $name = SMAttributes::GetAttribute("MyTestName");
        if ($name === null || $name === "")
            $name = "<i>Not specified</i>";

        $age = SMAttributes::GetAttribute("MyTestAge");
        if ($age === null || $age === "")
            $age = "<i>Not specified</i>";

        $content .= "Name: " . $name;
        $content .= "<br> Age: " . $age;

        return $content;
    }
}

?>
Go ahead and test the MyTest extension. Browse to the settings page by clicking the new link in the Admin menu. Type in some data, and hit the Save button with the mouse. Then create a new link in the menu extesion with the title Display Settings. Click the icon right to the URL input field and select the "My Test extension" item from the drop down list. Click Create. You may now browse to the page displaying the settings using the navigation menu.


Where to go now

The examples above are just some basic examples of what can be achieved with extensions for Sitemagic CMS. The documentation provides excellent examples of how to use data sources to store data, work with templates, add support for different languages, inserting GUI controls and much more. Browse the menu for more details.