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
(25 intermediate revisions by 9 users not shown)
Line 1: Line 1:
Create one access.php file in the <<NEWMODULE>>/db directory.
{{New_Module}}
In order to add a capabilities for your <<NEWMODULE>> you need to:


It must contain
*the capabilities to be installed and
*the defaults for each standard role.


A model of what has to be added is:
==1. Create a file access.php in the <<NEWMODULE>>/db directory==
    $mod_glossary_capabilities = array(
        'mod/<<NEWMODULE>>:<<CAPABILITYNAME>>' => array(
            'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_CONFIG,
            'captype' => 'write',
            'contextlevel' => CONTEXT_MODULE,
            'legacy' => array(
                'student' => CAP_ALLOW,
                'teacher' => CAP_ALLOW,
                'editingteacher' => CAP_ALLOW,
                'admin' => CAP_ALLOW
            )
        ),
    and you are supposed to iterate the array 'mod/<<NEWMODULE>>:<<CAPABILITYNAME>>'
    for each <<CAPABILITYNAME>> you want to add
    following the same structure described before and, at the end,
    close the main array with
    )


The element 'riskbitmask' will be reflected in the list of icons of each row of the 'Override permissions'->roles page.
This should contain a list of the capabilities that you want to define. For example:


You should add the right number of icon ranging from
<code php>
     all four icons
$capabilities = array(
         'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_CONFIG,
     'mod/<<NEWMODULE>>:<<CAPABILITYNAME>>' => array(
    up to no icon at all,
         'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_CONFIG,
         just removing the element 'riskbitmask' from the array 'mod/<<NEWMODULE>>:<<CAPABILITYNAME>>'
        'captype'      => 'write',
        'contextlevel' => CONTEXT_MODULE,
         'archetypes'   => array(
            'student'       => CAP_ALLOW,
            'teacher'        => CAP_ALLOW,
            'editingteacher' => CAP_ALLOW,
            'manager'          => CAP_ALLOW
        )
    ),


The element 'captype' is ???
    // Add more capabilities here ...
)
</code>


The element 'contextlevel' is ???
The various parts of the capability definition are:


The element 'legacy' defines the legacy default for that permission. It is an array and for each each element you are allowed to choose one (and only one) of these four elements: CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT, and inherit (if you don't set it). This defines the default selected radio button in the 'Legacy roles' page.
===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|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 [[:en:Manage_roles#Reset_role_to_defaults|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:
<code php>
'mod/newmodule:dosomething' => array(
    'captype' => 'read',
    'contextlevel' => CONTEXT_MODULE,
    'archetypes' => array()
),
</code>
 
==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
# Increase your module's version number by editing the file mod/<<NEWMODULE>>/version.php.
# 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.
<code php>
$cmid = required_param('cmid', PARAM_INT);
if (!$cm = get_coursemodule_from_id('<<NEWMODULE>>', $cmid)) {
    error("Course module ID was incorrect");
}
</code>
 
2. Then you get the module context:
<code php>
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
</code>
 
3. Finally, you can actually check the permission
<code php>
if (has_capability('mod/<<NEWMODULE>>:<<CAPABILITYNAME>>', $context)) {
</code>
 
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:
<code php>
require_capability($capability, $context);
</code>
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>
if (!has_capability($capability, $context)) {
    // Display error and exit.
}
</code>
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:
<code php>
has_capability($capability, $context, $otheruser->id);
</code>
 
====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:
<code php>
has_capability($capability, $context, NULL, false);
</code>
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:
<code php>
foreach ($attempts as $attempt) {
    if (has_capability('mod/quiz:viewreports', $context)) {
        // ...
    }
}
</code>
Instead do
<code php>
$canviewreports = has_capability('mod/quiz:viewreports', $context);
foreach ($attempts as $attempt) {
    if ($canviewreports) {
        // ...
    }
}
</code>
 
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==
 
* [[Access API]]
* [[Roles]]
* [[Hardening_new_Roles_system]] - information about risks
* [[NEWMODULE_Documentation]] - NEWMODULE Documentation front page
 
[[Category:Roles]]

Revision as of 13:38, 25 April 2018

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 = get_context_instance(CONTEXT_MODULE, $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