Code structure

This document provides an outline of code structure within Totara.
It focuses on plugins structure as that is without a doubt the most prevalent area of work for most reading this document, however the same structure applies to components.
Core on the other hand has a more bespoke structure.


Item directories and frankenstyle naming

The earlier document on High level architecture and organisation introduced you to the concepts we use to organise our product.

Every component, plugintype, plugin, subplugin type and subplugin has its own directory within Totara.
The location of that directory will depend upon what it is.
If it is a component or plugintype then it can be anywhere within the directory structure, except within other plugin type and subplugin type directories.
If it is a subplugintype then it will be within the owning plugin.
If it is a plugin or subplugin then it will be within the owning type's directory.

Each of these items has to have a unique name. This is both enforced in code, and in the file system as you cannot have two directories with the same name!

Within the product we need a way in which we can identify and talk about an individual item, whether it is a component, a plugin, or a subplugin.
To do this we use what we call frankenstyle names; they are a concatenation of the the type, and the unique name of an item.

The following provides examples for each to help illustrate how this work.

Core

Core is core, is core.
There is only one, and we refer to it as core.

Frankenstyle name
core

Component

All components are said to belong to core.
The following is a table illustrating some of the more widely know components.

ComponentFrankenstyle nameDirectory
Coursecore_course./course
Completioncore_completion./completion
Gradingcore_grading./grade/grading

Plugin types

Plugin types are talked about as top level items, as they are a single parent with many children.
The following table illustrates some of the more often talked about plugin types.

Plugin typeFrankenstyle nameDirectory
Activitiesmod./mod
Authentication pluginsauth./auth
Course formatsformat./course/format

Plugins

Plugins must be of a type, and their directory must exist directly within the plugin types directory.
The following table has examples for the three plugin types noted above.

TypePluginFrankenstyle nameDirectory
ActivityAssignmentmod_assign./mod/assign

Seminarmod_facetoface./mod/facetoface

SCORMmod_scorm./mod/scorm
Authentication plugginManualauth_manual./auth/manual

LDAPauth_ldap./auth/ldap

OAuthauth_oauth2./auth/oauth2
Course formatWeeksformat_weeks./course/format/weeks

Topicsformat_topics./course/format/topics

Single activityformat_singleactivity./course/format/singleactivity

You will notice in the above examples that we sometimes have names that are different from the frankenstyle name and directory.
This is important to illustrate, and more often than not arrives from a business decision to change our terminology and how we refer to a item.
In the case of Seminar/Factoface the decision was made to rename it from FaceToFace to Seminar as they more accurately described the functionality in our target markets.
Renaming directories is not something we do, simply put the frankenstyle name that is given to something is used through the system and changing it is not a simple task.
In fact in light of the fact we are open source, maintain a deprecation policy and facilitate extensibility and customisability so extensively it is nigh impossible to rename a directory.
Doing so would have a potentially disaster butterfly effect on our partners and subscribers.

Sub plugin types

These are just like plugin types, except that their directory must exist within a plugins directory structure.
A quick example:

Owning pluginSubplugin typeFrankenstyle nameDirectory
AssignmentFeedbackassignfeedback./mod/assign/feedback

Submissionassignsubmission./mod/assign/submission
Atto editorPluginatto./lib/editor/atto/plugins

Subplugins are used sparingly within our product. In our core distribution at the time of writing this there are only 15 subplugin types.

Sub plugins

Just like plugins, except these must exist within the subplugin type directory.

Subplugin typeSubpluginFrankenstyle nameDirectory
Atto pluginBoldatto_bold./lib/editor/atto/plugins/bold

Italicatto_italic./lib/editor/atto/plugins/italic

Underlineatto_underline./lib/editor/atto/plugins/underline

Files

The following are the key files you will commonly encounter directly within an items directory.

version.php

This is required for all plugins and subplugins.
It contains the following information:

  • Version of the plugin
    Increased when upgrade is required, such as for database schema changes, required data manipulation, changes to language files etc.
  • The required version of the product
    Remember there are a lot of third party plugins out there. This helps ensure when installing a plugin that it is compatible with the version of Totara that you are running.
  • The component
    This is the plugins frankenstyle name and is used to verify the location and name of the plugin during installation and upgrade.
    Makes sure you have things in the right place.

There are a few other things that it can specify, as documented in the developer document.

index.php

Nearly all components, plugins and subplugins will have an index.php.
Plugins and subplugins will share an approach to this, however each is free to implement their own functionality.
Its existence benefits security by helping to ensure that incorrectly configured web servers that list directories will have a file to serve by default when a directory is requested.

settings.php

Contains administrative settings for the plugin. These are included within the main administration structure of the site and bundled into the configuration state by the configuration API.

lib.php

Contains functions belonging to this component or plugin, but that are used by core, or other components in order to interact with this item in a managed way.
These files used to collect a very large number of functions.
Because the file is intended to serve core and other components it is frequently included from outside of its own scope and context.

This is a legacy approach, our preference now is autoloaded classes that serve specific purposes.
You will find more information on our preferred approach below.

locallib.php

Contains functions for interacting with this item that are designed to be used internally by the item only.
This file will only ever be included by scripts belonging to this item, and should never be included outside of the items scope and context.

This is, like lib.php, a legacy approach, and we now prefer autoloaded classes to serve a specific purpose.

externallib.php

Contain web service definitions and handlers.

Our preferred approach is now to use GraphQL which requires autoloaded, namespaced classes instead of a predetermined root directory file.

renderer.php

Contains an output renderer belonging to this item and that should be used by all entry pages to produce output.
They form the link between the items logic and template system.

Importantly they are factory driven and designed so that they can be overridden by the theme, providing theme developers a means of rewriting and extending the user experience of an item.

styles.css

Any CSS the plugin or component may want to introduce to a theme.
You may find less, or sass directories containing source used to compile this styles.css file.

It is also possible to have theme specific style files, named styles_{theme_frankenstyle_name}.css.
These are rarely encountered in core as we have control over our themes but are often encountered in third party plugins.

upgrade.txt

This is a running record of all deprecations, API changes, dev notes occurring within the items scope.
It is a running record maintained by developers at the time that changes are made.
It serves as an excellent reference for anyone maintaining customisations, or working with API's published by the item.

*_form.php

This is an old standard, before class autoloading was available to the platform.
They are form definitions, with * typically matching an entry point in the item.

Our preferred approach is to autoload these from a recognised namespace.


Directories

The following is a list of commonly encountered directories and the purpose that they serve.

classes

The most important of all. The classes directory contains autoloadable classes.
The file name and name of a class within this directory is vitally important in order to work with autoloading.
Namespacing is also commonly used, with first level names acting as reserved name spaces for API integrations.
Each namespace block is matched by a directory within the classes directory.

For more information on class autoloading within Totara please refer to our developer documentation.

amd

Contains AMD JavaScript modules.

These will be organised into two sub directories:

  • src - where we make our changes.
  • build - created and updated by running grunt.

backup

Courses, and all item within the course context can contain backup and restore code that is used to export data relating to a specific context (that which is being backed up) and then import code to re-inject it into the product during a restoration.
This is used by the backup API within the product.

db

Contains key files used to install, upgrade, and define the plugin.
The most notable of which are as follows:

  • install.xml
    The database schema for this item. It should be created by the in product XMLDB editor.
  • install.php
    Code to execute during installation.
  • upgrade.php
    Upgrade steps for this item. Each step maps references a version found in the version.php mentioned above.
  • upgradelib.php
    Extracts upgrade operations into functions in order to facilitate testing and reuse when required.
  • access.php
    Defines the capabilities this item wishes to introduce.
    Used by the core_role component.
  • caches.php
    Defines any caches this item wishes to use.
    Used by the core_cache component.
  • events.php
    Defines event observers that this item has, enabling it to react to those events when they are fired.
  • hooks.php
    Defines hook observers that this item has, enabling it to react to those hooks when they are triggered.
  • renamedclasses.php
    Keeps a list of class names that have been renamed, automatically creating aliases for them and printing a debugging notice is someone tries to use one.
    Required as part of our Deprecation guidelines
  • services.php
    Defines web services published by this item.
  • subplugins.php
    Defines any sub plugins this item introduces.
  • tag.php
    Defines tag collections and areas introduced by this item.

lang

Contains language translations for this item.

All strings within Totara that are presented to the user must come through the translation system.
The definition of those strings exist within this directory.

Within the main distribution you will find only an "en" directory.
In core we only provide english [british english] translation.
Other translations are available for download and installation from our translation system, see our documentation on Internationalisation for more information.

pix

Contains any images the item wants to introduce. These resources are then accessible through our mediation layer and can be overridden by themes.

rb_sources

Contains report builder sources and embedded report definitions.
See our documentation on Report builder for more information.

templates

Contains mustache templates.
These can be overridden by themes.
See our documentation on Mustache templates and 2021-03-09_10-42-44_Output for more information.

tests

Contains automated unit and acceptance tests.

Unit tests must exist directly within this directory, in a file named *_test.php and contains a testcase named frankenstylename_*_testcase that extends advanced_testcase or basic_testcase.
See our documentation on Unit testing for more information.

We use Behat for acceptance testing.
Feature files exist within a "behat" subdirectory.
See our documentation on Acceptance testing for more information.

yui

Contains YUI JavaScript modules.
These are a legacy item, with our preferred approach being either Vue modules, or AMD modules.

See our documentation on JavaScript & AMD modules for more information.

webapi

Contains GraphQL schema files for the services this item chooses to publish.