ExpressionEngine Docs

Add Control Panel Pages To Your Add-On

If you are working with an existing add-on, we recommend you start with Modernizing add-ons

Overview

If you have ever used some of the add-ons that ship with ExpressionEngine such as Block and Allow or Pro Search, you will notice those add-ons have settings and configuration pages associated with them in the Control Panel. You add this functionality to your add-on using Control Panel Routes.. Whenever you add a Control Panel Route to your add-on using the CLI, a ControlPanel/Routes and views folder is automatically created for you, opening the door to creating your own Control Panel settings and pages.

NOTE: Control Pages are what is rendered in the browser when visiting your add-on in the Add-on Manager. Control Panel Routes are what we had to our add-on that tells ExpressionEngine to render the pages. Think of it as we add a route to create a page.

Before adding a Control Panel route to your add-on, you need to already have an add-on in place. See Building An Add-On: Getting Started for how to generate the starter files for your add-on.

Creating An Amazing Control Panel Route

If your add-on doesn’t already have the required ControlPanel/Routes and views files, you can add a route to your add-on using the make:cp-route command in the CLI.

$ php system/ee/eecli.php make:cp-route
Let's create a control panel route!
What is the route name? index
What add-on is the route being added to? [amazing_add_on]:  amazing_add_on
Building control panel route.
Control panel route created successfully!

Follow the prompts to add the route to your add-on.

This will create an mcp.[addon_name].php file in your add-on along with a ControlPanel/Routes and views folder.

Inside of the ControlPanel/Routes folder, you will see that the CLI has created your first control route with ControlPanel/Routes/Index.php and a corresponding view in you views folder. This page is accessible via The Add-On Manager -> [Add-On Name] or via the /admin.php?/cp/addons/settings/[add-on-name] URL.

Create and Index route first. When your add-on has one or more Control Panel pages associated with it, your add-on’s card on the Add-On Manager page will link to the Index route of your add-on (ControlPanel/Routes/Index.php). If this route does not exist, users will be presented with a 404 page.

Anatomy Of A Control Panel Route

When you first add a route to your add-on a ControlPanel/Routes folder along with an ControlPanel/Routes/[RouteName].php starter file is created. The starter file will look something like this:

<?php

namespace ExpressionengineDeveloper\ControlPanel\Routes;

use ExpressionEngine\Service\Addon\Controllers\Mcp\AbstractRoute;

class Index extends AbstractRoute
{

    /**
     * @var string
     */
    protected $cp_page_title = 'home';

    /**
     * @param false $id
     * @return AbstractRoute
     */
    public function process($id = false)
    {
        $this->addBreadcrumb('index', 'Home');

        $variables = [
            'name' => 'Matt',
            'color' => 'Green'
        ];

        $this->setBody('Index', $variables);

        return $this;
    }
}

Let’s dissect the starter file:

$cp_page_title = 'home';

$cp_page_title defines what is rendered in the <title> tag in the browser.

function process($id = false)

The process() function, similar to other functionality in your add-on, is the meat of your route file. This is where you will render views, add sidebars, and add any necessary logic.

Adding Content To Your Page

Now that you have a page in the Control Panel that users can access, let’s look at how to output what you want users to see. There are four main areas to a Control Panel page where you will be outputting information to:

  • Breadcrumb
  • Sidebar
  • Toolbar
  • Main Body

Add-on Breadcrumbs

Breadcrumbs

Located at the top of the Control Panel screen, breadcrumbs help users easily know where they are in the Control Panel and navigate your add-on’s settings quickly.

Add-on Breadcrumbs

To add a breadcrumb for your current Control Panel page, simply use $this->addBreadcrumb().

In the starter route file created for you by the CLI you will already see $this->addBreadcrumb('index', 'Home');. This will add a breadcrumb similar to the one in the screen shot above (Add-Ons -> [Add-On Name] -> Home).

If you needed to add more levels to your breadcrumbs you can chain them together as such:

        $this->addBreadcrumb('amazing_add_on', 'Settings')
            ->addBreadcrumb('amazing_add_on/configuration', 'Configuration');

This would add a breadcrumb that would look like Add-Ons -> [Add-On Name] -> Settings -> Configuration

Sidebar

If your add-on has multiple Control Panel pages, a sidebar can be incredibly helpful for your users to easily navigate between pages. Sidebars can either be generated automatically or manually.

Both of these options start with generating your sidebar class using the CLI.

We use the make:sidebar CLI command to generate a sidebar file for us.

$ php system/ee/eecli.php make:sidebar
What add-on is the sidebar being added to? [amazing_add_on]:  amazing_add_on
Building sidebar.
Sidebar created successfully!

This will create the file ControlPanel/Sidebar.php in your add-on’s folder.

amazing_add_on
┣ ControlPanel
┃ ┣ Sidebar.php
┗ ...

Note: An add-on can only have one sidebar file.

Inside of our Sidebpar.php file we’ll have the following:

<?php

namespace ExpressionEngineDeveloper\ControlPanel\Routes;

use ExpressionEngine\Service\Addon\Controllers\Mcp\AbstractSidebar;

class Sidebar extends AbstractSidebar
{
    public $automatic = true;

    /**
     * @param false $id
     * @return AbstractRoute
     */
    public function process()
    {
    }
}

Automatically Generate Your Sidebar

If you’d like to have your sidebar automatically generated then that’s it! The Sidebar class will scan our ControlPanel/Routes folder for all available Control Panel routes and automatically create respective entries in our sidebar. There’s no need to add the sidebar to your Control Panel page as it will automatically be added when the page is rendered.

Example:

With an add-on folder like this:

amazing_add_on
┣ ControlPanel
┃ ┣ Routes
┃ ┃ ┣ Configurations.php
┃ ┃ ┣ Settings.php
┃ ┃ ┣ Licenses.php
┃ ┃ ┣ Index.php
┃ ┗ Sidebar.php
┗ ...

Would produce a sidebar in the Control Panel like this:

control panel sidebar

You can take this a step further by adjusting the following properties in your Control Panel routes (ControlPanel/Routes/[route_name].php) to adjust how sidebar items are displayed:

protected $sidebar_title (string)

By default the sidebar link text is based on your route’s name. This property will overwrite the text displayed for this route.

protected $sidebar_icon (string)

The Font Awesome icon you wish to display next to the sidebar link for this route.

protected $sidebar_priority (int)

Give this route a higher priority in the sidebar. Higher priority items will appear first in the sidebar.

protected $exclude_from_sidebar (bool)

Exclude this route from the sidebar.

protected $sidebar_divider_before (bool)

Inserts a divider in the sidebar before the link to this route.

protected $sidebar_divider_after (bool)

Inserts a divider in the sidebar after the link to this route.

You can even modify the generated sidebar in the process() method of your ControlPanel/Routes/Sidebar.php file by utilizing the CP/Sidebar Service.

Manually Generate Your Sidebar

We will set the $automatic property in our ControlPanel/Routes/Sidebar.php file to false.

public $automatic = false;

This tells our Sidebar class not to automatically create any sidebars.

From here you can utilize the CP/Sidebar Service in the process() method of our Sidebar class to manually create your sidebar and all related items.

Toolbar

The toolbar is a series of icons located in the top right of your add-on settings page. Many add-ons use this area for quick links to settings or other areas of their add-on settings.

Add-on Toolbar

We can add items to the Toolbar by passing an array into ee()->view->header.

$header['toolbar_items'] = array(
    'settings' => array(
        'href'  => ee('CP/URL','addons/settings/amazing_add_on'),
        'title' => 'settings'
    ),
    'user ' => array(
        'href'  => ee('CP/URL','addons/settings/amazing_add_on/user_settings'),
        'title' => 'Users'
    ),
    'export ' => array(
        'href'  => ee('CP/URL','addons/settings/amazing_add_on/export'),
        'title' => 'Export Data'
    )
);

ee()->view->header = $header;

The icon used in the toolbar for each link corresponds to each items title element in the array. Available icons are add,author,cart,category,caution,changes,channel,close,columns,dashboard,date,delete,export,files,filters,folder,gift,home,invisible,issue,locked,logout,members,missing,nested,offline,primary,remove,reorder,settings,status,success,sync,tabbed,tip,tools,user,view,visible,export,settings.

Main Body

Probably the most important part of your add-on’s Control Panel page is the main body.

Add-on Main body

There are two ways to output to the main body of your add-on’s page.

  • HTML String
  • Using Views

The setBody() method is smart enough to know what you’re trying to do. If you pass a single parameter to the method such as $this->setBody($myString), the method knows you are only passing a single string to be rendered on your page. However, if you pass multiple parameters such as $this->setBody('Index', $variables);, the method knows that you are passing a View and an array of variables that should be passed to the View.

Continue reading to see examples of how to use these two options.

HTML String

If you would just like to output a string of HTML for you control panel page, then you would just do something like the following:

 public function process($id = false)
    {
      $this->addBreadcrumb('index', 'Home');

      $html = "<h2>Welcome to my add-on</h2><p>This is an amazing add-on that does amazing things!"

      $this->setBody($html);

      return $this;
    }

This would produce a page looking like this:

simple MCP page

If you would like to make this page more dynamic or include additional functionality, then you need to explore using Views.

Views

When your add-on was created via the CLI a views folder was also created in your add-on’s folder. Inside of this folder a file was also created, views/Index.php. By default this Control Panel page is already using the View service by calling $this->setBody().

$variables = [
    'name' => 'Matt',
    'color' => 'Green'
];

$this->setBody('Index', $variables);

As we can see here, the route file is passing two arguments to $this->setBody():

  • Index is the name of the corresponding view to use (references views/Index.php)
  • $variables is an array of variables that will be available to the view.

Now let’s look at the view itself, found at views/Index.php.

<?php

echo "<h2>Time to make magic</h2>";

if (isset($name)) {
    echo "<p>Name: " . $name . "</p>";
}

if (isset($color)) {
    echo "<p>Color: " . $color . "</p>";
}

Notice the variables $name and $color correspond to the $variables array passed to the view in the route file.

This renders as such:

add-on rendered with view

Taking this a bit further, we can use the CP\Form service to display panels and forms similar to what is found on many pages in the Control Panel.

Using the CP\Form is highly recommend to speed up your development and to maintain the integrity of the Control Panel design.

We’ll start by using the CP\Form service in a method which will return our $form array. The $form array will then be passed to our View through our $variables array.

private function getForm()
{
  $form = ee('CP/Form');
  $form->asTab();
  $form->asFileUpload();
  $field_group = $form->getGroup('header 1');
  $field_set = $field_group->getFieldSet('first_name');
  $field_set->getField('first_name', 'text')
            ->setDisabled(true)
            ->setValue('Eric');
  
  return $form->toArray();
}

Now, let’s add that to our route file and be sure to pass the array returned from getForm() to our view.

public function process($id = false)
{

  // set the breadcrumb
  $this->addBreadcrumb('index', 'Home');

  // call our getForm() method to get
  // our array
  $form = $this->getForm();

  // store our form in our $variables array
  // to be passed into our view
  $variables = [
    'form'  => $form
  ];

  $this->setBody('Index', $variables);

  return $this;
}

private function getForm()
{
  $form = ee('CP/Form');
  $form->asTab();
  $form->asFileUpload();
  $field_group = $form->getGroup('header 1');
  $field_set = $field_group->getFieldSet('first_name');
  $field_set->getField('first_name', 'text')
            ->setDisabled(true)
            ->setValue('Eric');
  
  return $form->toArray();
}

Since we passed our form to our view in the $variables array, inside of views/Index.php we can now use the View service to render our $form array as the main body of our Control Panel page.

if (isset($form)) {
    echo ee('View')->make('ee:_shared/form')->render($form);
}

Your add-on will now have a Control Panel page with a form as seen in this screenshot:

add-on with form

Finally, make the form submission do something. The form is being submitted to same CP controller, so we can check whether it has been submitted by adding ee('Request')->isPost() check. For the sake of this demo, we’ll skip the database saving part and just display success message on submission.

public function process($id = false)
{
  if (ee('Request')->isPost()) {
    ee('CP/Alert')->makeBanner('amazing_add_on')
      ->asSuccess()
      ->withTitle(lang('success'))
      ->addToBody('Form has been submitted!')
      ->now();
  }

  // set the breadcrumb
  $this->addBreadcrumb('index', 'Home');

  // call our getForm() method to get
  // our array
  $form = $this->getForm();

  // store our form in our $variables array
  // to be passed into our view
  $variables = [
    'form'  => $form
  ];

  $this->setBody('Index', $variables);

  return $this;
}

This is only the beginning of what you can do with forms in the Control Panel. Read more in the docs on the CP\Form service to understand what else is possible.

{ee:u} Learn more about the CP\Form service on ExpressionEngine University.

Adding More Pages

Often your add-on will need more than one page in the Control Panel. The CLI makes this simple.

Start with:

php system/ee/eecli.php make:cp-route

This adds a new file to your ControlPanel/Routes folder which will act similar to the Index page we discussed above, along with a matching View in our views folder.

Now inside of your ControlPanel/Routes folder you have a new file and matching class based on the name you chose in the CLI. This page will be accessible based on the class name you provided. For example:

php system/ee/eecli.php make:cp-route
What is the route name? configurations
Which add-on would you like to add this to? amazing_add_on
Control panel route created successfully!

This will create a file in my ControlPanel/Routes folder called Configurations.php which will look like this:

<?php

namespace ExpressionengineDeveloper\ControlPanel\Routes;

use ExpressionEngine\Service\Addon\Controllers\Mcp\AbstractRoute;

class Configurations extends AbstractRoute
{

    /**
     * @var string
     */
    protected $cp_page_title = 'configurations';

    /**
     * @param false $id
     * @return AbstractRoute
     */
    public function process($id = false)
    {

        $variables = [
            'name' => 'Matt',
            'color' => 'Green'
        ];

        $this->setBody('Index', $variables);

        return $this;
    }
}

This page will now be accessible via /admin.php?/cp/addons/settings/amazing_add_on/configurations.

At this point you probably want to make sure you update your breadcrumb on your second page and create a sidebar item to ensure users can easily access this page.

If you just want to create a new View without a Control Panel Route, simply duplicate a current view and rename the file and class accordingly.

JavaScript

ExpressionEngine includes both its own JavaScript library as well as the The jQuery JavaScript library, enabling developers to easily include JavaScript enhancements. It is worth noting some ‘best practices’ when using JavaScript in your control panel:

  • Loading jQuery plugins:

    ee()->cp->add_js_script(array('plugin' => 'dataTables'));
    
  • Outputting JavaScript to the browser:

    ee()->javascript->output();
    
  • After defining any JavaScript output, you must compile in order to display it:

    ee()->javascript->compile();