Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

NEWMODULE Adding capabilities: Difference between revisions

From MoodleDocs
No edit summary
m (Text replacement - "<code php>" to "<syntaxhighlight lang="php">")
 
(2 intermediate revisions by 2 users not shown)
Line 7: Line 7:
This should contain a list of the capabilities that you want to define. For example:
This should contain a list of the capabilities that you want to define. For example:


<code php>
<syntaxhighlight lang="php">
$capabilities = array(
$capabilities = array(
     'mod/<<NEWMODULE>>:<<CAPABILITYNAME>>' => array(
     'mod/<<NEWMODULE>>:<<CAPABILITYNAME>>' => array(
Line 23: Line 23:
     // Add more capabilities here ...
     // Add more capabilities here ...
)
)
</code>
</syntaxhighlight>


The various parts of the capability definition are:
The various parts of the capability definition are:
Line 56: Line 56:


If you don't want to specify any roles that will be given your capability by default, you can pass a blank array to the 'archetypes' parameter:
If you don't want to specify any roles that will be given your capability by default, you can pass a blank array to the 'archetypes' parameter:
<code php>
<syntaxhighlight lang="php">
'mod/newmodule:dosomething' => array(
'mod/newmodule:dosomething' => array(
     'captype' => 'read',
     'captype' => 'read',
Line 62: Line 62:
     'archetypes' => array()
     'archetypes' => array()
),
),
</code>
</syntaxhighlight>


==2. Get Moodle to load the updated capabilities==
==2. Get Moodle to load the updated capabilities==
Line 76: Line 76:


1. First we need to get the $cm id, and verify that it is correct (there are lots of different ways you might do this, this is only an example.
1. First we need to get the $cm id, and verify that it is correct (there are lots of different ways you might do this, this is only an example.
<code php>
<syntaxhighlight lang="php">
$cmid = required_param('cmid', PARAM_INT);
$cmid = required_param('cmid', PARAM_INT);
if (!$cm = get_coursemodule_from_id('<<NEWMODULE>>', $cmid)) {
if (!$cm = get_coursemodule_from_id('<<NEWMODULE>>', $cmid)) {
     error("Course module ID was incorrect");
     error("Course module ID was incorrect");
}
}
</code>
</syntaxhighlight>


2. Then you get the module context:
2. Then you get the module context:
<code php>
<syntaxhighlight lang="php">
$context = context_module::instance($cm->id);
$context = context_module::instance($cm->id);
</code>
//for course use below code
//$context = context_course::instance($cm->id);
//for system
//$context = context_system::instance($cm->id);
</syntaxhighlight>


3. Finally, you can actually check the permission
3. Finally, you can actually check the permission
<code php>
<syntaxhighlight lang="php">
if (has_capability('mod/<<NEWMODULE>>:<<CAPABILITYNAME>>', $context)) {
if (has_capability('mod/<<NEWMODULE>>:<<CAPABILITYNAME>>', $context)) {
</code>
</syntaxhighlight>


Normally, you do 1. and 2. once at the top of a script, and then call has_capability as needed within the script with the appropriate capabilities.
Normally, you do 1. and 2. once at the top of a script, and then call has_capability as needed within the script with the appropriate capabilities.
Line 100: Line 104:


Suppose you have a page that should only be available to users with a particular capability. For example, only users with mod/quiz:viewreports should be able to access mod/quiz/report.php. In cases like this, you can use the require_capability function:
Suppose you have a page that should only be available to users with a particular capability. For example, only users with mod/quiz:viewreports should be able to access mod/quiz/report.php. In cases like this, you can use the require_capability function:
<code php>
<syntaxhighlight lang="php">
require_capability($capability, $context);
require_capability($capability, $context);
</code>
</syntaxhighlight>
near the top of your script. (As soon as you have got the context and called require_login is a good time.) All this does internally is
near the top of your script. (As soon as you have got the context and called require_login is a good time.) All this does internally is
<code php>
<syntaxhighlight lang="php">
if (!has_capability($capability, $context)) {
if (!has_capability($capability, $context)) {
     // Display error and exit.
     // Display error and exit.
}
}
</code>
</syntaxhighlight>
but using require_capability makes your code simpler and is recommended. (Of course, anywhere you might print a link to a page like this, you should only print the link if the user has the right capability.)
but using require_capability makes your code simpler and is recommended. (Of course, anywhere you might print a link to a page like this, you should only print the link if the user has the right capability.)


Line 118: Line 122:


There is an optional 3rd parameter to has_capability that you can use to check another user's permissions:
There is an optional 3rd parameter to has_capability that you can use to check another user's permissions:
<code php>
<syntaxhighlight lang="php">
has_capability($capability, $context, $otheruser->id);
has_capability($capability, $context, $otheruser->id);
</code>
</syntaxhighlight>


====Excluding administrators====
====Excluding administrators====


Administrators have a magic 'moodle/site:doanything' capability that gives them every other capability. If you wish to disable that magic override for one particular capability check, you can use the optional 4th parameter to has capability:
Administrators have a magic 'moodle/site:doanything' capability that gives them every other capability. If you wish to disable that magic override for one particular capability check, you can use the optional 4th parameter to has capability:
<code php>
<syntaxhighlight lang="php">
has_capability($capability, $context, NULL, false);
has_capability($capability, $context, NULL, false);
</code>
</syntaxhighlight>
However, you normally should not do this.
However, you normally should not do this.


Line 133: Line 137:


The has_capability function has been carefully optimised, and is pretty fast and you should not really worry. However, it has to perform a fairly complex computation, and if you are going to make exactly the same has_capability call several times in a page (perhaps in a loop) it is probably worth moving the permission check outside the loop. For example don't do:
The has_capability function has been carefully optimised, and is pretty fast and you should not really worry. However, it has to perform a fairly complex computation, and if you are going to make exactly the same has_capability call several times in a page (perhaps in a loop) it is probably worth moving the permission check outside the loop. For example don't do:
<code php>
<syntaxhighlight lang="php">
foreach ($attempts as $attempt) {
foreach ($attempts as $attempt) {
     if (has_capability('mod/quiz:viewreports', $context)) {
     if (has_capability('mod/quiz:viewreports', $context)) {
Line 139: Line 143:
     }
     }
}
}
</code>
</syntaxhighlight>
Instead do
Instead do
<code php>
<syntaxhighlight lang="php">
$canviewreports = has_capability('mod/quiz:viewreports', $context);
$canviewreports = has_capability('mod/quiz:viewreports', $context);
foreach ($attempts as $attempt) {
foreach ($attempts as $attempt) {
Line 148: Line 152:
     }
     }
}
}
</code>
</syntaxhighlight>


get_users_by_capability is a very expensive computation. If you are calling it more than once in your script, you are probably doing something wrong ;-)
get_users_by_capability is a very expensive computation. If you are calling it more than once in your script, you are probably doing something wrong ;-)

Latest revision as of 13:34, 14 July 2021

In order to add a capabilities for your <<NEWMODULE>> you need to:


1. Create a file access.php in the <<NEWMODULE>>/db directory

This should contain a list of the capabilities that you want to define. For example:

$capabilities = array(
    'mod/<<NEWMODULE>>:<<CAPABILITYNAME>>' => array(
        'riskbitmask'  => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_CONFIG,
        'captype'      => 'write',
        'contextlevel' => CONTEXT_MODULE,
        'archetypes'   => array(
            'student'        => CAP_ALLOW,
            'teacher'        => CAP_ALLOW,
            'editingteacher' => CAP_ALLOW,
            'manager'          => CAP_ALLOW
        )
    ),

    // Add more capabilities here ...
)

The various parts of the capability definition are:

Capability name ('mod/<<NEWMODULE>>:<<CAPABILITYNAME>>)

This is the internal name used for this this capability. In addition to this internal name, you should also add the language string <<NEWMODULE>>:<<CAPABILITYNAME>> to your module's language file, to give the capability a name that users will see in the interface.

riskbitmask

Allowing people to do various things sometimes requires introducing possible security risks. For example, if you can post to a forum, you can post unsolicited advertising. To a certain extent users have to be trusted. To help administrators and teachers know what the issues are, each capability should list any associated risks. See Hardening_new_Roles_system. will be reflected in the list of icons of each row of the 'Override permissions'->roles page.

Technically, this value is a bit field, so you should combine the relevant risks constants with the '|' operator. So typical values might be:

  • RISK_SPAM
  • RISK_PERSONAL | RISK_XSS | RISK_DATALOSS

captype

Should be either 'read' or 'write'. 'read' is for capabilities that just let you view things. 'write' for capabilities that let you change things.

contextlevel

The context level where this capability is most relevant. If you are writing a module this will almost always be CONTEXT_MODULE. (This does not have much effect. It is just used to sort and group capabilities on the define roles and override roles pages.)

archetypes

This section defines, for each role type, what default permissions those roles should be given when your module is first installed (or when a new capability is detected on upgrade).

Normally, you just add one line for each role that you want to give the capability to. The line should look like 'roletype' => CAP_ALLOW. Just leave out roles that you do not want to get the capability by default. Very exceptionally, you may need to specify a default permission of CAP_PREVENT, CAP_PROHIBIT.

Note that once a capability is established, permissions will not be automatically overwritten when a module is updated. If permissions have changed, an administrator must manually change or force capabilities to be reset to default for a role.

If you don't want to specify any roles that will be given your capability by default, you can pass a blank array to the 'archetypes' parameter:

'mod/newmodule:dosomething' => array(
    'captype' => 'read',
    'contextlevel' => CONTEXT_MODULE,
    'archetypes' => array()
),

2. Get Moodle to load the updated capabilities

The capabilities you defined are only read (and copied into the Moodle database) when your module is installed or upgraded. So every time you edit the db/access.php file you must

  1. Increase your module's version number by editing the file mod/<<NEWMODULE>>/version.php.
  2. Go to the the Administration ► Notifications page, and click through the steps to let Moodle upgrade itself. You should see the name of your module in one of the steps.


3. Checking the capability in your code

In order to check whether the current user has a particular capability, you need to use the has_capability function. To do that, first you have to get the appropriate context. In this case, it will be a module context.

1. First we need to get the $cm id, and verify that it is correct (there are lots of different ways you might do this, this is only an example.

$cmid = required_param('cmid', PARAM_INT);
if (!$cm = get_coursemodule_from_id('<<NEWMODULE>>', $cmid)) {
    error("Course module ID was incorrect");
}

2. Then you get the module context:

$context = context_module::instance($cm->id);
//for course use below code
//$context = context_course::instance($cm->id);
//for system 
//$context = context_system::instance($cm->id);

3. Finally, you can actually check the permission

if (has_capability('mod/<<NEWMODULE>>:<<CAPABILITYNAME>>', $context)) {

Normally, you do 1. and 2. once at the top of a script, and then call has_capability as needed within the script with the appropriate capabilities.

Useful variations

Controlling overall access to a script

Suppose you have a page that should only be available to users with a particular capability. For example, only users with mod/quiz:viewreports should be able to access mod/quiz/report.php. In cases like this, you can use the require_capability function:

require_capability($capability, $context);

near the top of your script. (As soon as you have got the context and called require_login is a good time.) All this does internally is

if (!has_capability($capability, $context)) {
    // Display error and exit.
}

but using require_capability makes your code simpler and is recommended. (Of course, anywhere you might print a link to a page like this, you should only print the link if the user has the right capability.)

Getting a list of users with a capability

Suppose you need to get a list of all the users with a particular capability. (For example, the quiz reports list all the users with the mod/quiz:attempt capability. Then you can use the get_users_by_capability function.

Checking the permissions of another user

There is an optional 3rd parameter to has_capability that you can use to check another user's permissions:

has_capability($capability, $context, $otheruser->id);

Excluding administrators

Administrators have a magic 'moodle/site:doanything' capability that gives them every other capability. If you wish to disable that magic override for one particular capability check, you can use the optional 4th parameter to has capability:

has_capability($capability, $context, NULL, false);

However, you normally should not do this.

Performance considerations

The has_capability function has been carefully optimised, and is pretty fast and you should not really worry. However, it has to perform a fairly complex computation, and if you are going to make exactly the same has_capability call several times in a page (perhaps in a loop) it is probably worth moving the permission check outside the loop. For example don't do:

foreach ($attempts as $attempt) {
    if (has_capability('mod/quiz:viewreports', $context)) {
        // ...
    }
}

Instead do

$canviewreports = has_capability('mod/quiz:viewreports', $context);
foreach ($attempts as $attempt) {
    if ($canviewreports) {
        // ...
    }
}

get_users_by_capability is a very expensive computation. If you are calling it more than once in your script, you are probably doing something wrong ;-)

See also