This is the documentation for concrete5 version 5.6 and earlier. View Current Documentation

When developing an addon with one or more dashboard pages, we are faced with a choice of where to place them.

  • In an existing dashboard section
  • In a System & Settings section
  • To create a new dashboard section

For many addons, placing its dashboard pages in an existing section that deals with similar functionality makes sense. For example, my addon Backup Voodoo is located in System & Settings > Backup and Restore.

For addons that provide several related dashboard pages, giving the addon its own dashboard area makes sense. My own Zone Based Shipping started life within Dashboard > eCommerce, but new features soon grew it to four dashboard pages. With the prospect of more to come it made sense for all these pages live in their own dashboard section as Dashboard > Zone Based Shipping.

The difficult decision comes when an addon does something different to anything currently in the dashboard. Should the use of an existing dashboard section be twisted? Should a new section be started for just one page?

My solution is take a step back and consider a new category that could be shared with other new addons, either addons I have planned or addons from other developers. But either way a general category that encompasses the purpose of my addon and allows scope for other addons covering adjoining functionality to share that section of the dashboard.

My first attempt at this was with my jQuickie and Style Quickie addons. Here I warn that this is not the best way to do it. Whichever is installed first owns and names the dashboard section and the other then resides within this. The trouble is, that makes it impossible to uninstall that first addon without orphaning the other addon pages. The uninstall logic then becomes convoluted to guard against that and inflicts similar convoluted thinking on any further addons using that dashboard area.

When developing Flags, I had to rethink how to do this, which is what I am coming to next. First my rationale.

Initially I thought to add Flags under System & Settings > Basics, because Language is there. I actually placed it there for a while when I began development. But while flags could be considered eye candy for internationalisation, it isnt really anything to do with languages. This section is also already getting crowded with a number of addons that provide single enable/disable pages.

I also considered putting it under Dashboard > Pages & Themes, but it didn’t really fit that either. Pages & Themes is more about pages and page design.

So I took a step back and came up with Dashboard > World. I have some other development work in progress that will comfortably fit there and I am sure other developers will also find the section useful. To make this possible, I needed to re-think how the installer would treat the shared pages.

First off, in the package controller, if we ignored other requirements the installation of the Flags dashboard page would be:

public function install() {
    $pkg = parent::install();
    Loader::model('single_page');
    $p = SinglePage::add('/dashboard/world/flags/', $pkg);
    $p->update(array('cName' => 'Flags', 'cDescription' => '…'));
}

concrete5 would then also create Dashboard > World at the path '/dashboard/world/', but also under the ownership of Flags. Should Flags be uninstalled, anything subsequently beneath '/dashboard/world/' would become an orphan.

The solution was to deliberately create the Dashboard > World section, but without any package ownership:

public function install() {
    $pkg = parent::install();
    Loader::model('single_page');
    $wp = Page::getByPath('/dashboard/world/');
    if($wp->isError()){
        $wp = SinglePage::add('/dashboard/world/', null);
    }
    $p = SinglePage::add('/dashboard/world/flags/', $pkg);
    $p->update(array('cName' => 'Flags', 'cDescription' => '…'));
}

Because other addons that also use Dashboard > World could have been installed first, there is a test to see if the page '/dashboard/world/' already exists. The key part of the install is that SinglePage::add() is passed 'null' instead of a $pkg object, so the page at '/dashboard/world/' is not owned by any package and hence will not be uninstalled automatically with the addon.

Any addon following this pattern can now safely install single pages within Dashboard > World without too much risk to other addons sharing this dashboard section. Of course, we have to be sensible about naming pages and paths. We cant have 10 addons all installing a page named Configure in here!

When any addon following this pattern is uninstalled, Dashboard > World will remain intact - which introduces another issue: when the last addon is uninstalled, what happens to the World?

The solution strategy is the proverbial "Can the last person to leave please switch off the lights". Each package following the above installer pattern also needs to provide an uninstaller that runs the regular uninstall before testing if Dashboard > World is empty and uninstalling that also.

public function uninstall() {
    parent::uninstall();
    $wp = Page::getByPath('/dashboard/world/');
    if (is_object($wp) && !$wp->getFirstChild()){
        $wp->delete();
    }
}

We only need to test for getFirstChild(), as any child indicates we are not the last one to leave the room.

This works so nicely I will be re-engineering jQuickie and Style Quickie to use it instead of their existing messy implementation.

For other developers, please feel free to make use of Dashboard > World or to share other new dashboard areas. Maybe we can discuss them in the forums to come up with some general organisation. All I ask is to play nicely with page ownership for shared sections and can the last person out please switch off the lights!

Read more How-tos by JohntheFish

Loading Conversation