Element extensions developer guide
Extensions are a way to add custom logic and extra complexity to already existing elements. Each element has the ability to load relevant extensions and as such, extensions should be custom tailored to one specific element.
Extensions can be defined in a course only, and are located in
[course directory]/elementExtensions. This folder is organized such that elements are contained as subfolders, and extensions are then contained inside the element folders. For example, if you had an extension
exampleExtension that extended
exampleElement, the folder structure would be:
Each extension needs only an
info.json file is structurally similar to the element info file and may contain the following fields:
The main Python controller script has no general structure and is instead defined by the element that is being extended. Any global functions and variables are available for use by the host element.
A host element can call the
load_extension() function to load one specific extension, or
load_all_extensions() to load everything that is available. These are defined in the freeform
A two-way flow of logic and information exists between elements and their extensions.
Importing an Extension From an Element
Loading extension Python scripts returns a named tuple of all globally defined functions and variables. Loading all extensions will return a dictionary mapping the extension name to its named tuple. For example, if an extension were to define the following in their controller:
def my_cool_function(): return "hello, world!"
The host element could then call this by running the following:
import prairielearn as pl def render(element_html, data): extension = pl.load_extension(data, "extension_name") contents = extension.my_cool_function() return contents
This small example above will render
"hello world!" to the question page. Note that when loading all extensions with
load_all_extensions(), modules are returned in ascending alphabetical order.
Importing a Host Element From an Extension
Extensions can also import files from their host element with the
load_host_script() function. This can be used to obtain helper functions, class definitions, constant variables, etc.
If the host element were to contain, for example:
import prairielearn as pl STATIC_VARIABLE = "hello" def render(element_html, data): extension = pl.load_extension(data, "extension_name") contents = extension.my_cool_function() return contents
The extension could then access
STATIC_VARIABLE by importing the host script:
import prairielearn as pl host_element = pl.load_host_script("pl-host-element.py") def my_cool_function(): return host_element.STATIC_VARIABLE
||The styles required by this extension, relative to
||The scripts required by this extension, relative to
||The styles required by this element relative to the extension's directory,
||The scripts required by this element relative to the extension's directory,
||The styles required by this extension relative to
||The scripts required by this extension relative to
Note that all client-side extension assets are always loaded, regardless of whether their Python controller was loaded or not.
Other Client Files
Other files available to the client may also be loaded, such as images or any downloadable content. These client files should be placed in
clientFilesExtension in the extension directory, and the full URL to that folder is given to the host extension in
data["options"]["client_files_extensions_url"][extension_name]. If this path is needed by the extension itself, it may be passed as an argument to a defined extension function.