Product support

Get help in the plugin support forum.


The plugin is a skeleton for building Software as a Service (SaaS) applications using OctoberCMS. SaaS is (according to Wikipedia) a software licensing and delivery model in which software is licensed on a subscription basis and is centrally hosted.

The easiest way to understand it is to imagine an application which you want to create for your clients (one application for more than one client) but it is going to be hosted on your server.

Here are some use cases where Cumulus may help:

  • system for your clients' companies where they can have their private data in cloud (your server) while other clients cannot see each other's data like invoicing system, client management system etc.
  • system for schools where classes can share some data and have access to some data while cannot see other classes data like exams system, school diary etc.
  • every system that supports cutting functionality for different plans (like "Free", "Plus", "Pro") like in the example in documentation in 'Example pricing table' section.


If you just want to see what Cumulus can do for you see the video below

Cumulus demo video thumbnail

or prepare example app by yourself:

  1. install official Cumulus theme
  2. run php artisan cumulus:seed command (see Cumulus Demo documentation for info about the command)

After that you are ready to play with Cumulus based app with demo data seeded (user demo@example.com with password demo) :).

See documentation for more about Cumulus usage and configuration.

CumulusCore extensions

Cumulus Subscriptions Cumulus Subscriptions Icon

Extend your Cumulus Core system with automatic subscription manager.

Cumulus Plus Cumulus Plus Icon

Extend your Cumulus Core system with dashboard and settings pages for clusters within seconds. The plugin also adds UpdateCluster component to easily update cluster's data from frontend pages.

Cumulus Announcements Cumulus Announcements Icon

Notify users of your system about things that concerns them, their clusters or their plans.

Other products using Cumulus Core

Cumulus Theme

Using the theme you can install bare Cumulus application with a single click of a mouse. This is only a suggestion, a scheme. Cumulus does not require this theme. It is fully up to you how you decide to use it.

Power Components Power Components icon

Power Components plugin integrates with Cumulus Core so that lists and forms generated by it can be easily filtered and accessible only for particular cluster. Together with Cumulus they create a very powerful environment for building SaaS apps.

The following plugins are required
This plugin is also included into the following bundle
The following plugins extend or depend on the plugin
The following theme uses this plugin

Terms used in Cumulus and this document

User is a frontend user from RainLab.User plugin who can log to frontend. See RainLab.User documentation for more info about this. Frontend users differs from backend admins, check "Separated frontend and backend user access" section below for more info.

Cluster is a group of users which share some data between them and can be described as one entity. The most common example is a company. But it also applies to offices, office branches, classes in school, schools etc. Cluster is not a usergroup from RainLab.User plugin (like guest, registered and so on). User groups are about permissions (like read, write, update etc.) while clusters are about organizing users in logical entities.

Plans are assigned to clusters. Cluster can have only one plan at a time. Imagine a pricing table like in the 'Example pricing table' section below. Plans in this case are "Free", "Plus", "Pro".

Feature is a part of functionality of application. The easiest explanation of features is the records in the above table. Features are registered by your plugins as described below. It is fully up to you to create functionality of your application. Features are assigned to plans so that clusters with some plan will have access to some features.

Separated frontend and backend user access

It may be difficult to understand the difference between frontend and backend users at first glance.

By design backend admin is a developer like me and you while frontend (Cumulus) user is your client. Your clients are using your application but they must not see other clients' data (of course if you do not want to). And you, as backend admin, have access to all data of your clients.

So as you can see those are two completely different points of view on the same application.


In all SaaS applications there are at least two groups of pages:

  1. publicly visible pages (where you put your offer, regulations, contact form, login form, register form etc.),
  2. pages accessible only for registered and logged in users.

In Cumulus we extend the second group of pages so that you may have also pages that are accessible only for users that are:

  1. logged in and nothing more (like manage profile page or some kind of dashboard),
  2. assigned to a particular cluster (for example cluster's dashboard page with data that can be visible for all users in the cluster),
  3. assigned to a cluster that has access to some features (for example a cluster can have access to pages concerning invoicing).

Check the "How to" section below to see how it is done.


Cumulus is using features to separate functionality and access for users. Every plugin can register it's own features using registerCumulusFeatures method in plugin registration file.

The syntax is similar to registering backend permissions. For example:

public function registerCumulusFeatures()
    return [
       'initbiz.cumulusinvoices.manage_invoices' => [
           'label' => 'initbiz.cumulusinvoices::lang.feature.manage_invoices',
           'description' => 'initbiz.cumulusinvoices::lang.feature.manage_invoices_desc',

Features are assigned to plans. So that every cluster that has particular plan has the same set of features.

Example plan

It is up to you while writing plugin how many features will it register for our clients. There must be a reasonable amount of them.

Before creating and using features it is a good idea to read about FeatureGuard component below.

Registering cluster's features

Registering features in system differs from registering cluster's features. Registering features in system is described in section right above.

Every time any cluster obtains access to any feature (for the first time, once) we call it registering cluster's feature. Registering clusters' features is the process of running some 'registering' code when cluster gets access to a feature whether by changes of cluster's plan or plan's features.

To register a feature you have to bind to initbiz.cumuluscore.registerClusterFeature event and check if it is your feature being registered right now like in the example below:

Event::listen('initbiz.cumuluscore.registerClusterFeature', function ($clusterSlug, $featureCode) {
    if ($featureCode === "initbiz.cumulusinvoices.manage_invoices") {
        // perform some registering code, for example seed tables for the cluster with sample data

The event is blocking so if you decide to stop the process of registration then return false and exception will be thrown.



As described in "TL;DR" section there is a much faster way to install Cumulus core app. If you want to play with example application in seconds then install clean OctoberCMS using Cumulus theme and run php artisan cumulus:seed command.

This will prepare very basic environment. More info about the process can be found in Cumulus Demo docs.

Full way

As defined in Concept section above, pages in Cumulus applications are in one of four groups. In Cumulus we use October's components to create page (or layout) as one of the group.

  • public pages do not require special configuration
  • pages that require user to be logged in should use Session component from RainLab.User
  • pages that require user to be assigned to particular cluster should use CumulusGuard component
  • pages that require current cluster to have access to particular feature should use FeatureGuard component

If you want to check if user is signed in, is assigned to a cluster and the cluster has access to a feature, then embed all three components (Session, CumulusGuard and FeatureGuard).

Feature guard

The cleanest way will be creating layouts with sets of component configurations instead of embedding them on every page.

More information about the components can be found below.

Example usage for our client will be as follows:

  1. User visits login page and after successful logging in he/she will be redirected to "Choose cluster" page (screenshot below)
  2. After he/she chooses cluster he/she will be redirected to the cluster's dashboard.

On "Choose cluster" page will be UserClustersList component embedded which automatically redirects user to cluster's dashboard if he/she is assigned to only one cluster.

Clusters' usernames

Clusters' usernames are unique strings to be used in URLs so that URLs can be changed by client the way they want to. The same feature in Facebook and Twitter is called username so we decided to use name username as well.

If you enable 'using usernames in URLs' in Cumulus general settings than you have to ensure that you have not been using cluster_slug variable from URL. To get cluster or its slug from URL you may use Cumulus's helpers class:

$cluster = Helpers::getClusterFromUrlParam($this->property('clusterUniq'));
$clusterSlug = Helpers::getClusterSlugFromUrlParam($this->property('clusterUniq'));

Notice however that CumulusGuard component injects cluster object to pages that use it (or their layout).

Username validation

There are two events fired where you can validate username.

The first one is initbiz.cumuluscore.beforeClusterSave which basically is fired every time you save or update cluster. It gets cluster model object as a parameter and you can stop executing and propagating to other listeners if you return false.

The second one is initbiz.cumuluscore.usernameUnique which is run in ClusterRepositoryin usernameUnique method. It gets two parameters: username and clusterSlug. It may be used for example to check if username is unique in more places than one cumulus host.

ClusterUpdate component in Cumulus Plus plugin validates the username for you using AJAX.

Login page

Login page can use Account component from RainLab.Users plugin. It should be configured so that it automatically redirects to "Choose cluster" page after successful logging in.

Login page

"Choose cluster" page

On "Choose cluster" page should be UserClustersList component embedded. It will automatically redirect user to cluster's dashboard if he/she is assigned to only one cluster.

Choose cluster page

Cluster's dashboard

Welcome screen for everyone in the cluster. Place for statistics (and Cumulus Plus component).

Cluster's dashboard page

From this level every page's url should contain cluster slug variable. By default it is :cluster but it can be changed in component. So from now all pages will have url similar to this:

Cluster's dashboard page view

Feature pages

Every page of your system that provides some functionality is considered as "Feature page". So here is where Cumulus cannot help anymore (and tries to not disturb you with unnecessary code bloat).



The components role is to render view to select cluster if user is assigned to more than one cluster.

Note: If user is assigned to one cluster then the component will automatically redirect to Cluster dashboard page

Clusters list component


The CumulusGuard component is here to check if the logged in user has access to cluster that he/she tries to visit. It uses cluster slug from url.

What is more, the component pushes two variables to view:

  • cluster which contains current cluster's slug
  • clusterData which contains array of the current cluster data.

and sets active cluster's slug in session variable and cookie as cumulus_clusterslug.

FeatureGuard Feature guard is a component which ensures if current cluster can see the page based on features it has access to.

Remember that only one of checked features is enough to let the user see the page

Feature guard

If you want to filter content on one page so that only a cluster that has access to this feature can see it use canEnterFeature twig function described below.


In Cumulus we decided to use repositories to access data from DB.

That is because we had problems with models using models using models (this inception O.o) and problems with organization of useful methods since Models are just representations of tables in database and not the entire data.

Of course under the hood there are typical Eloquent models, so if you want to use them go ahead.

To use clusterRepository you have to create the object as in the example below:

use Initbiz\CumulusCore\Repositories\ClusterRepository;
$clusterSlug = $this->property('clusterUniq');
$clusterRepository = new ClusterRepository($clusterSlug);
$clusterData = $clusterRepository->getCurrentCluster();


canEnterCluster(int $userId, string $clusterSlug) - check if user can enter given cluster

canEnterFeature(string $clusterSlug, string $featureCode) - check if cluster can enter the feature (using its plan)

getClustersUsers(array $clustersSlugs) - get array of users in given clusters array (can be more than one)

getClusterFeatures(string $clusterSlug) - get array of cluster's features

addUserToCluster(int $userId, string $clusterSlug) - adds user to cluster

addClusterToPlan(string $clusterSlug, string $planSlug) - assigns a plan to cluster

getClustersPlans(array $clustersSlugs) - get array of clusters that has given plans (can be more than one)


getPlansUsers(array $plansSlugs) - the method takes array of plans slugs and get all users that are in those plans.


addUserToGroup($userId, $groupCode) - adds user to group (RainLab.UserGroup)

activateUser($userId) - activate user (by default users are not active)

Auto assign

Auto assigning is Cumulus functionality that automatically assigns users and clusters during their registering. You can create a lot of different configurations so it should meet you expectations.

Go to Settings -> Cumulus -> Auto assign where you wil find two tabs: "Auto assign users" and "Auto assign clusters".

Auto assign users

Auto assign users

While auto assigning users to clusters you can decide whether you want to:

  • create new cluster using variable specified in "variable name" (for example company name)
  • choose existing cluster for every newly registered user
  • get cluster slug from variable

You can also decide whether you want to add user to a group (RainLab.UserGroup) after registering or not.

Auto assign clusters

Auto assign clusters

While auto assigning clusters to plans you can decide if you want to:

  • assign user to concrete plan (in most cases something like Free or Trial) or
  • get plan from variable (if you have more then one plan that cluster can be assigned)

Please note that:

  • auto assigning clusters will work only if creating new cluster is enabled in "Auto assign users" tab
  • auto assigning clusters to plans from variable will be possible only when you allow that in the plan definition

ClusterFiltrable trait

As you may have noticed, data in database will not be filtered automatically to your clusters. You have to do it by yourself. The ClusterFiltrable trait will be useful in this case.

Just use it in your model as in the example:

class Course extends Model
    use \Initbiz\CumulusCore\Traits\ClusterFiltrable;

If you want to use clusterFiltered() method without any parameters than add cluster_slug attribute to your model where you will be storing the owning cluster's slug. If you use id than you will have to add parameters to clusterFiltered() method as described below.

clusterFiltered($value = '', $attribute = 'cluster_slug') scope

The method is a Laravel scope, so it is very easy to use it on models when you want it. Just add clusterFiltered() to your query and done.


The method gets two optional parameters. The first is a value and the second is an attribute. If you do not specify any of them, then the scope will use current cluster slug and tries to use cluster_slug attribute in the model.

If you want to use cluster_id instead of cluster_slug then you will have to run the method like

ExampleModel::clusterFiltered($clusterId, 'cluster_id')->get();

clusterUnique($attribute, $table = null, $columnName = 'cluster_slug')

The ClusterFiltrable trait adds clusterUnique method as well. The method returns validation rule (string) for October's validator (more about validation can be found here).

Parameters to this method work similar to clusterFiltered method described above.

The method returns string of validation rule. You can use the rule in model's constructor. Let's say we want to check if invoice_number is unique in cluster (while other clusters can safely have the same number).

public function __construct(array $attributes = array())
    $this->rules['invoice_number'] = $this->clusterUnique('invoice_number');

If you want to specify table name or column name to build unique rule, than you have to use parameters in the method. By default it will use $this->table attribute and cluster_slug as a column name, for example:

$this->rules['invoice_number'] = $this->clusterUnique('invoice_number', 'invoices', 'cluster_id');

Twig extensions


If you want to check in twig if current cluster has access to "feature.code" than use canEnterFeature('feature.code') Twig function.

For example:

{% if canEnterFeature('initbiz.cumulusdemo.paid_feature') %}
    Something visible only to those who have access to initbiz.cumulusdemo.paid_feature.
{% endif %}

Rainlab.User note

The plugin extends RainLab.User plugin and uses the same User model, so if you want to restrict backend admin to manage users remember that there is controller from RainLab.Users that uses the same Model and can access the same data.

Menus / Navigation

Cumulus extends RainLab.Pages plugin to build menus.

Version 1.2.20 of RainLab.Pages is required

Static menu in cumulus

As you can see there are two new things. The first is a menu item type: Cumulus page. It defines items that have cluster_slug in URLs and the cluster slug will be injected to URL. The second is "Cumulus" tab. Under the tab you can choose features that are required to see the menu item for cluster. If none specified then everybody can see it. But if any feature is checked than the cluster must have access to it to see the menu entry. What is more, cluster can have access to just one of the features and entry will appear.


I cannot see my registered features

If you cannot see your features then go to Settings -> Cumulus -> Features and click Clear feature cache button.

Future plans (TODO)

  • Component that automatically builds features table


Every contribution is very welcomed, especially from professional devs who can suggest better organization of code. Thanks for you time in advance :)

Doc helpers etc.

Example pricing table

Here you have an example pricing table with plans that you can create using Cumulus. The image is here only for reference purposes and is not related with Cumulus itself.

Pricing table example

  • Found the plugin useful on 19 Jun, 2019

    So many possibilities for creating an excellent subscription based SAAS application - quite brilliant!

  • author

    Replied on 19 Jun, 2019

    Thanks for the review :)

    More features and extensions for Cumulus are under development so stay tuned!

  • Found the plugin useful on 22 May, 2018

    Thank you for awesome plugin!

  • author

    Replied on 19 Jun, 2019

    Thank you for your review! :)


Added allow user registration checkbox to plans

Jul 03, 2019


Fixed seeding after soft deletion

Mar 22, 2019


Fixed typo

Mar 22, 2019


Added softDelete trait for Plan Model

Mar 11, 2019


Added softDelete trait for Cluster Model

Mar 11, 2019


Fixed buggy migration

Feb 21, 2019


Now working with Cumulus Theme again

Feb 18, 2019


Fixed assigning clusters to empty plans

Feb 17, 2019


Cleaned translations and added Polish translation

Feb 12, 2019


Small fix of auto assigning

Feb 08, 2019


Small fix with registering cluster features

Dec 18, 2018


!!! Moved some helpers to Initbiz.InitDry plugin. Install the plugin if want to proceed.

Dec 13, 2018


Registration of clusters features support added

Dec 10, 2018


!!! Username for cluster added. Read the upgrade guide.

Nov 17, 2018


Better fix for the previous drop

Nov 09, 2018


Dropping foreign for MySQL fixed

Nov 09, 2018


!!! Before upgrading to 2.0.0 read the upgrade guide

Nov 08, 2018


Added helper getFileListToDropdown

Oct 09, 2018


User-cluster relation fixed

Oct 05, 2018


View fixes, translations and plan filter in cluster list

Oct 05, 2018


Cleaned registering and extending views

Sep 07, 2018


Added backend permissions to restrict access to controllers and settings.

Sep 07, 2018


Added empty cluster dashboard for better permissions managing

Sep 07, 2018


Fixed extending RainLab.User backend menu by other plugins

Sep 07, 2018


Removing cookie and session cluster_slug on logout + small fix

Sep 07, 2018


Added unique in cluster scope validation rule + minor fixes

Aug 17, 2018


Added handling transactions on user register

Jul 06, 2018


!!! Updated repository interface. Temporarily removed validation. Refactored cluster repository + minor fixes

Jun 27, 2018


Fixed typo

Jun 25, 2018


!!! Changed method getClusterModulesName to getClusterModulesSlugs + optimized cluster repo

Jun 19, 2018


!!! Removed cumulusDashboard and cumulusSettings components. They have been moved to CumulusPlus extension

Jun 19, 2018


Fixed recurrent dependencies in repositories

Jun 11, 2018


Set event listeners priorities

Jun 11, 2018


First Cumulus extension - Cumulus Announcements: octobercms.com/plugin/initbiz-cumulusannouncements

May 31, 2018


Created plan repo and added some useful methods to existing ones

May 30, 2018


Cleanup modules creating and listing them in settings

May 27, 2018


Updated table initbiz_cumuluscore_modules -> added nullable description column

May 27, 2018


Added ClusterFiltrable trait for models to easy filter their data using cluster_slug property

May 24, 2018


Funny part here. Both session and cookie should have been used. They are named cumulus_clusterslug :)

May 24, 2018


Changed the concept from session to cookie: cumulus_clusterslug

May 24, 2018


Put current cluster slug to Session with key: initbiz.cumuluscore.currentClusterSlug

May 23, 2018


Firing events: addClusterToPlan and addUserToCluster

May 18, 2018


Dropping foreign

Mar 16, 2018


Small organizational fixes

Mar 08, 2018


Fixed MySQL refreshing plugin

Jan 30, 2018


Fixed MySQL installation

Jan 30, 2018


Beautified cluster view in backend

Jan 26, 2018


Moved Cluster slug property from all components to CumulusComponentProperties trait

Jan 22, 2018


Changed texts to strings, extended cluster table

Jan 20, 2018


Fixed auto assigning with tests

Jan 17, 2018


Managing automatically adding users and clusters from settings view + cleanup and small fixes

Jan 16, 2018


Added unique at slug in Modules table

Jan 10, 2018


Added dynamic method onRedirectMe for building login pages

Jan 04, 2018


Fixed one to many relation between Plan and Cluster

Nov 09, 2017


After clean up, Cumulus is ready to be published on Marketplace

Oct 11, 2017


Created table initbiz_cumuluscore_plan_module

Oct 10, 2017


Created table initbiz_cumuluscore_plans

Oct 10, 2017


Created table initbiz_cumuluscore_modules

Oct 10, 2017


Created table initbiz_cumuluscore_cluster_user

Oct 10, 2017


Created table initbiz_cumuluscore_clusters

Oct 10, 2017


Initialize plugin.

Oct 10, 2017

from v.2.0.2 to v.2.0.3

Introducing Clusters' usernames

Clusters' usernames is a new feature of Cumulus, where your users can specify their "username" in URL. See documentation for more info about the feature.

While installing this version Cumulus will by default copy Clusters' slugs to their usernames so by default usernames will be seeded and everything should work out of the box if you enable using usernames.

ClusterSlug becomes ClusterUniq

ClusterSlug property from Cumulus components becomes ClusterUniq. That is because it can be either slug or username. It depends on the setting in General settings tab in Backend Settings.

The only thing you have to change is ClusterSlug to ClusterUniq in all places you have been using it directly. Those places are:

  • layouts and pages in themes using Cumulus,
  • components that have been using clusterSlug param,
  • external methods that used components' clusterSlug param.

As a consequence method defineClusterSlug becomes defineClusterUniq.

cluster and clusterData variables injected to page by CumulusGuard have changed

cluster variable so far has been actually cluster's slug. This was misleading convention that had to be changed. Right now cluster is object of current cluster model, while clusterData variable is removed.

from v.1.x.x to v.2.0.0

It is big. I know. It is funny in technology that after you create something it does not make sense after you work with it for some time. This is what happened to modules and some conventions we used in versions 1.x.x. Sorry about the amount of changes, but we hope our plugin will be much better and usable after the upgrade.

Database changes

Make backup before proceeding.

In the beginning of Cumulus we did not know some October's and Laravel's conventions. While designing and developing Cumulus we used our own experience and ideas. During this time we get familiar with October's naming suggestions. As a consequence in version 2.0.0 we decided to change a few names.

Cluster full_name becomes name

Full_name from clusters table becomes name.

Primary keys in Cumulus

In version 1.x.x we were using cluster_id, module_id and plan_id as a primary keys. From now all of them will become id.

Drop modules

initbiz_cumuluscore_modules and initbiz_cumuluscore_plan_module tables will be dropped during upgrade to 2.0.0. Because of that the relation between your plans and modules will be lost. You should create a backup of initbiz_cumuluscore_plan_modules and initbiz_cumuluscore_modules if you want to review them after upgrade.

In most cases it should be easy to restore them as modules were whole plugins. Only plans and their relations with modules have to be restored in feature convention.

Modules becomes features

The biggest change in Cumulus v.2 concerns modules. We noticed, that it was not enough for plugin to register only one feature (since modules were actually features of system). This leaded us to plugin registration file, where now plugins can register as many features as they want to (more info in documentation).

Methods from ClusterRepository that concerns modules will right now use features. It applies to almost every "module" word in methods and attributes names. What is more modules used slugs while features use codes. So every time where we were talking about module slug, right now it is about feature code.

Modify modules

Before updating to v.2 you will have to ensure you register features as described in documentation for all of your modules.

What is more, you have to remove the initial migration previously created by create:module command:

  1. remove file named register_initbiz_cumulus_module.php
  2. remove line running it in version.yaml file (at the beginning)

ModuleGuard becomes FeatureGuard

The responsibility of ModuleGuard component was to ensure that plan has access to specified module and return 403 (Forbidden access) if it does not. The responsibility of FeatureGuard is the same but it checks if plan has access to any of features specified in component configuration.

Access to only one feature is enough to enter the page.

Command create:module removed

As a consequence the command create:module is removed. If you want to create something similar then create normal OctoberCMS plugin using create:plugin command and by adding registerCumulusFeatures method (details in documentation).

Settings model becomes AutoAssignSettings

If you have used Settings model somewhere in your code than you will have to change its name to AutoAssignSettings.

Because of that you will have to reconfigure autoassign in settings or update initbiz_cumuluscore_settings row code to initbiz_cumuluscore_autoassignsettings in system_settings table.

Menu and MenuItem components removed

From version 2.0.0 we decided to use RainLab.Pages to build menus. It is powerful, supported and extendable way to build menus.

Cumulus Plus users

If you are using Cumulus Plus extension make sure you change permissions from module name to feature code in "permissions".