MDL-62062 tool_policy: Let manager/dpo withdraw consent to policies
authorSara Arjona <sara@moodle.com>
Thu, 3 May 2018 11:27:04 +0000 (13:27 +0200)
committerSara Arjona <sara@moodle.com>
Mon, 7 May 2018 08:11:13 +0000 (10:11 +0200)
The policies can be revoked by clicking the "Agreed [on behalf]" ticks.
A message has been added also to explain users they must contact to
the DPO if they want to withdraw their consent to policies.

12 files changed:
admin/tool/policy/accept.php
admin/tool/policy/amd/build/acceptmodal.min.js
admin/tool/policy/amd/src/acceptmodal.js
admin/tool/policy/classes/api.php
admin/tool/policy/classes/form/accept_policy.php
admin/tool/policy/classes/output/acceptances.php
admin/tool/policy/classes/output/user_agreement.php
admin/tool/policy/lang/en/tool_policy.php
admin/tool/policy/templates/acceptances.mustache
admin/tool/policy/templates/user_agreement.mustache
admin/tool/policy/tests/api_test.php
admin/tool/policy/tests/behat/acceptances.feature

index 8a79198..8e6aece 100644 (file)
@@ -15,7 +15,7 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Accept policies on behalf of users (non-JS version)
+ * Accept or revoke policies on behalf of users (non-JS version)
  *
  * @package     tool_policy
  * @copyright   2018 Marina Glancy
@@ -28,6 +28,7 @@ require_once($CFG->dirroot.'/user/editlib.php');
 $userids = optional_param_array('userids', null, PARAM_INT);
 $versionids = optional_param_array('versionids', null, PARAM_INT);
 $returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
+$action = optional_param('action', null, PARAM_ALPHA);
 
 require_login();
 if (isguestuser()) {
@@ -48,7 +49,7 @@ if ($returnurl) {
 }
 // Initialise the form, this will also validate users, versions and check permission to accept policies.
 $form = new \tool_policy\form\accept_policy(null,
-    ['versionids' => $versionids, 'userids' => $userids, 'showbuttons' => true]);
+    ['versionids' => $versionids, 'userids' => $userids, 'showbuttons' => true, 'action' => $action]);
 $form->set_data(['returnurl' => $returnurl]);
 
 if ($form->is_cancelled()) {
@@ -58,8 +59,14 @@ if ($form->is_cancelled()) {
     redirect($returnurl);
 }
 
+if ($action == 'revoke') {
+    $title = get_string('revokedetails', 'tool_policy');
+} else {
+    $title = get_string('consentdetails', 'tool_policy');
+}
+
 $output = $PAGE->get_renderer('tool_policy');
 echo $output->header();
-echo $output->heading(get_string('consentdetails', 'tool_policy'));
+echo $output->heading($title);
 $form->display();
 echo $output->footer();
index 8c9aab2..da1432a 100644 (file)
Binary files a/admin/tool/policy/amd/build/acceptmodal.min.js and b/admin/tool/policy/amd/build/acceptmodal.min.js differ
index 07b9ed7..a642ac1 100644 (file)
@@ -71,6 +71,14 @@ define(['jquery', 'core/str', 'core/modal_factory', 'core/modal_events', 'core/n
             },
             {
                 key: 'ok'
+            },
+            {
+                key: 'revokedetails',
+                component: 'tool_policy'
+            },
+            {
+                key: 'irevokethepolicy',
+                component: 'tool_policy'
             }
         ];
 
@@ -111,16 +119,33 @@ define(['jquery', 'core/str', 'core/modal_factory', 'core/modal_events', 'core/n
          * @param {object} triggerElement The trigger HTML jQuery object
          */
         AcceptOnBehalf.prototype.showFormModal = function(formData, triggerElement) {
+            var action;
+            var params = formData.split('&');
+            for (var i = 0; i < params.length; i++) {
+                var pair = params[i].split('=');
+                if (pair[0] == 'action') {
+                    action = pair[1];
+                }
+            }
             // Fetch the title string.
             Str.get_strings(this.stringKeys).done(function(strings) {
+                var title;
+                var saveText;
+                if (action == 'revoke') {
+                    title = strings[4];
+                    saveText = strings[5];
+                } else {
+                    title = strings[0];
+                    saveText = strings[1];
+                }
                 // Create the modal.
                 ModalFactory.create({
                     type: ModalFactory.types.SAVE_CANCEL,
-                    title: strings[0],
+                    title: title,
                     body: ''
                 }, triggerElement).done(function(modal) {
                     this.modal = modal;
-                    this.setupFormModal(formData, strings[1]);
+                    this.setupFormModal(formData, saveText);
                 }.bind(this));
             }.bind(this))
                 .fail(Notification.exception);
index 0b668d6..39d49a6 100644 (file)
@@ -813,6 +813,39 @@ class api {
         }
     }
 
+    /**
+     * Checks if user can revoke policies for themselves or on behalf of another user
+     *
+     * @param int $userid
+     * @param bool $throwexception
+     * @return bool
+     */
+    public static function can_revoke_policies($userid = null, $throwexception = false) {
+        global $USER;
+
+        if (!isloggedin() || isguestuser()) {
+            if ($throwexception) {
+                throw new \moodle_exception('noguest');
+            } else {
+                return false;
+            }
+        }
+        if (!$userid) {
+            $userid = $USER->id;
+        }
+
+        // At the moment, current users can't revoke their own policies.
+        // Check capability to revoke on behalf as the real user.
+        $realuser = manager::get_realuser();
+        $usercontext = \context_user::instance($userid);
+        if ($throwexception) {
+            require_capability('tool/policy:acceptbehalf', $usercontext, $realuser);
+            return;
+        } else {
+            return has_capability('tool/policy:acceptbehalf', $usercontext, $realuser);
+        }
+    }
+
     /**
      * Accepts the current revisions of all policies that the user has not yet accepted
      *
index 8fcc474..f53af7a 100644 (file)
@@ -32,7 +32,7 @@ defined('MOODLE_INTERNAL') || die();
 require_once($CFG->dirroot.'/lib/formslib.php');
 
 /**
- * Represents the form for accepting a policy.
+ * Represents the form for accepting or revoking a policy.
  *
  * @package     tool_policy
  * @copyright   2018 Marina Glancy
@@ -53,9 +53,10 @@ class accept_policy extends \moodleform {
         if (empty($this->_customdata['versionids']) || !is_array($this->_customdata['versionids'])) {
             throw new \moodle_exception('missingparam', '', '', 'versionids');
         }
+        $revoke = (!empty($this->_customdata['action']) && $this->_customdata['action'] == 'revoke');
         $userids = clean_param_array($this->_customdata['userids'], PARAM_INT);
         $versionids = clean_param_array($this->_customdata['versionids'], PARAM_INT);
-        $usernames = $this->validate_and_get_users($userids);
+        $usernames = $this->validate_and_get_users($userids, $revoke);
         $versionnames = $this->validate_and_get_versions($versionids);
 
         foreach ($usernames as $userid => $name) {
@@ -75,13 +76,23 @@ class accept_policy extends \moodleform {
         $mform->addElement('static', 'policy', get_string('acceptancepolicies', 'tool_policy'),
             join(', ', $versionnames));
 
-        $mform->addElement('static', 'ack', '', get_string('acceptanceacknowledgement', 'tool_policy'));
+        if ($revoke) {
+            $mform->addElement('static', 'ack', '', get_string('revokeacknowledgement', 'tool_policy'));
+            $mform->addElement('hidden', 'action', 'revoke');
+            $mform->setType('action', PARAM_ALPHA);
+        } else {
+            $mform->addElement('static', 'ack', '', get_string('acceptanceacknowledgement', 'tool_policy'));
+        }
 
         $mform->addElement('textarea', 'note', get_string('acceptancenote', 'tool_policy'));
         $mform->setType('note', PARAM_NOTAGS);
 
         if (!empty($this->_customdata['showbuttons'])) {
-            $this->add_action_buttons(true, get_string('iagreetothepolicy', 'tool_policy'));
+            if ($revoke) {
+                $this->add_action_buttons(true, get_string('irevokethepolicy', 'tool_policy'));
+            } else {
+                $this->add_action_buttons(true, get_string('iagreetothepolicy', 'tool_policy'));
+            }
         }
 
         $PAGE->requires->js_call_amd('tool_policy/policyactions', 'init');
@@ -91,9 +102,10 @@ class accept_policy extends \moodleform {
      * Validate userids and return usernames
      *
      * @param array $userids
+     * @param boolean $revoke True if policies will be revoked; false when policies will be accepted.
      * @return array (userid=>username)
      */
-    protected function validate_and_get_users($userids) {
+    protected function validate_and_get_users($userids, $revoke = false) {
         global $DB;
         $usernames = [];
         list($sql, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
@@ -112,7 +124,11 @@ class accept_policy extends \moodleform {
                 throw new \moodle_exception('noguest');
             }
             \context_helper::preload_from_record($user);
-            api::can_accept_policies($userid, true);
+            if ($revoke) {
+                api::can_revoke_policies($userid, true);
+            } else {
+                api::can_accept_policies($userid, true);
+            }
             $usernames[$userid] = fullname($user);
         }
         return $usernames;
@@ -148,8 +164,15 @@ class accept_policy extends \moodleform {
      */
     public function process() {
         if ($data = $this->get_data()) {
+            $revoke = (!empty($data->action) && $data->action == 'revoke');
             foreach ($data->userids as $userid) {
-                \tool_policy\api::accept_policies($data->versionids, $userid, $data->note);
+                if ($revoke) {
+                    foreach ($data->versionids as $versionid) {
+                        \tool_policy\api::revoke_acceptance($versionid, $userid, $data->note);
+                    }
+                } else {
+                    \tool_policy\api::accept_policies($data->versionids, $userid, $data->note);
+                }
             }
         }
     }
index dada079..526f33c 100644 (file)
@@ -50,6 +50,9 @@ class acceptances implements renderable, templatable {
     /** @var moodle_url */
     protected $returnurl;
 
+    /** @var bool */
+    protected $canrevoke;
+
     /**
      * Contructor.
      *
@@ -59,6 +62,7 @@ class acceptances implements renderable, templatable {
     public function __construct($userid, $returnurl = null) {
         $this->userid = $userid;
         $this->returnurl = $returnurl ? (new moodle_url($returnurl))->out(false) : null;
+        $this->canrevoke = \tool_policy\api::can_revoke_policies($this->userid);
     }
 
     /**
@@ -72,6 +76,7 @@ class acceptances implements renderable, templatable {
         $data->hasonbehalfagreements = false;
         $data->pluginbaseurl = (new moodle_url('/admin/tool/policy'))->out(false);
         $data->returnurl = $this->returnurl;
+        $data->canrevoke = $this->canrevoke;
 
         // Get the list of policies and versions that current user is able to see
         // and the respective acceptance records for the selected user.
index b1d96c8..6f4a53a 100644 (file)
@@ -59,6 +59,9 @@ class user_agreement implements \templatable, \renderable {
     /** @var bool */
     protected $canaccept;
 
+    /** @var bool */
+    protected $canrevoke;
+
     /**
      * user_agreement constructor
      *
@@ -68,8 +71,10 @@ class user_agreement implements \templatable, \renderable {
      * @param array $versions list of versions (id=>name)
      * @param bool $onbehalf whether at least one version was accepted by somebody else on behalf of the user
      * @param bool $canaccept does the current user have permission to accept the policy on behalf of user $userid
+     * @param bool $canrevoke does the current user have permission to revoke the policy on behalf of user $userid
      */
-    public function __construct($userid, $accepted, moodle_url $pageurl, $versions, $onbehalf = false, $canaccept = null) {
+    public function __construct($userid, $accepted, moodle_url $pageurl, $versions, $onbehalf = false,
+                                $canaccept = null, $canrevoke = null) {
         $this->userid = $userid;
         $this->onbehalf = $onbehalf;
         $this->pageurl = $pageurl;
@@ -79,6 +84,9 @@ class user_agreement implements \templatable, \renderable {
         if (count($this->accepted) < count($this->versions) && $canaccept === null) {
             $this->canaccept = \tool_policy\api::can_accept_policies($this->userid);
         }
+        if (count($this->accepted) == count($this->versions) && $canrevoke === null) {
+            $this->canrevoke = \tool_policy\api::can_revoke_policies($this->userid);
+        }
     }
 
     /**
@@ -92,6 +100,7 @@ class user_agreement implements \templatable, \renderable {
             'status' => count($this->accepted) == count($this->versions),
             'onbehalf' => $this->onbehalf,
             'canaccept' => $this->canaccept,
+            'canrevoke' => $this->canrevoke,
         ];
         if (!$data['status'] && $this->canaccept) {
             $linkparams = ['userids[0]' => $this->userid];
@@ -101,6 +110,15 @@ class user_agreement implements \templatable, \renderable {
             $linkparams['returnurl'] = $this->pageurl->out_as_local_url(false);
             $link = new \moodle_url('/admin/tool/policy/accept.php', $linkparams);
             $data['acceptlink'] = $link->out(false);
+        } else if ($data['status'] && $this->canrevoke) {
+            $linkparams = ['userids[0]' => $this->userid];
+            foreach (array_keys($this->versions) as $versionid) {
+                $linkparams["versionids[{$versionid}]"] = $versionid;
+            }
+            $linkparams['returnurl'] = $this->pageurl->out_as_local_url(false);
+            $linkparams['action'] = 'revoke';
+            $link = new \moodle_url('/admin/tool/policy/accept.php', $linkparams);
+            $data['revokelink'] = $link->out(false);
         }
         $data['singleversion'] = count($this->versions) == 1;
         if ($data['singleversion']) {
index 5febbbe..32be8f9 100644 (file)
@@ -25,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$string['acceptanceacknowledgement'] = 'I acknowledge that consents to these policies have been acquired';
+$string['acceptanceacknowledgement'] = 'I acknowledge that I have received the user\'s request to consent on the abovementioned policy on behalf of the user.';
 $string['acceptancecount'] = '{$a->agreedcount} of {$a->policiescount}';
 $string['acceptancenote'] = 'Remarks';
 $string['acceptancepolicies'] = 'Policies';
@@ -39,12 +39,22 @@ $string['activateconfirm'] = '<p>You are about to activate policy <em>\'{$a->nam
 $string['activateconfirmyes'] = 'Activate';
 $string['agreed'] = 'Agreed';
 $string['agreedby'] = 'Agreed by';
+$string['agreedno'] = 'Not agreed';
+$string['agreednowithlink'] = 'Not agreed, click to agree to "{$a}"';
+$string['agreednowithlinkall'] = 'Not agreed, click to agree to all policies';
 $string['agreedon'] = 'Agreed on';
+$string['agreedyes'] = 'Agreed';
+$string['agreedyesonbehalf'] = 'Agreed on behalf of';
+$string['agreedyesonbehalfwithlink'] = 'Agreed on behalf of, click to withdraw consent to "{$a}"';
+$string['agreedyesonbehalfwithlinkall'] = 'Agreed on behalf of, click to withdraw consent to all policies"';
+$string['agreedyeswithlink'] = 'Agreed, click to withdraw consent to "{$a}"';
+$string['agreedyeswithlinkall'] = 'Agreed, click to withdraw consent to all policies';
 $string['agreepolicies'] = 'Please agree to the following policies';
 $string['backtotop'] = 'Back to top';
 $string['consentbulk'] = 'Consent';
-$string['consentdetails'] = 'Consent details';
+$string['consentdetails'] = 'Agree on behalf of the user';
 $string['consentpagetitle'] = 'Consent';
+$string['contactdpo'] = 'For questions regarding the policies please contact the Data Protection Officer.';
 $string['dataproc'] = 'Personal data processing';
 $string['deleting'] = 'Deleting a version';
 $string['deleteconfirm'] = '<p>Are you sure you want to delete policy <em>\'{$a->name}\'</em>?</p><p>This operation can not be undone.</p>';
@@ -67,12 +77,13 @@ $string['filterpolicy'] = 'Policy: {$a}';
 $string['guestconsent:continue'] = 'Continue';
 $string['guestconsentmessage'] = 'If you continue browsing this website, you agree to our policies:';
 $string['iagree'] = 'I agree to the {$a}';
-$string['iagreetothepolicy'] = 'I agree to the policy';
+$string['iagreetothepolicy'] = 'Agree';
 $string['inactivate'] = 'Set status to "Inactive"';
 $string['inactivating'] = 'Inactivating a policy';
 $string['inactivatingconfirm'] = '<p>You are about to inactivate policy <em>\'{$a->name}\'</em> version <em>\'{$a->revision}\'</em>.</p><p>The policy will not apply until some version is made the current one.</p>';
 $string['inactivatingconfirmyes'] = 'Inactivate';
 $string['invalidversionid'] = 'There is no policy with this identifier!';
+$string['irevokethepolicy'] = 'Withdraw consent';
 $string['minorchange'] = 'Minor change';
 $string['minorchangeinfo'] = 'Minor changes do not amend the meaning of the policy text, terms or conditions. Users do not need to reconfirm their consent.';
 $string['managepolicies'] = 'Manage policies';
@@ -128,6 +139,8 @@ $string['privacy:metadata:acceptances:note'] = 'Any comments added by the user w
 $string['privacysettings'] = 'Privacy settings';
 $string['readpolicy'] = 'Please read our {$a}';
 $string['refertofullpolicytext'] = 'Please refer to the full {$a} text if you would like to review.';
+$string['revokeacknowledgement'] = 'I acknowledge that I have received the user\'s request to withdraw consent on the abovementioned policy on behalf of the user.';
+$string['revokedetails'] = 'Withdraw user\'s consent';
 $string['save'] = 'Save';
 $string['saveasdraft'] = 'Save as draft';
 $string['selectuser'] = 'Select user {$a}';
@@ -146,8 +159,3 @@ $string['userpolicysettings'] = 'Policies';
 $string['usersaccepted'] = 'Agreements';
 $string['viewarchived'] = 'View previous versions';
 $string['viewconsentpageforuser'] = 'Viewing this page on behalf of {$a}';
-$string['agreedno'] = 'Not agreed';
-$string['agreednowithlink'] = 'Not agreed, click to agree to "{$a}"';
-$string['agreednowithlinkall'] = 'Not agreed, click to agree to all policies';
-$string['agreedyes'] = 'Agreed';
-$string['agreedyesonbehalf'] = 'Agreed on behalf of';
index 6c37b4c..1315448 100644 (file)
@@ -31,6 +31,7 @@
     Example context (json):
     {
         "hasonbehalfagreements": true,
+        "canrevoke": true,
         "policies": [
             {
               "versions": [
         ]
     }
 }}
+{{^canrevoke}}
+    <div class="alert alert-info">{{#str}} contactdpo, tool_policy {{/str}}</div>
+{{/canrevoke}}
+
 <table class="generaltable fullwidth">
     <thead>
     <tr>
index 684a7cb..0836ed3 100644 (file)
         "status": false,
         "onbehalf": false,
         "canaccept": true,
+        "canrevoke": true,
         "acceptlink": "/",
+        "revokelink": "/",
         "singleversion": false,
         "versionname": ""
     }
 }}
 {{#status}}
+    {{#canrevoke}}
+        {{#singleversion}}
+            {{#onbehalf}}
+                <a href="{{revokelink}}" data-action="acceptmodal">{{#pix}}agreedyesonbehalf, tool_policy,
+                    {{#str}} agreedyesonbehalfwithlink, tool_policy, {{{versionname}}} {{/str}}{{/pix}}</a>
+            {{/onbehalf}}
+            {{^onbehalf}}
+                <a href="{{revokelink}}" data-action="acceptmodal">{{#pix}}agreedyes, tool_policy,
+                    {{#str}} agreedyeswithlink, tool_policy, {{{versionname}}} {{/str}}{{/pix}}</a>
+            {{/onbehalf}}
+        {{/singleversion}}
+        {{^singleversion}}
+            {{#onbehalf}}
+                <a href="{{revokelink}}" data-action="acceptmodal">{{#pix}}agreedyesonbehalf, tool_policy,
+                    {{#str}} agreedyesonbehalfwithlinkall, tool_policy {{/str}}{{/pix}}</a>
+            {{/onbehalf}}
+            {{^onbehalf}}
+                <a href="{{revokelink}}" data-action="acceptmodal">{{#pix}}agreedyes, tool_policy,
+                    {{#str}} agreedyeswithlinkall, tool_policy {{/str}}{{/pix}}</a>
+            {{/onbehalf}}
+        {{/singleversion}}
+    {{/canrevoke}}
 
-    {{#onbehalf}}
-        <span>{{#pix}}agreedyesonbehalf, tool_policy, {{#str}} agreedyesonbehalf, tool_policy {{/str}}{{/pix}}</span>
-    {{/onbehalf}}
-    {{^onbehalf}}
-        <span>{{#pix}}agreedyes, tool_policy, {{#str}} agreedyes, tool_policy {{/str}}{{/pix}}</span>
-    {{/onbehalf}}
+    {{^canrevoke}}
+        {{#onbehalf}}
+            <span>{{#pix}}agreedyesonbehalf, tool_policy, {{#str}} agreedyesonbehalf, tool_policy {{/str}}{{/pix}}</span>
+        {{/onbehalf}}
+        {{^onbehalf}}
+            <span>{{#pix}}agreedyes, tool_policy, {{#str}} agreedyes, tool_policy {{/str}}{{/pix}}</span>
+        {{/onbehalf}}
+    {{/canrevoke}}
 {{/status}}
+
 {{^status}}
     {{#canaccept}}
         {{#singleversion}}
index 547bffc..ed5f3cb 100644 (file)
@@ -368,6 +368,72 @@ class tool_policy_api_testcase extends advanced_testcase {
         $this->assertTrue(api::can_user_view_policy_version($policy3, null, $parent->id));
     }
 
+    /**
+     * Test behaviour of the {@link api::can_revoke_policies()} method.
+     */
+    public function test_can_revoke_policies() {
+        global $CFG;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        $user = $this->getDataGenerator()->create_user();
+        $child = $this->getDataGenerator()->create_user();
+        $parent = $this->getDataGenerator()->create_user();
+        $officer = $this->getDataGenerator()->create_user();
+        $manager = $this->getDataGenerator()->create_user();
+
+        $syscontext = context_system::instance();
+        $childcontext = context_user::instance($child->id);
+
+        $roleminorid = create_role('Digital minor', 'digiminor', 'Not old enough to accept site policies themselves');
+        $roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
+        $roleofficerid = create_role('Policy officer', 'policyofficer', 'Can see all acceptances but can\'t edit policy documents');
+        $rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
+
+        assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
+        assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
+        assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleofficerid, $syscontext->id);
+        assign_capability('tool/policy:viewacceptances', CAP_ALLOW, $roleofficerid, $syscontext->id);
+        assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $syscontext->id);
+        assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
+
+        role_assign($roleminorid, $child->id, $syscontext->id);
+        // Becoming a parent is easy. Being a good one is difficult.
+        role_assign($roleparentid, $parent->id, $childcontext->id);
+        role_assign($roleofficerid, $officer->id, $syscontext->id);
+        role_assign($rolemanagerid, $manager->id, $syscontext->id);
+
+        accesslib_clear_all_caches_for_unit_testing();
+
+        // Prepare a policy document with some versions.
+        list($policy1, $policy2, $policy3) = $this->create_versions(3);
+
+        // Normally users do not have access to revoke policies.
+        $this->setUser($user);
+        $this->assertFalse(api::can_revoke_policies($user->id));
+        $this->setUser($child);
+        $this->assertFalse(api::can_revoke_policies($child->id));
+
+        // The parent can revoke the policy on behalf of her child (but not her own policies).
+        $this->setUser($parent);
+        $this->assertFalse(api::can_revoke_policies($parent->id));
+        $this->assertTrue(api::can_revoke_policies($child->id));
+
+        // Officers and managers can revoke everything.
+        $this->setUser($officer);
+        $this->assertTrue(api::can_revoke_policies($officer->id));
+        $this->assertTrue(api::can_revoke_policies($child->id));
+        $this->assertTrue(api::can_revoke_policies($parent->id));
+        $this->assertTrue(api::can_revoke_policies($manager->id));
+
+        $this->setUser($manager);
+        $this->assertTrue(api::can_revoke_policies($manager->id));
+        $this->assertTrue(api::can_revoke_policies($child->id));
+        $this->assertTrue(api::can_revoke_policies($parent->id));
+        $this->assertTrue(api::can_revoke_policies($officer->id));
+    }
+
     /**
      * Test {@link api::fix_revision_values()} behaviour.
      */
index 5bdd926..b8ec129 100644 (file)
@@ -58,12 +58,12 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
     And I navigate to "Privacy and policies > Manage policies" in site administration
     And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
     And I click on "Not agreed" "link" in the "User One" "table_row"
-    Then I should see "Consent details"
+    Then I should see "Agree on behalf of the user"
     And I should see "User One"
     And I should see "This site policy"
-    And I should see "I acknowledge that consents to these policies have been acquired"
+    And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
     And I set the field "Remarks" to "Consent received from a parent"
-    And I press "I agree to the policy"
+    And I press "Agree"
     And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
     And "Max Manager" "link" should exist in the "User One" "table_row"
     And "Consent received from a parent" "text" should exist in the "User One" "table_row"
@@ -83,12 +83,12 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
     And I navigate to "Privacy and policies > Manage policies" in site administration
     And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
     And I click on "Not agreed" "link" in the "User One" "table_row"
-    Then I should see "Consent details"
+    Then I should see "Agree on behalf of the user"
     And I should see "User One"
     And I should see "This site policy"
-    And I should see "I acknowledge that consents to these policies have been acquired"
+    And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
     And I set the field "Remarks" to "Consent received from a parent"
-    And I press "I agree to the policy"
+    And I press "Agree"
     And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
     And "Max Manager" "link" should exist in the "User One" "table_row"
     And "Consent received from a parent" "text" should exist in the "User One" "table_row"
@@ -150,12 +150,12 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
     And I press "Next"
     And I navigate to "Privacy and policies > User agreements" in site administration
     And I click on "Not agreed, click to agree to \"This site policy\"" "link" in the "User One" "table_row"
-    Then I should see "Consent details"
+    Then I should see "Agree on behalf of the user"
     And I should see "User One"
     And I should see "This site policy"
-    And I should see "I acknowledge that consents to these policies have been acquired"
+    And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
     And I set the field "Remarks" to "Consent received from a parent"
-    And I press "I agree to the policy"
+    And I press "Agree"
     And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
     And "Not agreed, click to agree to \"This privacy policy\"" "icon" should exist in the "User One" "table_row"
     And I click on "1 of 2" "link" in the "User One" "table_row"
@@ -183,12 +183,12 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
     And I press "Next"
     And I navigate to "Privacy and policies > User agreements" in site administration
     And I click on "Not agreed, click to agree to \"This site policy\"" "link" in the "User One" "table_row"
-    Then I should see "Consent details"
+    Then I should see "Agree on behalf of the user"
     And I should see "User One"
     And I should see "This site policy"
-    And I should see "I acknowledge that consents to these policies have been acquired"
+    And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
     And I set the field "Remarks" to "Consent received from a parent"
-    And I press "I agree to the policy"
+    And I press "Agree"
     And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
     And "Not agreed, click to agree to \"This privacy policy\"" "icon" should exist in the "User One" "table_row"
     And I click on "1 of 2" "link" in the "User One" "table_row"
@@ -248,12 +248,12 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
     And I navigate to "Privacy and policies > Manage policies" in site administration
     And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
     And I click on "Not agreed" "link" in the "User One" "table_row"
-    Then I should see "Consent details"
+    Then I should see "Agree on behalf of the user"
     And I should see "User One"
     And I should see "This site policy"
-    And I should see "I acknowledge that consents to these policies have been acquired"
+    And I should see "I acknowledge that I have received the user's request to consent on the abovementioned policy on behalf of the user."
     And I set the field "Remarks" to "Consent received from a parent"
-    And I press "I agree to the policy"
+    And I press "Agree"
     And "Agreed on behalf of" "icon" should exist in the "User One" "table_row"
     And "Max Manager" "link" should not exist in the "User One" "table_row"
     And "Admin User" "link" should exist in the "User One" "table_row"