Difference between revisions of "Module visibility and display"

Jump to: navigation, search
(Summary)
m
 
(47 intermediate revisions by 4 users not shown)
Line 1: Line 1:
(This document is a draft proposal.)
+
{{Moodle 2.0}}
 +
 
 +
The features described here are available since Moodle 2.0.2.
  
 
== Summary ==
 
== Summary ==
  
We would like to create a generic API that allows the following:
+
A new API allows you to customise how your module displays on the main course page:
  
* Module display on front page can be customised, for example making it possible to create another module that behaves like Label (displaying arbitrary html rather than a link to activity view.php) - currently this is hardcoded hack for Label. This change should apply to other areas such as navigation block as well as course page.
+
* You can display custom HTML below the link to your module.  
  
* Some modules can provide dynamic text, such as forum displaying unread messages. At present this is hardcoded so that only forum can do it.
+
* If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.
  
* Modules can be hidden completely, or greyed out, from the view of particular students according to either custom module behaviour, or (if not specified by module) default behaviour regarding a new capability moodle/course:viewactivity and the existing option for whether a non-available module is greyed out or hidden entirely.
+
* You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).
  
This should take advantage of the existing modinfo cache in order that performance is not adversely affected.
+
* You can display additional icons next to the other module editing icons when the user is editing the page.
  
== API improvement ==
+
In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.
  
Following Petr's suggestions, we would also like to improve the API by switching the modinfo in-memory data structures to use defined classes instead of anonymous stdClass objects. This achieves the following:
+
The <tt>get_fast_modinfo</tt> function now returns specific classes which are documented and which you can use to obtain new information about modules.
  
* Provides a location for documentation of these structures. (Currently they are undocumented.)
+
== Backward compatibility ==
  
* In future, allows functions to specify the required type (i.e. some functions require the information from modinfo, not just a row from the table; they will now be able to specify this). There is no plan to change function definitions immediately.
+
All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API unless you want to use the new features.
  
* Where the type is defined, makes metadata available to IDEs, allowing code completion and automatic documentation viewing.
+
== Removing your link ==
  
The following requirements should also be met:
+
If your module should not appear in navigation and in other lists of modules to visit or get information for, like Label, the easiest way to remove that link is to return true for FEATURE_NO_VIEW_LINK in your module's <tt>_supports</tt> function.
  
* 100% backward compatibility for existing use of these structures by core and third-party modules (except within minor parts of the label code, which will be altered as part of this change).
+
== Customising module display, in cache ==
  
* 100% backward compatibility for existing modinfo data (in database).
+
The first place you can customise your module display is in the existing <tt>_get_coursemodule_info</tt> API function. This function obtains information about the module which will be stored in the course cache (the <tt>modinfo</tt> field of the course table).
  
== Removing existing hacks ==
+
The course cache is only updated when somebody edits a module, so it can't be used for dynamic information - but it's okay if it takes a few database queries to calculate the data because it will be cached for future use.
  
* Existing hacks regarding label in all areas of code (e.g. navigation, etc) will be changed from the logic 'is this a label?' to the logic 'does this activity have a url?' (and label will be changed to work with this)
+
The function should return a value of class <tt>cached_cm_info</tt>. For example:
* Existing hacks regarding forum unread data will be removed and the forum unread code will be moved into the new API function. The code will be written in such a way as to have the same performance characteristics.
 
  
== get_fast_modinfo change ==
+
<code php>
 +
function mod_frog_get_coursemodule_info($cm) {
 +
    $info = new cached_cm_info();
 +
    $info->content = '<p>This will display below the module.</p>';
 +
    return $info;
 +
}
 +
</code>
  
The get_fast_modinfo function will be changed to:
+
You can change several properties which are documented in that class definition. If you don't change a property, its value remains default.
  
* support new values in _get_coursemodule_info
+
* <tt>name</tt> - name of activity (text of the link on course page).
 +
* <tt>icon</tt>, <tt>iconcomponent</tt> - name and component name of icon to display by the link.
 +
* <tt>content</tt> - extra HTML content to display below the module link on course page (not shown in navigation etc).
 +
* <tt>customdata</tt> - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course. Warning: Do not store complex objects here because when they are serialized (together with all other data) they may contain \0 byte and it causes fatal error on Postgres.
 +
* <tt>extraclasses</tt> - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.
 +
* <tt>onclick</tt> - already-escaped HTML that will be inserted as the value of the onclick attribute.
  
* extend dynamic per-user calculation (that checks is something is visible to current user, ->uservisible) with additional checks
+
If you don't need the information to be cached (it can be retrieved very quickly without making any database queries) then you might consider using one of the functions below instead, in order to avoid unnecessarily increasing the size of the course cache. Although the headings mention the current user, you can of course use those functions in a way that doesn't depend on the current user.
  
* call the new module API (if provided) after calculating modinfo, to get dynamic information
+
Don't use renderers in this function (see MDL-41074). If you have data you would like to render onto the course page, put it into the custom data property and render it in the MODNAME_cm_info_view() function (see below). For an example, see mod_folder (version > 2013012100).
  
== Modify _get_coursemodule_info ==
+
== Customising module display, for current user ==
  
There will be a change to the existing module API function _get_coursemodule_info. This change will retain backward compatibility. I wrote documentation for the new function below; in that documentation, the # marker indicates a changed or new entry, other stuff is existing.
+
You can customise module display dynamically (when the page loads). For example you might want to alter it based on the permissions of the current user.
  
In get_fast_modinfo, the system can automatically turn the ->nolink option into a ->url which will either be a moodle_url or null (this avoids duplicate code working out the url mod/whatever/view.php?id=whatever in a zillion places). Possibly we could also allow ->url to be specified directly but this won't be supported everywhere, at least to start with.
+
<code php>
 +
function mod_frog_cm_info_dynamic(cm_info $cm) {
 +
    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
 +
    if (!has_capability('some/capability', $context)) {
 +
        $cm->set_user_visible(false);
 +
    }
 +
}
 +
</code>  
  
PERFORMANCE CONCERNS: None. No changes would be made to existing modules except label, and that one doesn't add a database query. Anyway, this data is cached.
+
This code can affect the navigation, and whether users are permitted to access the module (as above). It runs on all pages within the course, so it's very important that you do not put slow code in this function: it should not make any database queries.  
  
=== _get_coursemodule_info ===
+
In addition to the <tt>set_user_visible</tt> function shown, you can also set many other things such as additional editing icons which will appear if editing mode is enabled. See the cm_info class documentation for more information.
  
This optional function returns additional information about an instance of your module, which can be accessed quickly when displaying the module.
+
Most things are set using functions (as above; another example would be <tt>set_content</tt> which sets the same content data as mentioned in the previous section) while other things can be set directly using public variables.
  
You should return an object $info which may contain the following fields, all of which are optional:
+
== Customising module display, for current user, on course page only ==
  
* ->nolink #: set true if the module does not need a link.
+
Sometimes you need to display custom information for the current user that appears only on the course view page. For example, the forum module displays unread information on the view page. This information doesn't show on other pages (it isn't included in the navigation, for instance).
* ->name: name of instance (text displayed in link on course page) - this will not be used if nolink is true.
 
* ->content #: HTML content to be displayed on the course page where this module is placed; appears below the link, if present (this is how Label displays content on course page)
 
* ->extraclasses #: Additional CSS classes to be added to the A or DIV tag(s) for this item on main course page.
 
* ->icon: specifies an icon to use for this instance instead of the normal module icon; you can either use the name of an icon which will be directly passed to the $OUTPUT->pix_icon function, or the special format mod/mymodule/iconname which will be passed to that function as two parameters iconname, mymodule.
 
* ->customdata #: A place to store a string or object containing custom data for this module, which needs to be available course-wide via the get_fast_modinfo function. If present, this data should be small in size.
 
* ->afterlink #: If specified, includes HTML code which displays after the link. This is normally not set by this function, but rather in _get_dynamic_coursemodule_info to display things like forum unread status.
 
* ->editicons #: If specified, includes HTML code which displays as part of the editing icons (hide, edit, delete, etc). This is normally not set by this function, but rather in _get_dynamic_coursemodule_info in case the module has a specific feature that is needed on home page.  
 
  
* ->extra (deprecated in 2.1): puts content in a weird part of the A or DIV tag for the item; can be used to add attributes
+
<code php>
* ->iconcomponent (deprecated in 2.1): doesn't appear to do anything?!
+
function mod_frog_cm_info_view(cm_info $cm) {
 +
    $cm->set_after_link('Last tadpole: 22:17');
 +
}
 +
</code>
  
== Extend dynamic calculation ==
+
Because this function only runs when looking at the course page:
  
Currently the modinfo code makes the following checks that apply dynamically per-request (and do not directly come from the cache) in order to create the ->uservisible member variable.
+
* It is OK to do tasks which may require some database queries (such as checking for unread forum messages), although obviously this should be kept to a minimum. In particular, care should be taken so that if there are 20 instances of the activity on the course page, it doesn't make 20 separate queries to obtain the information.
  
* If ->groupmembersonly is set, checks if the user belongs to group or has accessallgroups.
+
* Inside this function you cannot set options which affect the appearance or access to the activity on other pages; for example, you cannot turn off the uservisible flag as shown in the previous example. This is because these options are required on other pages (e.g. to display navigation) so it does not make sense to set them only for the course page. If you try, you'll get a <tt>coding_exception</tt>.
* If availability restrictions (date, grade, completion) are set, checks these.
 
  
My proposal is:
+
== get_fast_modinfo data ==
  
* Make this part of the code (that 'specialises' a single mod value for the current user/request) into a separate function, just to simplify it.  
+
The function <tt>get_fast_modinfo</tt> now returns an object of class course_modinfo, which itself contains cm_info objects about each activity. (These are entirely backward-compatible with the previous return value.)
* Add a check for the moodle/course:viewactivity capability; if user doesn't have this capability, set ->uservisible to false. Also check the option about what to do with hidden activities; if this is set to the default 'grey it out', then set ->inactive to true.
 
** Note: The default value for moodle/course:viewactivity should be true for all roles, even guest. This maintains existing behaviour. Sites that don't want guests to view activities can change the main role definition for guest.
 
* Change the 'restriction information' code so that it adds restriction information (this is things like 'Not available until 30 December 2010') into a ->restrictions member of modinfo
 
* Call the _get_dynamic_coursemodule_info function (below) if the current module supplies one.
 
  
 +
In addition to the old methods for obtaining data from $modinfo, there are some new functions. For example, here is how to get a single cm_info from $modinfo:
  
PERFORMANCE CONCERNS: Minimal. No new database queries are required.
+
<code php>
 +
$modinfo = get_fast_modinfo($course);
 +
$cm = $modinfo->get_cm($cmid);
 +
</code>
  
== New module API ==
+
The cm_info objects contain additional information that is not present in the course_modules database row, such as the module's name, and the icon and associated content mentioned above. In order to distinguish these from the plain database objects, you can specify the cm_info class in a function definition:
  
A new module API function _get_dynamic_coursemodule_info will be defined. As parameters, this takes:
+
<code php>
 +
function my_clever_function(cm_info $cm) {
 +
    if (!$cm->uservisible) {
 +
        // the module is not visible or available to current user,
 +
        // so do something clever instead
 +
    }
 +
}
 +
</code>
  
* the contents of modinfo for this module (which will include data from get_coursemodule_info).
+
By specifying cm_info in the parameter list, you'll cause PHP to give an error if anyone tries to call that function with a $cm object that just came from the database row, instead of from <tt>get_fast_modinfo</tt>. (It is good practice to always get $cm from get_fast_modinfo, but there might be exceptions.)  
* an optional userid (default 0 = current user).
 
  
When it returns, this returns a new version of modinfo that has been customised for the current user. This could include:
+
Of course, this is only necessary if your function relies on a feature that is specific to cm_info, such as the 'uservisible' field above. If your function only uses fields which are present in the database row, then there's no need to require cm_info.
  
* setting ->uservisible=false (to hide the activity entirely)
+
== More documentation ==
* setting ->inactive to true (to grey it out) and maybe also adding text to the end of ->restrictions (to optionally display information about why the user can't access it, in case they wonder why it is greyed out)
 
* setting ->afterlink = '16 unread' (to display dynamic data)
 
* setting ->editicons (to add a custom editing icon for this module)
 
  
PERFORMANCE CONCERNS: None. Of standard modules, only the forum will implement this and it will use the same code as at present. Custom modules will need to be written carefully in a similar manner so that they also perform well (ie do any queries once for whole course and store in global cache, not once per module).
+
All three classes for this API are in the core file <tt>lib/modinfolib.php</tt> and contain complete PHPdoc information for all fields and functions.
  
NOTE: Maybe this function also needs access to the rest of $modinfo? But it hasn't all been completely filled in because this function obviously hasn't run... still we could provide it if required...
+
* [http://phpdocs.moodle.org/HEAD/core/lib/course_modinfo.html course_modinfo PHPdocs]
 +
* [http://phpdocs.moodle.org/HEAD/core/lib/cm_info.html cminfo PHPdocs]
 +
* [http://phpdocs.moodle.org/HEAD/core/lib/cached_cm_info.html cached_cm_info PHPdocs]

Latest revision as of 08:59, 9 August 2013

Moodle 2.0


The features described here are available since Moodle 2.0.2.

Summary

A new API allows you to customise how your module displays on the main course page:

  • You can display custom HTML below the link to your module.
  • If your module does not have a link (like Label, where it is only for display on the main page) then you can remove the link from the main page and from all navigation etc.
  • You can display HTML next to the link to your module that indicates dynamic information (like Forum, where it displays information about unread messages).
  • You can display additional icons next to the other module editing icons when the user is editing the page.

In addition, existing things you could already do (like change the icon on the main page) are still available when using the new API.

The get_fast_modinfo function now returns specific classes which are documented and which you can use to obtain new information about modules.

Backward compatibility

All modules and code written for Moodle 2.0 should continue to behave in exactly the same manner. There is no need to change existing modules for this API unless you want to use the new features.

Removing your link

If your module should not appear in navigation and in other lists of modules to visit or get information for, like Label, the easiest way to remove that link is to return true for FEATURE_NO_VIEW_LINK in your module's _supports function.

Customising module display, in cache

The first place you can customise your module display is in the existing _get_coursemodule_info API function. This function obtains information about the module which will be stored in the course cache (the modinfo field of the course table).

The course cache is only updated when somebody edits a module, so it can't be used for dynamic information - but it's okay if it takes a few database queries to calculate the data because it will be cached for future use.

The function should return a value of class cached_cm_info. For example:

function mod_frog_get_coursemodule_info($cm) {
    $info = new cached_cm_info();
    $info->content = '<p>This will display below the module.</p>';
    return $info;
}

You can change several properties which are documented in that class definition. If you don't change a property, its value remains default.

  • name - name of activity (text of the link on course page).
  • icon, iconcomponent - name and component name of icon to display by the link.
  • content - extra HTML content to display below the module link on course page (not shown in navigation etc).
  • customdata - arbitrary extra PHP data to store in modinfo cache; useful if, for performance reasons, your module needs to store data that should be accessible very quickly from other parts of the course. Warning: Do not store complex objects here because when they are serialized (together with all other data) they may contain \0 byte and it causes fatal error on Postgres.
  • extraclasses - extra CSS class or classes that will be added to the activity on the main page, so that you can alter the styling.
  • onclick - already-escaped HTML that will be inserted as the value of the onclick attribute.

If you don't need the information to be cached (it can be retrieved very quickly without making any database queries) then you might consider using one of the functions below instead, in order to avoid unnecessarily increasing the size of the course cache. Although the headings mention the current user, you can of course use those functions in a way that doesn't depend on the current user.

Don't use renderers in this function (see MDL-41074). If you have data you would like to render onto the course page, put it into the custom data property and render it in the MODNAME_cm_info_view() function (see below). For an example, see mod_folder (version > 2013012100).

Customising module display, for current user

You can customise module display dynamically (when the page loads). For example you might want to alter it based on the permissions of the current user.

function mod_frog_cm_info_dynamic(cm_info $cm) {
    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
    if (!has_capability('some/capability', $context)) {
        $cm->set_user_visible(false);
    }
}

This code can affect the navigation, and whether users are permitted to access the module (as above). It runs on all pages within the course, so it's very important that you do not put slow code in this function: it should not make any database queries.

In addition to the set_user_visible function shown, you can also set many other things such as additional editing icons which will appear if editing mode is enabled. See the cm_info class documentation for more information.

Most things are set using functions (as above; another example would be set_content which sets the same content data as mentioned in the previous section) while other things can be set directly using public variables.

Customising module display, for current user, on course page only

Sometimes you need to display custom information for the current user that appears only on the course view page. For example, the forum module displays unread information on the view page. This information doesn't show on other pages (it isn't included in the navigation, for instance).

function mod_frog_cm_info_view(cm_info $cm) {
    $cm->set_after_link('Last tadpole: 22:17');
}

Because this function only runs when looking at the course page:

  • It is OK to do tasks which may require some database queries (such as checking for unread forum messages), although obviously this should be kept to a minimum. In particular, care should be taken so that if there are 20 instances of the activity on the course page, it doesn't make 20 separate queries to obtain the information.
  • Inside this function you cannot set options which affect the appearance or access to the activity on other pages; for example, you cannot turn off the uservisible flag as shown in the previous example. This is because these options are required on other pages (e.g. to display navigation) so it does not make sense to set them only for the course page. If you try, you'll get a coding_exception.

get_fast_modinfo data

The function get_fast_modinfo now returns an object of class course_modinfo, which itself contains cm_info objects about each activity. (These are entirely backward-compatible with the previous return value.)

In addition to the old methods for obtaining data from $modinfo, there are some new functions. For example, here is how to get a single cm_info from $modinfo:

$modinfo = get_fast_modinfo($course);
$cm = $modinfo->get_cm($cmid);

The cm_info objects contain additional information that is not present in the course_modules database row, such as the module's name, and the icon and associated content mentioned above. In order to distinguish these from the plain database objects, you can specify the cm_info class in a function definition:

function my_clever_function(cm_info $cm) {
    if (!$cm->uservisible) {
        // the module is not visible or available to current user,
        // so do something clever instead
    }
}

By specifying cm_info in the parameter list, you'll cause PHP to give an error if anyone tries to call that function with a $cm object that just came from the database row, instead of from get_fast_modinfo. (It is good practice to always get $cm from get_fast_modinfo, but there might be exceptions.)

Of course, this is only necessary if your function relies on a feature that is specific to cm_info, such as the 'uservisible' field above. If your function only uses fields which are present in the database row, then there's no need to require cm_info.

More documentation

All three classes for this API are in the core file lib/modinfolib.php and contain complete PHPdoc information for all fields and functions.