Override (almost) any core file in Concrete CMS

This is a community-contributed tutorial. This tutorial is over a year old and may not apply to your version of Concrete CMS.
Jun 8, 2015
 Note: This tutorial may not work in every situation. See the official documentation on extending Concrete.

When building complex Concrete CMS sites, you may find yourself needing to change some core functionality. Modern versions of concrete offer a few ways that we can do this without sacrificing updatability or performance.

Disable caching

First, turn off overrides caching via Dashboard > System & Settings > Optimization > Cache & Speed Settings. Until this is turned off Concrete CMS won't look in the application directory unless it knows it has a file in there. This will save you from a lot of headaches when developing and testing these overrides.

Service Provider Overrides

Most of concrete's core functionality is bundled into what we call "services", you can find services in subdirectories of the /concrete/src/ directory.
In order to start these services up, concrete employs a pattern known as "Service Provider". A service provider manages booting up a service and adding it onto our Application instance so if we want to modify what a service is doing, this is the place to start.

For example, what if you wanted to swap the core logger with a custom one?

  1. Open up the core app config group, this is located in /concrete/config/app.php
  2. Find where we list "LoggingServiceProvider" and determine the config key. It looks to be app.providers.core_logging
  3. Open up our application's app config group, this is located in /application/config/app.php
  4. Change app.providers.core_logging to our own custom class

In /application/config/app.php:

<?php

return array(
    'providers' => array(
        'core_logging' => '\Application\Src\Custom\CustomLoggingServiceProvider'
    )
);

Now we've overridden the core logging service provider with our own, but the class we offered CustomLoggingServiceProvider doesn't exist since we just made it up.

In /application/src/Custom/CustomLoggingServiceProvider

<?php
namespace Application\Src\Custom;

class CustomLoggingServiceProvider extends \Concrete\Core\Service\Provider
{

    public function register()
    {
        $my_psr3_logger = new \Some\Custom\Logger();

        $this->app->bind('log', $my_psr3_logger);

        // Set the rest of the bindings that the core sets
        $this->app->bind('Concrete\Core\Logging\Logger', 'log');
        $this->app->bind('Psr\Log\LoggerInterface', 'log');
    }

}

Now instead of the core logging service provider, our own logging service provider is registered and our own custom logger is used everywhere!

Path Based Overrides

Many files can be overridden in concrete simply by providing a file at a specific path. Pretty much the only exclusion to what can be overridden by path is the contents of concrete/src/. Today, we can use paths to override essentially everything that is not a service.

Essentially all path overrides follow an extremely simply pattern: find the path of the file you want to override within the concrete directory, and create a file at that same path within your application directory.

For example:

Core: /concrete/blocks/some_block/view.php
Override: /application/blocks/some_block/view.php

Core: /concrete/js/some_js_file.js
Override: /application/js/some_js_file.js

Overriding Core Block Types

Concrete comes with many useful block types, but sometimes you might want to tweak how a core block functions. Some reasons you might want to override a block include:

  • Override functionality and display
  • Adding a custom template

Override functionality and display

We can add actions, change how things save, and even replace data by overriding a block controller.

For example if we wanted to override the content block to capitalize all content:

In /application/blocks/content/controller.php

<?php
namespace Application\Block\Content;

class Controller extends \Concrete\Block\Content\Controller
{
    public function view()
    {
        $this->set('content', strtoupper($this->getContent()));
    }
}

That's not all we can override, we can do this same thing with any file in the /concrete/blocks/content/ directory. All you need to do is create the file in your /application/blocks/content/ directory!

Adding a Custom Template

Custom Templates allow us to create custom views for blocks that we can optionally enable.

For example, to modify the HTML block to render as code:

In /application/blocks/html/templates/code_block/view.php

<pre><code><?= h(trim($content)) ?></code></pre>

Custom templates can be used to replace any javascript, css, or view files but they cannot be used to replace the controller.

Path Based Examples

  • Themes
    • Core Path: /concrete/themes/theme_handle/page_theme.php
    • Package Path: /packages/package_handle/themes/theme_handle/page_theme.php
    • Override Path: /application/themes/theme_handle/page_theme.php
    • Override Class Name: \Application\Theme\ThemeHandle\PageTheme
  • Blocks
    • Core Path: /concrete/blocks/block_handle/controller.php
    • Package Path: /packages/package_handle/blocks/block_handle/controller.php
    • Override Path: /application/blocks/block_handle/controller.php
    • Override Class Name: \Application\Block\BlockHandle\Controller
  • Attributes
    • Core Path: /concrete/attributes/attribute_handle/controller.php
    • Package Path: /packages/package_handle/attributes/attribute_handle/controller.php
    • Override Path: /application/attributes/attribute_handle/controller.php
    • Override Class Name: \Application\Attribute\AttributeHandle\Controller
  • Authentication Types
    • Core Path: /concrete/authentication/type_handle/controller.php
    • Package Path: /packages/package_handle/authentication/type_handle/controller.php
    • Override Path: /application/authentication/type_handle/controller.php
    • Override Class Name: \Application\Authentication\TypeHandle\Controller
  • Controllers
    • Core Path: /concrete/controllers/single_page/page_name.php
    • Package Path: /packages/package_handle/controllers/single_page/page_name.php
    • Override Path: /application/controllers/single_page/page_name.php
    • Override Class Name: \Application\Controller\SinglePage\PageName
  • Single Pages
    • Core Path: /concrete/single_pages/account/avatar.php
    • Override Path: /application/single_pages/account/avatar.php
  • Elements
    • Core Path: /concrete/elements/account/menu.php
    • Override Path: /application/elements/account/menu.php
  • Compiled CSS
    • Core Path: /concrete/css/image-editor.css
    • Override Path: /application/css/image-editor.css
  • Compiled JS
    • Core Path: /concrete/js/jquery.js
    • Override Path: /application/js/jquery.js
Recent Tutorials
Customize locale icons
Oct 29, 2024
By myq.

How to customize locale (language region) flags

Concrete CMS Caching Guide
Oct 16, 2024

An overview of types of caching in Concrete and considerations when using them.

Redirect all requests to HTTPS
Oct 9, 2024
By myq.

How to follow best practices for a secure web

Upgrade Concrete versions 9.3.1 and 9.3.2
Sep 10, 2024
By myq.

How to get past a bug in versions 9.3.1 and 9.3.2 that prevents upgrading the Concrete core through the Dashboard

How to use Composer with Marketplace extensions
Aug 22, 2024

Composer can be used to manage third-party extensions from the marketplace

Controlling Google Tag Manager Tags Based on Concrete CMS Edit Toolbar Visibility
Aug 13, 2024

This document provides a step-by-step guide on how to control the firing of Google Tag Manager (GTM) tags based on the visibility of the Concrete CMS edit toolbar. It explains how to create a custom JavaScript variable in GTM to detect whether the edit toolbar is present on a page and how to set up a trigger that ensures GTM tags only fire when the toolbar is not visible. This setup is particularly useful for developers and marketers who want to ensure that tracking and analytics tags are not activated during content editing sessions, thereby preserving the accuracy of data collected.

Improvements?

Let us know by posting here.