Concrete CMS 5.7: Add-On Development, Part 1

Apr 22, 2014

If you're a developer of Concrete CMS websites or add-ons that run on those websites, you should be ready for version 5.7. In this how-to, I'm going to go through how I got a relatively simple add-on ready for 5.7.


  1. Since writing this add-on, packages have changed slightly in 5.7. The controller file now needs to be namespaced. I've updated the code below (see Controller). Thanks to cpillz and exchangecore for pointing this out.


Don't know anything about Concrete 5.7? Check out this overview..

Don't know anything about the underlying changes coming in 5.7? Check out my post on the subject.

Want to get into the updated code base? 5.7 has its own Github repository.

The Add-On

For this tutorial, I wanted a relatively simple add-on that had some dashboard pages and at least one block. I found one here, in community member jordanlev's Email List Signup add-on. Download the add-on to get a sense as to what the code looked like prior to this how-to.

General Code Cleanup

Old Loader Methods

While it's not required, you can remove any calls to Loader::model() and Loader::library from within your add-ons. These methods no longer do anything. Your classes should load automatically, provided your code follows the Concrete modified PSR-4 standard.

Get Current Page

Anywhere your code uses "global $c" to get the current Concrete page object is not likely to work any longer. Instead, use this code to retrieve the current Page from most contexts:

$c = Page::getCurrentPage();

The Controller

Namespacing and Class Naming

We're going to need to namespace the controller – and then make sure that any classes we're using inside the controller are declared at the top in the global, non-namespaced scope:

namespace Concrete\Package\EmailListSignup;
use Package;
use BlockType;
use SinglePage;
use Loader;
class Controller extends Package {

Email List Signup Block

Namespacing and Class Naming

This add-on contains one block, the email_list_signup block. You don't have to restructure this block at all, or rename any files in it. You do, however, have to add some namespacing and "use" directives to the top of the block controller file. Here's how the block controller looked originally:

<?php defined('C5_EXECUTE') or die(_("Access Denied."));
class EmailListSignupBlockController extends BlockController {

And here's how it looks now:

namespace Concrete\Package\EmailListSignup\Block\EmailListSignup;
use \Concrete\Core\Block\BlockController;
use UserInfo;
use Loader;
use \Concrete\Package\EmailListSignup\Models\EmailListSignup as EmailListSignup;
use Config;
use Page;
use View;
class Controller extends BlockController {

Hopefully what's happening here is pretty obvious. All the class files within this add-on will generally have to be namespaced within the Concrete\Package\EmailListSignup namespace. The block controller has to be within the Concrete\Package\EmailListSignup\Block\EmailListSignup namespace.

After that, it's a simple matter of ensuring that all classes referenced within the file are declared at the top, since none of the classes exist within the same namespace.

Finally, we name the class "Controller."

On Page View Event

The on_page_view() method in Concrete block controllers used to fire every time a block of that type was included on a page. This let blocks include assets in headers. This method was separated from the general view() method of the block controller, because it had to fire earlier in the dispatcher flow than view() used to, in order to inject assets into the header of the page.

This is no longer a requirement. You can add assets to the header and footer of a page in the view() method. Consequently, while on_page_view() will work for some things still, it is deprecated. It wasn't working for what Jordan had wanted it to do, so I moved his code into the view() method.

Email List Signup Model

The EmailListSignup model found in the models directory is a simple database access class that extends the now deprecated Model class in Concrete. We have removed ADODB, but kept a polyfill around for legacy purposes. Here's what the EmailListSignup model used to look like:

<?php defined('C5_EXECUTE') or die(_("Access Denied."));
class EmailListSignup extends Model {

Here's what it looks like now:

namespace Concrete\Package\EmailListSignup\Models;
use \Concrete\Core\Legacy\Model;
use Loader;
class EmailListSignup extends Model {

Our legacy Model class works as a drop-in for the old ADODB ActiveRecord model.


Controller Reorganization

Our new autoloading standard has some different directory naming requirements. The add-on used to be structured in this way:

|- dashboard
   |- reports
      |- email_list_signups.php

Now, since controllers and views don't require pages to operate, we need another level to separate page-less controllers from those that are page controllers. Here is how the add-on's controllers directory is now structured.

|- single_page
   |- dashboard
      |- reports
         |- email_list_signups.php


Finally, we need to namespace the Email List Signup dashboard page controller. This is our old controller:

<?php defined('C5_EXECUTE') or die(_("Access Denied."));
class DashboardReportsEmailListSignupsController extends Controller {

This is new page controller:

namespace Concrete\Package\EmailListSignup\Controller\SinglePage\Dashboard\Reports;
use \Concrete\Core\Page\Controller\DashboardPageController;
use Loader;
use \Concrete\Package\EmailListSignup\Models\EmailListSignup;

class EmailListSignups extends DashboardPageController {

In general, this should be similar to the namespacing of the Block controller above.

That's It!

That should get us an add-on that works and installs! While this gets you an add-on that works and installs, it's not the most beautiful add-on in the world. Part two of this how-to will cover how we can make our add-on look nicer in the world of 5.7, both on the front-end and in the dashboard.

Recent Tutorials
Generate a report with author information and form summaries in Concrete CMS.
May 9, 2022

In Concrete CMS, you can use a form to initiate contact between logged-in users and then create helpful reports. After form submissions are collected, they can be searched, sorted, and exported as a spreadsheet. This tutorial will detail how to add author information to a report using the advanced search.

How to clone and customize Atomik theme
Feb 14, 2022
By linuxoid.

How to clone and customize Atomik theme

Update jQuery to 3.5 on Concrete CMS version 8.5.x
Dec 1, 2021
By hissy.

If you have to take some time to fix your site to work with version 9 and want to update jQuery immediately, you can override it.

Add-On Developers: Get Your Add-Ons Ready for Concrete CMS 9.0
Aug 6, 2021
By andrew.

Concrete CMS 9.0 is coming! But there are some changes in version 9 that might affected your add-ons and themes. This document aims to answer questions about the most common ways that your add-ons might need to be changed, and common problems you'll run into.

Permissions for editors in a multilingual site
Jun 2, 2021
By myq.

How to set up a multilingual Concrete CMS site for groups of language-specific editors

Getting Started with Doctrine in Concrete CMS
Jan 20, 2021
By linuxoid.

Doctrine is a very flexible, simple (once you get to know it better) and powerful PHP library for database interactions primarily focused on the ORM = Object Relational Mapping and DBAL = DataBase Abstraction Layer.

Was this information useful?
Thank you for your feedback.