Internally, the 'Catalogue' is referred to as the 'catalog' or 'totara_catalog'.
How to extend the Grid catalog
The grid catalog has been designed in a way which allows Totara devs and third parties to extend it easily. There are several levels of enhancement and customisation available.
...
Each of the features above are described in detail below.
Providers
The core of the grid catalog is built around providers. Each provider defines a type of learning item that can appear in the catalog. Providers define collections of dataholders, filters and features (and, frequently, dataformatters and observers will also be associated with a particular provider).
...
All items which might appear in the catalog for some user should be returned by get_all_objects_sql. Visibility restrictions are then handled by can_see. Any learning item which should never be displayed in the catalog should be excluded in get_all_objects_sql. For example, all courses except the site course are included by the course provider.
Dataholders
A provider will have a collection of dataholders, which define how the data stored in the database is retrieved and displayed. A provider implements a number of dataholder_factorys, which return one or more dataholders. A dataholder is defined as:
...
Code reuse can be achieved by having dataholder factories call code in other areas. For example, courses, programs and certs all have custom field dataholders, which they create by calling customfield_dataholder_factory::get_dataholders.
Dataformatters
A dataformatter defines how a given set of data should be manipulated in order to prepare it for use. For example, the user_date formatter takes a database date in integer format and converts it to a user-readable string. Each dataformatter can have a bespoke constructor, which should define the list of required data, and can record any other information which might be needed during formatting. For example, the ordered_list constructor takes a database field which indicates what database field contains the list, a source delimiter which indicates what delimiter was used within the data, and a result delimiter which defines how the resulting ordered list should be formatted.
...
A set of generic dataformatters are defined by the catalog, and include classes such as text, textarea, static_text (which use no database data, it just displays the text which was supplied in the constructor) and fts (which makes text suitable for inclusion in the FTS index). Each provider can define a collection of dataformatters which are specific to their data. For example, courses have a 'format' dataformatter, which calls get_string using the 'format' text key from the course table.
Filters
Filters are used by users to restrict the items displayed in the catalog. For example, a user can select 'Courses' in the 'Learning type' filter to see only courses. There are some core catalog filters, but most filters are defined by, and specific to, providers.
...
Like dataholders, code reuse can be achieved by calling another function which returns a set of filters configured for the specific provider.
Features
Features are used to define a subset of learning items which should be marked with the 'Featured' label, and for sorting. Like filters, there are some core catalog features, but most features are defined by, and specific to, providers.
...
Merging happens in basically the same way as for filters. When two features with matching keys are merged, options from both features are merged. It is important to consider what keys and values are used when merging options for features - are they compatible?
Observers
Catalog observers are used to make sure that the catalog index is up-to-date. Standard observers can also be used to schedule either provider_active_task (refreshes the specified provider's data) or refresh_catalog_adhoc (refreshes all providers' data). These adhoc tasks can also be scheduled within catalog observers. Catalog observers are defined by specifying the event that they observer, adding an entry in db/events.php, and calling register_for_update or register_for_delete from init_change_objects. The 'register' functions can be called multiple times from within one observer (if more than one catalog item is affected by the event), or not at all (if the event related to an item that doesn't need adding, updating or removing from the index).
Observers should be defined when there are new events which can result in learning items being added or removed from the catalog index, or when a data change event can result in information in the FTS index needing to be updated. For example, if a new dataholder is defined, and it provides data which can be included in the FTS index, and the provider dataholder configuration is updated to include the new data, then observers (and, where they are missing, events) should be created which will trigger update of the catalog. The type and nature of the observer will depend on the situation.
Merge selectors
Subclasses of merge_select are used to manage totara_core\output\select form elements (although it should be possible to use them to manage any form element which can return a core\output\template). The totara select form elements are simple classes that are used to produce template objects (which link to a template file and contain the data needed to fill in the template).
...
If you want to create a new type of form element for users to interact with, you would first create a template class (maybe based on the totara select class). Then define a merge_select subclass, which returns an instance of the template, and define how the objects should behave if selectors are merged. In some circumstances, it might not make sense to allow selectors to be merged at all, in which case the can_merge function would just return false and make sure that all uses of the new class have unique keys.
Datasearch filter classes
These classes are used to specify how sql filter snippets can be built. Each datasearch filter object contains information about the joins, parameters and conditions that need to be put together to add the particular filter to the catalog search query.
...
When the filter is used, it constructs a subquery using the data provided when it was constructed, and all sources that it contains. The subquery is slightly different depending on whether there is one source or more. More information about how this happens is available in the other technical documentation. The end result is that calling make_sql will return a join, some where conditions and some parameters. The catalog pieces together these string from each of the active filters, and adds the base table information, in order to retrieve the appropriate results.
How the catalog works
The purpose of this section is to help developers who have to debug the catalog. It might also be useful as background for developers who are trying to implement new providers or extend existing ones.
Provider handler
This class manages and instantiates the providers. Only active providers should be instantiated, and the handler ensures this is the case.
...
Function get_formatted_value_from_dataholder is used to transform the raw data obtained in the main sql query into the final formatted data, by calling the get_formatted_data function of the appropriate dataholder. Be aware that these formatter functions usually contain only simple php code, but they could also contain complex logic, function calls, or even database interactions.
Provider base class
The base class contains a number of functions which are used to:
- Change the status of a provider, active or inactive.
- Load filters, features and dataholders relating to the provider.
Filter handler
This class manages various lists of filters, including (but not limited to):
...
The filter handler also does the merging of catalog filters as they are loaded.
Feature handler
This class manages the list of features. Like the filter handler, it merges features as they are loaded.
Adding data to the index
The class catalog_storage manages data going into (and being deleted from) the catalog. This class is called from various observers and adhoc and scheduled tasks.
...
- objecttype: A string matching a provider's get_object_type, e.g. 'course'.
- objectid: An integer referencing some object belonging to the related provider, e.g. a course id.
- contextid: An integer, 0 where there is no applicable contextid for the object.
Getting data out of the index
The catalog_retrieval class is used to get records, by calling get_page_of_objects. It constructs an sql query based on the active filters, which must be activated by calling the filter_handler (see below). The query is executed (perhaps several times) and the function iterates over the resulting records, checking whether the resulting learning items are visible, and continues until a full page of objects has been retrieved, or the end of the search results is reached. The result is a list of objects containing catalog id, objecttype, objectid and contextid.
Displaying data in the catalog
The totara_catalog\output\catalog class is used to display the main catalog page. It is a template class, so an instance contains a link to a template file and the data needed to fill in the template. This template displays the whole catalog page, including:
...
- When the page is first loaded, through index.php, the param_processor takes filters and other parameters specified in the url and passes them to the catalog::create function. The resulting catalog object is rendered in index.php.
- When a user clicks on an interactive element within the catalog (e.g. filter, sorting, 'Load more'), a call is made to get_catalog_template_data in external.php. This function passes all the provided params to catalog::create, and then returns only the data the catalog object contains.
Catalog template class
The 'create' function takes a bunch of parameters and returns a new instance of the catalog template class. The parameters include details about how the grid items should be displayed, which page of records should be shown, and what filters should be applied to the grid results.
...
- The appropriate filters are loaded by the filter_handler and set_current_data is called with the provided filter params.
- A catalog_retrieval object is created and used to retrieve the appropriate page of objects.
- An item template object is created for each object, containing all the formatted data needed to display the item.
- A grid template object is created containing all of the item template objects.
Details template class
This class is used to retrieve the data needed to display the details popup for a learning item. It is created in get_details_template_data in external.php, and the template data is returned. The create function works in a similar way to the catalog template class, when the item template objects are created.
Config class
This class is used to manage catalog and provider settings. Settings are stored using the standard set_config function, so end up in the config_plugins table.