MDL-61864 tool_policy: display policies to guests and users
authorSara Arjona <sara@moodle.com>
Thu, 5 Apr 2018 06:04:07 +0000 (14:04 +0800)
committerMarina Glancy <marina@moodle.com>
Mon, 16 Apr 2018 03:30:13 +0000 (11:30 +0800)
24 files changed:
admin/tool/policy/amd/build/jquery-eu-cookie-law-popup.min.js [new file with mode: 0644]
admin/tool/policy/amd/build/policyactions.min.js [new file with mode: 0644]
admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js [new file with mode: 0644]
admin/tool/policy/amd/src/policyactions.js [new file with mode: 0644]
admin/tool/policy/classes/external.php [new file with mode: 0644]
admin/tool/policy/classes/output/guestconsent.php [new file with mode: 0644]
admin/tool/policy/classes/output/page_agreedocs.php [new file with mode: 0644]
admin/tool/policy/classes/output/page_nopermission.php [new file with mode: 0644]
admin/tool/policy/classes/output/page_viewalldoc.php [new file with mode: 0644]
admin/tool/policy/classes/output/page_viewdoc.php [new file with mode: 0644]
admin/tool/policy/classes/policy_version.php [new file with mode: 0644]
admin/tool/policy/classes/privacy/local/sitepolicy/handler.php [new file with mode: 0644]
admin/tool/policy/db/services.php [new file with mode: 0644]
admin/tool/policy/index.php [new file with mode: 0644]
admin/tool/policy/styles.css [new file with mode: 0644]
admin/tool/policy/templates/guestconsent.mustache [new file with mode: 0644]
admin/tool/policy/templates/page_agreedocs.mustache [new file with mode: 0644]
admin/tool/policy/templates/page_nopermission.mustache [new file with mode: 0644]
admin/tool/policy/templates/page_viewalldoc.mustache [new file with mode: 0644]
admin/tool/policy/templates/page_viewdoc.mustache [new file with mode: 0644]
admin/tool/policy/tests/externallib_test.php [new file with mode: 0644]
admin/tool/policy/thirdpartylibs.xml [new file with mode: 0644]
admin/tool/policy/view.php [new file with mode: 0644]
admin/tool/policy/viewall.php [new file with mode: 0644]

diff --git a/admin/tool/policy/amd/build/jquery-eu-cookie-law-popup.min.js b/admin/tool/policy/amd/build/jquery-eu-cookie-law-popup.min.js
new file mode 100644 (file)
index 0000000..8ea0109
Binary files /dev/null and b/admin/tool/policy/amd/build/jquery-eu-cookie-law-popup.min.js differ
diff --git a/admin/tool/policy/amd/build/policyactions.min.js b/admin/tool/policy/amd/build/policyactions.min.js
new file mode 100644 (file)
index 0000000..dcc6f61
Binary files /dev/null and b/admin/tool/policy/amd/build/policyactions.min.js differ
diff --git a/admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js b/admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
new file mode 100644 (file)
index 0000000..dd33415
--- /dev/null
@@ -0,0 +1,261 @@
+/**
+ *
+ * JQUERY EU COOKIE LAW POPUPS
+ * version 1.0.1
+ *
+ * Code on Github:
+ * https://github.com/wimagguc/jquery-eu-cookie-law-popup
+ *
+ * To see a live demo, go to:
+ * http://www.wimagguc.com/2015/03/jquery-eu-cookie-law-popup/
+ *
+ * by Richard Dancsi
+ * http://www.wimagguc.com/
+ *
+ */
+
+define(
+['jquery'],
+function($) {
+
+// for ie9 doesn't support debug console >>>
+if (!window.console) {
+    window.console = {};
+}
+if (!window.console.log) {
+    window.console.log = function () {
+        };
+}
+// ^^^
+
+$.fn.euCookieLawPopup = (function() {
+
+    var _self = this;
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // PARAMETERS (MODIFY THIS PART) //////////////////////////////////////////////////////////////
+    _self.params = {
+        cookiePolicyUrl : 'http://www.wimagguc.com/?cookie-policy',
+        popupPosition : 'top',
+        colorStyle : 'default',
+        compactStyle : false,
+        popupTitle : 'This website is using cookies',
+        popupText : 'We use cookies to ensure that we give you the best experience on our website. ' +
+            'If you continue without changing your settings, we\'ll assume that you are happy to ' +
+            'receive all cookies on this website.',
+        buttonContinueTitle : 'Continue',
+        buttonLearnmoreTitle : 'Learn&nbsp;more',
+        buttonLearnmoreOpenInNewWindow : true,
+        agreementExpiresInDays : 30,
+        autoAcceptCookiePolicy : false,
+        htmlMarkup : null
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // VARIABLES USED BY THE FUNCTION (DON'T MODIFY THIS PART) ////////////////////////////////////
+    _self.vars = {
+        INITIALISED : false,
+        HTML_MARKUP : null,
+        COOKIE_NAME : 'EU_COOKIE_LAW_CONSENT'
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // PRIVATE FUNCTIONS FOR MANIPULATING DATA ////////////////////////////////////////////////////
+
+    // Overwrite default parameters if any of those is present
+    var parseParameters = function(object, markup, settings) {
+
+        if (object) {
+            var className = $(object).attr('class') ? $(object).attr('class') : '';
+            if (className.indexOf('eupopup-top') > -1) {
+                _self.params.popupPosition = 'top';
+            }
+            else if (className.indexOf('eupopup-fixedtop') > -1) {
+                _self.params.popupPosition = 'fixedtop';
+            }
+            else if (className.indexOf('eupopup-bottomright') > -1) {
+                _self.params.popupPosition = 'bottomright';
+            }
+            else if (className.indexOf('eupopup-bottomleft') > -1) {
+                _self.params.popupPosition = 'bottomleft';
+            }
+            else if (className.indexOf('eupopup-bottom') > -1) {
+                _self.params.popupPosition = 'bottom';
+            }
+            else if (className.indexOf('eupopup-block') > -1) {
+                _self.params.popupPosition = 'block';
+            }
+            if (className.indexOf('eupopup-color-default') > -1) {
+                _self.params.colorStyle = 'default';
+            }
+            else if (className.indexOf('eupopup-color-inverse') > -1) {
+                _self.params.colorStyle = 'inverse';
+            }
+            if (className.indexOf('eupopup-style-compact') > -1) {
+                _self.params.compactStyle = true;
+            }
+        }
+
+        if (markup) {
+            _self.params.htmlMarkup = markup;
+        }
+
+        if (settings) {
+            if (typeof settings.cookiePolicyUrl !== 'undefined') {
+                _self.params.cookiePolicyUrl = settings.cookiePolicyUrl;
+            }
+            if (typeof settings.popupPosition !== 'undefined') {
+                _self.params.popupPosition = settings.popupPosition;
+            }
+            if (typeof settings.colorStyle !== 'undefined') {
+                _self.params.colorStyle = settings.colorStyle;
+            }
+            if (typeof settings.popupTitle !== 'undefined') {
+                _self.params.popupTitle = settings.popupTitle;
+            }
+            if (typeof settings.popupText !== 'undefined') {
+                _self.params.popupText = settings.popupText;
+            }
+            if (typeof settings.buttonContinueTitle !== 'undefined') {
+                _self.params.buttonContinueTitle = settings.buttonContinueTitle;
+            }
+            if (typeof settings.buttonLearnmoreTitle !== 'undefined') {
+                _self.params.buttonLearnmoreTitle = settings.buttonLearnmoreTitle;
+            }
+            if (typeof settings.buttonLearnmoreOpenInNewWindow !== 'undefined') {
+                _self.params.buttonLearnmoreOpenInNewWindow = settings.buttonLearnmoreOpenInNewWindow;
+            }
+            if (typeof settings.agreementExpiresInDays !== 'undefined') {
+                _self.params.agreementExpiresInDays = settings.agreementExpiresInDays;
+            }
+            if (typeof settings.autoAcceptCookiePolicy !== 'undefined') {
+                _self.params.autoAcceptCookiePolicy = settings.autoAcceptCookiePolicy;
+            }
+            if (typeof settings.htmlMarkup !== 'undefined') {
+                _self.params.htmlMarkup = settings.htmlMarkup;
+            }
+        }
+
+    };
+
+    var createHtmlMarkup = function() {
+
+        if (_self.params.htmlMarkup) {
+            return _self.params.htmlMarkup;
+        }
+
+        var html =
+            '<div class="eupopup-container' +
+                ' eupopup-container-' + _self.params.popupPosition +
+                (_self.params.compactStyle ? ' eupopup-style-compact' : '') +
+                ' eupopup-color-' + _self.params.colorStyle + '">' +
+                '<div class="eupopup-head">' + _self.params.popupTitle + '</div>' +
+                '<div class="eupopup-body">' + _self.params.popupText + '</div>' +
+                '<div class="eupopup-buttons">' +
+                  '<a href="#" class="eupopup-button eupopup-button_1">' + _self.params.buttonContinueTitle + '</a>' +
+                  '<a href="' + _self.params.cookiePolicyUrl + '"' +
+                     (_self.params.buttonLearnmoreOpenInNewWindow ? ' target=_blank ' : '') +
+                    ' class="eupopup-button eupopup-button_2">' + _self.params.buttonLearnmoreTitle + '</a>' +
+                  '<div class="clearfix"></div>' +
+                '</div>' +
+                '<a href="#" class="eupopup-closebutton">x</a>' +
+            '</div>';
+
+        return html;
+    };
+
+    // Storing the consent in a cookie
+    var setUserAcceptsCookies = function(consent) {
+        var d = new Date();
+        var expiresInDays = _self.params.agreementExpiresInDays * 24 * 60 * 60 * 1000;
+        d.setTime( d.getTime() + expiresInDays );
+        var expires = "expires=" + d.toGMTString();
+        document.cookie = _self.vars.COOKIE_NAME + '=' + consent + "; " + expires + ";path=/";
+
+        $(document).trigger("user_cookie_consent_changed", {'consent' : consent});
+    };
+
+    // Let's see if we have a consent cookie already
+    var userAlreadyAcceptedCookies = function() {
+        var userAcceptedCookies = false;
+        var cookies = document.cookie.split(";");
+        for (var i = 0; i < cookies.length; i++) {
+            var c = cookies[i].trim();
+            if (c.indexOf(_self.vars.COOKIE_NAME) == 0) {
+                userAcceptedCookies = c.substring(_self.vars.COOKIE_NAME.length + 1, c.length);
+            }
+        }
+
+        return userAcceptedCookies;
+    };
+
+    var hideContainer = function() {
+        // $('.eupopup-container').slideUp(200);
+        $('.eupopup-container').animate({
+            opacity: 0,
+            height: 0
+        }, 200, function() {
+            $('.eupopup-container').hide(0);
+        });
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // PUBLIC FUNCTIONS  //////////////////////////////////////////////////////////////////////////
+    var publicfunc = {
+
+        // INITIALIZE EU COOKIE LAW POPUP /////////////////////////////////////////////////////////
+        init : function(settings) {
+
+            parseParameters(
+                $(".eupopup").first(),
+                $(".eupopup-markup").html(),
+                settings);
+
+            // No need to display this if user already accepted the policy
+            if (userAlreadyAcceptedCookies()) {
+                return;
+            }
+
+            // We should initialise only once
+            if (_self.vars.INITIALISED) {
+                return;
+            }
+            _self.vars.INITIALISED = true;
+
+            // Markup and event listeners >>>
+            _self.vars.HTML_MARKUP = createHtmlMarkup();
+
+            if ($('.eupopup-block').length > 0) {
+                $('.eupopup-block').append(_self.vars.HTML_MARKUP);
+            } else {
+                $('BODY').append(_self.vars.HTML_MARKUP);
+            }
+
+            $('.eupopup-button_1').click(function() {
+                setUserAcceptsCookies(true);
+                hideContainer();
+                return false;
+            });
+            $('.eupopup-closebutton').click(function() {
+                setUserAcceptsCookies(true);
+                hideContainer();
+                return false;
+            });
+            // ^^^ Markup and event listeners
+
+            // Ready to start!
+            $('.eupopup-container').show();
+
+            // In case it's alright to just display the message once
+            if (_self.params.autoAcceptCookiePolicy) {
+                setUserAcceptsCookies(true);
+            }
+
+        }
+
+    };
+
+    return publicfunc;
+});
+
+});
diff --git a/admin/tool/policy/amd/src/policyactions.js b/admin/tool/policy/amd/src/policyactions.js
new file mode 100644 (file)
index 0000000..b1155a9
--- /dev/null
@@ -0,0 +1,121 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Policy actions.
+ *
+ * @module     tool_policy/policyactions
+ * @package    tool_policy
+ * @copyright  2018 Sara Arjona (sara@moodle.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+define([
+    'jquery',
+    'core/ajax',
+    'core/notification',
+    'core/modal_factory',
+    'core/modal_events'],
+function($, Ajax, Notification, ModalFactory, ModalEvents) {
+
+    /**
+     * List of action selectors.
+     *
+     * @type {{VIEW_POLICY: string}}
+     */
+    var ACTIONS = {
+        VIEW_POLICY: '[data-action="view"]'
+    };
+
+    /**
+     * PolicyActions class.
+     */
+    var PolicyActions = function() {
+        this.registerEvents();
+    };
+
+    /**
+     * Register event listeners.
+     */
+    PolicyActions.prototype.registerEvents = function() {
+        $(ACTIONS.VIEW_POLICY).click(function(e) {
+            e.preventDefault();
+
+            var versionid = $(this).data('versionid');
+            var behalfid = $(this).data('behalfid');
+
+            var params = {
+                'versionid': versionid,
+                'behalfid': behalfid
+            };
+
+            var request = {
+                methodname: 'tool_policy_get_policy_version',
+                args: params
+            };
+
+            var promises = Ajax.call([request]);
+            var modalTitle = '';
+            var modalType = ModalFactory.types.DEFAULT;
+            $.when(promises[0]).then(function(data) {
+                if (data.result.policy) {
+                    modalTitle = data.result.policy.name;
+                    return data.result.policy.content;
+                }
+                // Fail.
+                Notification.addNotification({
+                    message: data.warnings[0].message,
+                    type: 'error'
+                });
+                return false;
+
+            }).then(function(html) {
+                if (html != false) {
+                    return ModalFactory.create({
+                        title: modalTitle,
+                        body: html,
+                        type: modalType,
+                        large: true
+                    }).then(function(modal) {
+                        // Handle hidden event.
+                        modal.getRoot().on(ModalEvents.hidden, function() {
+                            // Destroy when hidden.
+                            modal.destroy();
+                        });
+
+                        return modal;
+                    });
+                }
+            }).done(function(modal) {
+                // Show the modal.
+                modal.show();
+            }).fail(Notification.exception);
+        });
+
+    };
+
+    return /** @alias module:tool_policy/policyactions */ {
+        // Public variables and functions.
+
+        /**
+         * Initialise the actions helper.
+         *
+         * @method init
+         * @return {PolicyActions}
+         */
+        'init': function() {
+            return new PolicyActions();
+        }
+    };
+});
diff --git a/admin/tool/policy/classes/external.php b/admin/tool/policy/classes/external.php
new file mode 100644 (file)
index 0000000..34caab9
--- /dev/null
@@ -0,0 +1,199 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Class containing the external API functions functions for the Policy tool.
+ *
+ * @package    tool_policy
+ * @copyright  2018 Sara Arjona (sara@moodle.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_policy;
+
+defined('MOODLE_INTERNAL') || die();
+
+use coding_exception;
+use context_system;
+use context_user;
+use core\invalid_persistent_exception;
+use dml_exception;
+use external_api;
+use external_description;
+use external_function_parameters;
+use external_single_structure;
+use external_value;
+use external_warnings;
+use invalid_parameter_exception;
+use moodle_exception;
+use restricted_context_exception;
+use tool_policy\api;
+use tool_policy\form\accept_policy;
+
+/**
+ * Class external.
+ *
+ * The external API for the Policy tool.
+ *
+ * @copyright   2018 Sara Arjona (sara@moodle.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class external extends external_api {
+
+    /**
+     * Parameter description for get_policy_version_parameters().
+     *
+     * @return external_function_parameters
+     */
+    public static function get_policy_version_parameters() {
+        return new external_function_parameters([
+            'versionid' => new external_value(PARAM_INT, 'The policy version ID', VALUE_REQUIRED),
+            'behalfid' => new external_value(PARAM_INT, 'The id of user on whose behalf the user is viewing the policy',
+                VALUE_DEFAULT, 0)
+        ]);
+    }
+
+    /**
+     * Fetch the details of a policy version.
+     *
+     * @param int $versionid The policy version ID.
+     * @param int $behalfid The id of user on whose behalf the user is viewing the policy.
+     * @return array
+     * @throws coding_exception
+     * @throws dml_exception
+     * @throws invalid_parameter_exception
+     * @throws restricted_context_exception
+     * @throws moodle_exception
+     */
+    public static function get_policy_version($versionid, $behalfid = null) {
+        global $PAGE;
+
+        $result = [];
+        $warnings = [];
+        $params = external_api::validate_parameters(self::get_policy_version_parameters(), [
+            'versionid' => $versionid,
+            'behalfid' => $behalfid
+        ]);
+        $versionid = $params['versionid'];
+        $behalfid = $params['behalfid'];
+
+        $context = context_system::instance();
+        $PAGE->set_context($context);
+
+        try {
+            // Validate if the user has access to the policy version.
+            $version = api::get_policy_version($versionid);
+            if (!api::can_user_view_policy_version($version, $behalfid)) {
+                $warnings[] = [
+                    'item' => $versionid,
+                    'warningcode' => 'errorusercantviewpolicyversion',
+                    'message' => get_string('errorusercantviewpolicyversion', 'tool_policy')
+                ];
+            } else if (!empty($version)) {
+                $version = api::get_policy_version($versionid);
+                $policy['name'] = $version->name;
+                $policy['versionid'] = $versionid;
+                list($policy['content'], $notusedformat) = external_format_text(
+                    $version->content,
+                    $version->contentformat,
+                    SYSCONTEXTID,
+                    'tool_policy',
+                    'policydocumentcontent',
+                    $version->id
+                );
+                $result['policy'] = $policy;
+            }
+        } catch (moodle_exception $e) {
+            $warnings[] = [
+                'item' => $versionid,
+                'warningcode' => 'errorpolicyversionnotfound',
+                'message' => get_string('errorpolicyversionnotfound', 'tool_policy')
+            ];
+        }
+
+        return [
+            'result' => $result,
+            'warnings' => $warnings
+        ];
+    }
+
+    /**
+     * Parameter description for get_policy_version().
+     *
+     * @return external_description
+     */
+    public static function get_policy_version_returns() {
+        return new external_single_structure([
+            'result' => new external_single_structure([
+                            'policy' => new external_single_structure([
+                                    'name' => new external_value(PARAM_RAW, 'The policy version name', VALUE_OPTIONAL),
+                                    'versionid' => new external_value(PARAM_INT, 'The policy version id', VALUE_OPTIONAL),
+                                    'content' => new external_value(PARAM_RAW, 'The policy version content', VALUE_OPTIONAL)
+                                    ], 'Policy information', VALUE_OPTIONAL)
+                            ]),
+            'warnings' => new external_warnings()
+        ]);
+    }
+
+    /**
+     * Describes the parameters for submit_create_group_form webservice.
+     * @return external_function_parameters
+     */
+    public static function submit_accept_on_behalf_parameters() {
+        return new external_function_parameters(
+            array(
+                'jsonformdata' => new external_value(PARAM_RAW, 'The data from the create group form, encoded as a json array')
+            )
+        );
+    }
+
+    /**
+     * Submit the create group form.
+     *
+     * @param string $jsonformdata The data from the form, encoded as a json array.
+     * @return int new group id.
+     */
+    public static function submit_accept_on_behalf($jsonformdata) {
+        // We always must pass webservice params through validate_parameters.
+        $params = self::validate_parameters(self::submit_accept_on_behalf_parameters(),
+            ['jsonformdata' => $jsonformdata]);
+
+        self::validate_context(context_system::instance());
+
+        $serialiseddata = json_decode($params['jsonformdata']);
+
+        $data = array();
+        parse_str($serialiseddata, $data);
+
+        // The last param is the ajax submitted data.
+        $mform = new accept_policy(null, $data, 'post', '', null, true, $data);
+
+        // Do the action.
+        $mform->process();
+
+        return true;
+    }
+
+    /**
+     * Returns description of method result value.
+     *
+     * @return external_description
+     * @since Moodle 3.0
+     */
+    public static function submit_accept_on_behalf_returns() {
+        return new external_value(PARAM_BOOL, 'success');
+    }
+}
diff --git a/admin/tool/policy/classes/output/guestconsent.php b/admin/tool/policy/classes/output/guestconsent.php
new file mode 100644 (file)
index 0000000..016cd41
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Provides {@link tool_policy\output\renderer} class.
+ *
+ * @package     tool_policy
+ * @category    output
+ * @copyright   2018 Sara Arjona <sara@moodle.com>
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_policy\output;
+
+defined('MOODLE_INTERNAL') || die();
+
+use moodle_url;
+use renderable;
+use renderer_base;
+use templatable;
+use tool_policy\api;
+use tool_policy\policy_version;
+
+/**
+ * Renderer for the policies plugin.
+ *
+ * @copyright 2018 Sara Arjona <sara@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class guestconsent implements renderable, templatable {
+
+    /**
+     * Export the page data for the mustache template.
+     *
+     * @param renderer_base $output renderer to be used to render the page elements.
+     * @return stdClass
+     */
+    public function export_for_template(renderer_base $output) {
+        global $PAGE;
+
+        $data = (object) [];
+        $data->pluginbaseurl = (new moodle_url('/admin/tool/policy'))->out(true);
+        if (strpos(qualified_me(), '/tool/policy/view.php') === false) {
+            // Current page is not a policy doc, so returnurl parameter will be it.
+            $data->returnurl = qualified_me();
+        } else {
+            // If current page is also a policy doc to view, get previous returnurl parameter to avoid error.
+            $returnurl = $PAGE->url->get_param('returnurl');
+            if (isset($returnurl)) {
+                $data->returnurl = $returnurl;
+            }
+        }
+        $data->returnurl = urlencode($data->returnurl);
+
+        $policies = api::list_current_versions(policy_version::AUDIENCE_GUESTS);
+        $data->policies = array_values($policies);
+        $data->haspolicies = !empty($policies);
+
+        return $data;
+    }
+}
diff --git a/admin/tool/policy/classes/output/page_agreedocs.php b/admin/tool/policy/classes/output/page_agreedocs.php
new file mode 100644 (file)
index 0000000..7fd6ac6
--- /dev/null
@@ -0,0 +1,414 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Provides {@link tool_policy\output\renderer} class.
+ *
+ * @package     tool_policy
+ * @category    output
+ * @copyright   2018 Sara Arjona <sara@moodle.com>
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_policy\output;
+
+defined('MOODLE_INTERNAL') || die();
+
+use context_system;
+use core\output\notification;
+use core_user;
+use html_writer;
+use moodle_url;
+use renderable;
+use renderer_base;
+use single_button;
+use templatable;
+use tool_policy\api;
+use tool_policy\policy_version;
+
+/**
+ * Represents a page for showing all the policy documents which a user has to agree to.
+ *
+ * @copyright 2018 Sara Arjona <sara@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class page_agreedocs implements renderable, templatable {
+
+    /** @var array $policies List of public policies objects with information about the user acceptance. */
+    protected $policies = null;
+
+    /** @var array $agreedocs List of policy identifiers which the user has agreed using the form. */
+    protected $agreedocs = null;
+
+    /** @var string $action Form action to identify when user agreeds policies. */
+    protected $action = null;
+
+    /** @var int User id who wants to accept this page. */
+    protected $behalfid = null;
+
+    /** @var object User who wants to accept this page. */
+    protected $behalfuser = null;
+
+    /** @var boolean True if signup user has agreed to all the policies; false otherwise. */
+    protected $signupuserpolicyagreed = false;
+
+    /** @var array Info or error messages to show. */
+    protected $messages = [];
+
+    /**
+     * Prepare the page for rendering.
+     *
+     * @param array $agreedocs Array with the policy identifiers which the user has agreed using the form.
+     * @param int $behalfid The userid to accept the policy versions as (such as child's id).
+     * @param string $action Form action to identify when user agreeds policies.
+     */
+    public function __construct($agreedocs = null, $behalfid = 0, $action = null) {
+        global $USER;
+
+        $this->agreedocs = $agreedocs;
+        if (empty($this->agreedocs)) {
+            $this->agreedocs = [];
+        }
+
+        $this->behalfid = $behalfid;
+        $this->action = $action;
+
+        if (!empty($this->behalfid) && $USER->id != $this->behalfid) {
+            $this->behalfuser = core_user::get_user($this->behalfid, '*');
+            // If behalf user doesn't exist, behalfid parameter will be ignored.
+            if ($this->behalfuser === false) {
+                $this->behalfid = 0;
+            }
+        }
+
+        $this->policies = api::list_current_versions(policy_version::AUDIENCE_LOGGEDIN);
+        if (empty($this->behalfid)) {
+            $userid = $USER->id;
+        } else {
+            $userid = $this->behalfid;
+        }
+        $this->accept_and_revoke_policies();
+        $this->prepare_global_page_access($userid);
+        $this->prepare_user_acceptances($userid);
+    }
+
+    /**
+     * Accept and revoke the policy versions.
+     * The capabilities for accepting/revoking policies are checked into the api functions.
+     *
+     */
+    protected function accept_and_revoke_policies() {
+        global $USER;
+
+        if (!empty($USER->id)) {
+            // Existing user.
+            if (!empty($this->action) && confirm_sesskey()) {
+                // The form has been sent. Update policies acceptances according to $this->agreedocs.
+                $lang = current_language();
+                // Accept / revoke policies.
+                $acceptversionids = array();
+                foreach ($this->policies as $policy) {
+                    if (in_array($policy->id, $this->agreedocs)) {
+                        // Save policy version doc to accept it.
+                        $acceptversionids[] = $policy->id;
+                    } else {
+                        // Revoke policy doc.
+                        api::revoke_acceptance($policy->id, $this->behalfid);
+                    }
+                }
+                // Accept all policy docs saved in $acceptversionids.
+                api::accept_policies($acceptversionids, $this->behalfid, null, $lang);
+                // Show a message to let know the user he/she must agree all the policies.
+                if (count($acceptversionids) != count($this->policies)) {
+                    $message = (object) [
+                        'type' => 'error',
+                        'text' => get_string('mustagreetocontinue', 'tool_policy')
+                    ];
+                } else {
+                    $message = (object) [
+                        'type' => 'success',
+                        'text' => get_string('acceptancessavedsucessfully', 'tool_policy')
+                    ];
+                }
+                $this->messages[] = $message;
+            } else if (empty($this->policies)) {
+                // There are no policies to agree to. Update the policyagreed value to avoid display empty consent page.
+                $currentuser = (!empty($this->behalfuser)) ? $this->behalfuser : $USER;
+                // Check for updating when the user policyagreed is false.
+                if (!$currentuser->policyagreed) {
+                    api::update_policyagreed($currentuser);
+                }
+            } else if (empty($USER->policyagreed)) {
+                // Inform users they must agree to all policies before continuing.
+                $message = (object) [
+                    'type' => 'error',
+                    'text' => get_string('mustagreetocontinue', 'tool_policy')
+                ];
+                $this->messages[] = $message;
+            }
+        } else {
+            // New user.
+            if (!empty($this->action) && confirm_sesskey()) {
+                // The form has been sent.
+                $currentpolicyversionids = [];
+                foreach ($this->policies as $policy) {
+                    $currentpolicyversionids[] = $policy->id;
+                }
+                // If the user has accepted all the policies, add it to the session to let continue with the signup process.
+                $this->signupuserpolicyagreed = empty(array_diff($currentpolicyversionids, $this->agreedocs));
+                \cache::make('core', 'presignup')->set('tool_policy_userpolicyagreed',
+                    $this->signupuserpolicyagreed);
+            } else if (empty($this->policies)) {
+                // There are no policies to agree to. Update the policyagreed value to avoid show empty consent page.
+                \cache::make('core', 'presignup')->set('tool_policy_userpolicyagreed', 1);
+            }
+            if (!empty($this->policies) && !$this->signupuserpolicyagreed) {
+                // During the signup process, inform users they must agree to all policies before continuing.
+                $message = (object) [
+                    'type' => 'error',
+                    'text' => get_string('mustagreetocontinue', 'tool_policy')
+                ];
+                $this->messages[] = $message;
+            }
+        }
+    }
+
+    /**
+     * Before display the consent page, the user has to view all the still-non-accepted policy docs.
+     * This function checks if the non-accepted policy docs have been shown and redirect to them.
+     *
+     * @param array $userid User identifier who wants to access to the consent page.
+     * @param url $returnurl URL to return after shown the policy docs.
+     */
+    protected function redirect_to_policies($userid, $returnurl = null) {
+        global $USER;
+
+        $acceptances = api::get_user_acceptances($userid);
+        $allpolicies = $this->policies;
+        if (!empty($userid)) {
+            foreach ($allpolicies as $policy) {
+                if (api::is_user_version_accepted($userid, $policy->id, $acceptances)) {
+                    // If this version is accepted by the user, remove from the pending policies list.
+                    unset($allpolicies[array_search($policy, $allpolicies)]);
+                }
+            }
+        }
+
+        if (!empty($allpolicies)) {
+            $currentpolicyversionids = [];
+            foreach ($allpolicies as $policy) {
+                $currentpolicyversionids[] = $policy->id;
+            }
+
+            $cache = \cache::make('core', 'presignup');
+            $cachekey = 'tool_policy_viewedpolicies';
+
+            $viewedpolicies = $cache->get($cachekey);
+            if (!empty($viewedpolicies)) {
+                // Get the list of the policies docs which the user haven't viewed during this session.
+                $pendingpolicies = array_diff($currentpolicyversionids, $viewedpolicies);
+            } else {
+                $pendingpolicies = $currentpolicyversionids;
+            }
+            if (count($pendingpolicies) > 0) {
+                // Still is needed to show some policies docs. Save in the session and redirect.
+                $policyversionid = array_shift($pendingpolicies);
+                $viewedpolicies[] = $policyversionid;
+                $cache->set($cachekey, $viewedpolicies);
+                if (empty($returnurl)) {
+                    $returnurl = new moodle_url('/admin/tool/policy/index.php');
+                }
+                $urlparams = ['versionid' => $policyversionid,
+                              'returnurl' => $returnurl,
+                              'numpolicy' => count($currentpolicyversionids) - count($pendingpolicies),
+                              'totalpolicies' => count($currentpolicyversionids),
+                ];
+                redirect(new moodle_url('/admin/tool/policy/view.php', $urlparams));
+            }
+        }
+    }
+
+    /**
+     * Redirect to $SESSION->wantsurl if defined or to $CFG->wwwroot if not.
+     */
+    protected function redirect_to_previous_url() {
+        global $SESSION, $CFG;
+
+        if (!empty($SESSION->wantsurl)) {
+            $returnurl = $SESSION->wantsurl;
+            unset($SESSION->wantsurl);
+        } else {
+            $returnurl = $CFG->wwwroot.'/';
+        }
+
+        redirect($returnurl);
+    }
+
+    /**
+     * Sets up the global $PAGE and performs the access checks.
+     *
+     * @param int $userid
+     */
+    protected function prepare_global_page_access($userid) {
+        global $PAGE, $SESSION, $SITE, $USER;
+
+        // Guest users or not logged users (but the users during the signup process) are not allowed to access to this page.
+        $newsignupuser = !empty($SESSION->wantsurl) && strpos($SESSION->wantsurl, 'login/signup.php') !== false;
+        if (isguestuser() || (empty($USER->id) && !$newsignupuser)) {
+            $this->redirect_to_previous_url();
+        }
+
+        // Check for correct user capabilities.
+        if (!empty($USER->id)) {
+            // For existing users, it's needed to check if they have the capability for accepting policies.
+            if (empty($this->behalfid) || $this->behalfid == $USER->id) {
+                require_capability('tool/policy:accept', context_system::instance());
+            } else {
+                $usercontext = \context_user::instance($this->behalfid);
+                require_capability('tool/policy:acceptbehalf', $usercontext);
+            }
+        } else {
+            // For new users, the behalfid parameter is ignored.
+            if ($this->behalfid != $USER->id) {
+                redirect(new moodle_url('/admin/tool/policy/index.php'));
+            }
+        }
+
+        // If the current user has the $USER->policyagreed = 1 or $userpolicyagreed = 1
+        // and $SESSION->wantsurl is defined, redirect to the return page.
+        $hasagreedsignupuser = empty($USER->id) && $this->signupuserpolicyagreed;
+        $hasagreedloggeduser = $USER->id == $userid && !empty($USER->policyagreed);
+        if (!is_siteadmin() && ($hasagreedsignupuser || ($hasagreedloggeduser && !empty($SESSION->wantsurl)))) {
+            $this->redirect_to_previous_url();
+        }
+
+        $myparams = [];
+        if (!empty($USER->id) && !empty($this->behalfid) && $this->behalfid != $USER->id) {
+            $myparams['userid'] = $this->behalfid;
+        }
+        $myurl = new moodle_url('/admin/tool/policy/index.php', $myparams);
+
+        // Redirect to policy docs before the consent page.
+        $this->redirect_to_policies($userid, $myurl);
+
+        // Page setup.
+        $PAGE->set_context(context_system::instance());
+        $PAGE->set_pagelayout('standard');
+        $PAGE->set_url($myurl);
+        $PAGE->set_heading($SITE->fullname);
+        $PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
+        $PAGE->navbar->add(get_string('policiesagreements', 'tool_policy'), new moodle_url('/admin/tool/policy/index.php'));
+    }
+
+    /**
+     * Prepare user acceptances.
+     *
+     * @param int $userid
+     */
+    protected function prepare_user_acceptances($userid) {
+        global $USER;
+
+        // Get all the policy version acceptances for this user.
+        $acceptances = api::get_user_acceptances($userid);
+        $lang = current_language();
+        foreach ($this->policies as $policy) {
+            // Get a link to display the full policy document.
+            $policy->url = new moodle_url('/admin/tool/policy/view.php',
+                array('policyid' => $policy->policyid, 'returnurl' => qualified_me()));
+            $policyattributes = array('data-action' => 'view',
+                                      'data-versionid' => $policy->id,
+                                      'data-behalfid' => $this->behalfid);
+            $policymodal = html_writer::link($policy->url, $policy->name, $policyattributes);
+
+            // Check if this policy version has been agreed or not.
+            if (!empty($userid)) {
+                // Existing user.
+                $versionagreed = false;
+                $policy->versionacceptance = api::get_user_version_acceptance($userid, $policy->id, $acceptances);
+                if (!empty($policy->versionacceptance)) {
+                    // The policy version has ever been agreed. Check if status = 1 to know if still is accepted.
+                    $versionagreed = $policy->versionacceptance->status;
+                    if ($versionagreed) {
+                        if ($policy->versionacceptance->lang != $lang) {
+                            // Add a message because this version has been accepted in a different language than the current one.
+                            $policy->versionlangsagreed = get_string('policyversionacceptedinotherlang', 'tool_policy');
+                        }
+                        if ($policy->versionacceptance->usermodified != $userid && $USER->id == $userid) {
+                            // Add a message because this version has been accepted in behalf of current user.
+                            $policy->versionbehalfsagreed = get_string('policyversionacceptedinbehalf', 'tool_policy');
+                        }
+                    }
+                }
+            } else {
+                // New user.
+                $versionagreed = in_array($policy->id, $this->agreedocs);
+            }
+            $policy->versionagreed = $versionagreed;
+            $policy->policylink = html_writer::link($policy->url, $policy->name);
+            $policy->policymodal = $policymodal;
+        }
+    }
+
+    /**
+     * Export the page data for the mustache template.
+     *
+     * @param renderer_base $output renderer to be used to render the page elements.
+     * @return stdClass
+     */
+    public function export_for_template(renderer_base $output) {
+        global $USER;
+
+        $myparams = [];
+        if (!empty($USER->id) && !empty($this->behalfid) && $this->behalfid != $USER->id) {
+            $myparams['userid'] = $this->behalfid;
+        }
+        $data = (object) [
+            'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
+            'myurl' => (new moodle_url('/admin/tool/policy/index.php', $myparams))->out(false),
+            'sesskey' => sesskey(),
+        ];
+
+        if (!empty($this->messages)) {
+            foreach ($this->messages as $message) {
+                switch ($message->type) {
+                    case 'error':
+                        $data->messages[] = $output->notification($message->text, notification::NOTIFY_ERROR);
+                        break;
+
+                    case 'success':
+                        $data->messages[] = $output->notification($message->text, notification::NOTIFY_SUCCESS);
+                        break;
+
+                    default:
+                        $data->messages[] = $output->notification($message->text, notification::NOTIFY_INFO);
+                        break;
+                }
+            }
+        }
+
+        $data->policies = array_values($this->policies);
+
+        // If viewing docs in behalf of other user, get his/her full name and profile link.
+        if (!empty($this->behalfuser)) {
+            $userfullname = fullname($this->behalfuser, has_capability('moodle/site:viewfullnames', \context_system::instance()) ||
+                        has_capability('moodle/site:viewfullnames', \context_user::instance($this->behalfid)));
+            $data->behalfuser = html_writer::link(\context_user::instance($this->behalfid)->get_url(), $userfullname);
+        }
+
+        return $data;
+    }
+
+}
diff --git a/admin/tool/policy/classes/output/page_nopermission.php b/admin/tool/policy/classes/output/page_nopermission.php
new file mode 100644 (file)
index 0000000..94c0ec8
--- /dev/null
@@ -0,0 +1,178 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Provides {@link tool_policy\output\renderer} class.
+ *
+ * @package     tool_policy
+ * @category    output
+ * @copyright   2018 Sara Arjona <sara@moodle.com>
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_policy\output;
+
+use moodle_exception;
+
+defined('MOODLE_INTERNAL') || die();
+
+use context_system;
+use core_user;
+use html_writer;
+use moodle_url;
+use renderable;
+use renderer_base;
+use templatable;
+use tool_policy\api;
+use tool_policy\policy_version;
+
+/**
+ * Represents a page for showing the error messages.
+ *
+ * This is used when a user has no permission to agree to policies or accept policies on behalf of defined behalfid.
+ *
+ * @copyright 2018 Sara Arjona <sara@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class page_nopermission implements renderable, templatable {
+
+    /** @var int User id who wants to view this page. */
+    protected $behalfid = null;
+
+    /** @var object User who wants to accept this page. */
+    protected $behalfuser = null;
+
+    /** @var bool True if user has permission to accept policy documents; false otherwise. */
+    protected $haspermissionagreedocs = true;
+
+    /** @var array $policies List of public policies objects. */
+    protected $policies = null;
+
+    /**
+     * Prepare the page for rendering.
+     *
+     * @param int $behalfid The userid to consent policies as (such as child's id).
+     */
+    public function __construct($behalfid) {
+        global $USER;
+
+        $this->behalfid = $behalfid;
+        if (!empty($this->behalfid) && $USER->id != $this->behalfid) {
+            $this->behalfuser = core_user::get_user($this->behalfid, '*');
+            // If behalf user doesn't exist, behalfid parameter will be ignored.
+            if ($this->behalfuser === false) {
+                $this->behalfid = 0;
+            }
+        }
+
+        if (!empty($USER->id)) {
+            // For existing users, it's needed to check if they have the capability for accepting policies.
+            if (empty($this->behalfid) || $this->behalfid == $USER->id) {
+                $this->haspermissionagreedocs = has_capability('tool/policy:accept', context_system::instance());
+            } else {
+                $usercontext = \context_user::instance($this->behalfid);
+                $this->haspermissionagreedocs = has_capability('tool/policy:acceptbehalf', $usercontext);
+            }
+        }
+
+        $this->policies = api::list_current_versions(policy_version::AUDIENCE_LOGGEDIN);
+
+        if (empty($this->policies) && !empty($USER->id)) {
+            // Existing user without policies to agree to.
+            $currentuser = (!empty($this->behalfuser)) ? $this->behalfuser : $USER;
+            if (!$currentuser->policyagreed) {
+                // If there are no policies to agreed, change $user->policyagreed to true.
+                api::update_policyagreed($currentuser);
+            }
+        }
+
+        $this->prepare_global_page_access();
+    }
+
+    /**
+     * Sets up the global $PAGE and performs the access checks.
+     */
+    protected function prepare_global_page_access() {
+        global $PAGE, $SITE, $USER;
+
+        $myurl = new moodle_url('/admin/tool/policy/index.php', [
+            'behalfid' => $this->behalfid,
+        ]);
+
+        if (isguestuser() || empty($USER->id) || !$USER->policyagreed) {
+            // Disable notifications for new users, guests or users who haven't agreed to the policies.
+            $PAGE->set_popup_notification_allowed(false);
+        }
+        $PAGE->set_context(context_system::instance());
+        $PAGE->set_pagelayout('standard');
+        $PAGE->set_url($myurl);
+        $PAGE->set_heading($SITE->fullname);
+        $PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
+        $PAGE->navbar->add(get_string('policiesagreements', 'tool_policy'), new moodle_url('/admin/tool/policy/index.php'));
+    }
+
+    /**
+     * Export the page data for the mustache template.
+     *
+     * @param renderer_base $output renderer to be used to render the page elements.
+     * @return stdClass
+     */
+    public function export_for_template(renderer_base $output) {
+        global $CFG;
+
+        $data = (object) [
+            'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
+            'haspermissionagreedocs' => $this->haspermissionagreedocs,
+            'supportname' => $CFG->supportname,
+            'supportemail' => $CFG->supportemail,
+        ];
+
+        // Get the messages to display.
+        $messagetitle = null;
+        $messagedesc = null;
+        if (!$this->haspermissionagreedocs) {
+            if (!empty($this->behalfuser)) {
+                // If viewing docs in behalf of other user, get his/her full name and profile link.
+                $userfullname = fullname($this->behalfuser, has_capability('moodle/site:viewfullnames', \context_system::instance())
+                    || has_capability('moodle/site:viewfullnames', \context_user::instance($this->behalfid)));
+                $data->behalfuser = html_writer::link(\context_user::instance($this->behalfid)->get_url(), $userfullname);
+
+                $messagetitle = get_string('nopermissiontoagreedocsbehalf', 'tool_policy');
+                $messagedesc = get_string('nopermissiontoagreedocsbehalf_desc', 'tool_policy', $data->behalfuser);
+            } else {
+                $messagetitle = get_string('nopermissiontoagreedocs', 'tool_policy');
+                $messagedesc = get_string('nopermissiontoagreedocs_desc', 'tool_policy');
+            }
+        }
+        $data->messagetitle = $messagetitle;
+        $data->messagedesc = $messagedesc;
+
+        // Add policies list.
+        $policieslinks = array();
+        foreach ($this->policies as $policyversion) {
+            // Get a link to display the full policy document.
+            $policyurl = new moodle_url('/admin/tool/policy/view.php',
+                array('policyid' => $policyversion->policyid, 'returnurl' => qualified_me()));
+            $policyattributes = array('data-action' => 'view',
+                                      'data-versionid' => $policyversion->id,
+                                      'data-behalfid' => $this->behalfid);
+            $policieslinks[] = html_writer::link($policyurl, $policyversion->name, $policyattributes);
+        }
+        $data->policies = $policieslinks;
+
+        return $data;
+    }
+}
diff --git a/admin/tool/policy/classes/output/page_viewalldoc.php b/admin/tool/policy/classes/output/page_viewalldoc.php
new file mode 100644 (file)
index 0000000..d6db0b8
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Provides {@link tool_policy\output\renderer} class.
+ *
+ * @package     tool_policy
+ * @category    output
+ * @copyright   2018 Sara Arjona <sara@moodle.com>
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_policy\output;
+
+use moodle_exception;
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once("$CFG->libdir/filelib.php");
+
+use context_system;
+use moodle_url;
+use renderable;
+use renderer_base;
+use single_button;
+use templatable;
+use tool_policy\api;
+use tool_policy\policy_version;
+
+/**
+ * Represents a page for showing all the policy documents with a current version.
+ *
+ * @copyright 2018 Sara Arjona <sara@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class page_viewalldoc implements renderable, templatable {
+
+    /**
+     * Prepare the page for rendering.
+     *
+     */
+    public function __construct() {
+
+        $this->prepare_policies();
+        $this->prepare_global_page_access();
+    }
+
+    /**
+     * Loads the policy versions to display on the page.
+     *
+     */
+    protected function prepare_policies() {
+        global $USER;
+
+        if (isguestuser() || empty($USER->id)) {
+            $audience = policy_version::AUDIENCE_GUESTS;
+        } else {
+            $audience = policy_version::AUDIENCE_LOGGEDIN;
+        }
+        $this->policies = api::list_current_versions($audience);
+    }
+
+    /**
+     * Sets up the global $PAGE and performs the access checks.
+     */
+    protected function prepare_global_page_access() {
+        global $PAGE, $SITE, $USER;
+
+        $myurl = new moodle_url('/admin/tool/policy/viewall.php', []);
+
+        // Disable notifications for new users, guests or users who haven't agreed to the policies.
+        if (isguestuser() || empty($USER->id) || !$USER->policyagreed) {
+            $PAGE->set_popup_notification_allowed(false);
+        }
+
+        $PAGE->set_context(context_system::instance());
+        $PAGE->set_pagelayout('popup');
+        $PAGE->set_url($myurl);
+        $PAGE->set_heading($SITE->fullname);
+        $PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
+    }
+
+    /**
+     * Export the page data for the mustache template.
+     *
+     * @param renderer_base $output renderer to be used to render the page elements.
+     * @return stdClass
+     */
+    public function export_for_template(renderer_base $output) {
+
+        $data = (object) [
+            'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
+        ];
+
+        $data->policies = array_values($this->policies);
+
+        return $data;
+    }
+}
diff --git a/admin/tool/policy/classes/output/page_viewdoc.php b/admin/tool/policy/classes/output/page_viewdoc.php
new file mode 100644 (file)
index 0000000..6b90426
--- /dev/null
@@ -0,0 +1,169 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Provides {@link tool_policy\output\renderer} class.
+ *
+ * @package     tool_policy
+ * @category    output
+ * @copyright   2018 Sara Arjona <sara@moodle.com>
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_policy\output;
+
+use moodle_exception;
+
+defined('MOODLE_INTERNAL') || die();
+
+use context_system;
+use moodle_url;
+use renderable;
+use renderer_base;
+use single_button;
+use templatable;
+use tool_policy\api;
+use tool_policy\policy_version;
+
+/**
+ * Represents a page for showing the given policy document version.
+ *
+ * @copyright 2018 Sara Arjona <sara@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class page_viewdoc implements renderable, templatable {
+
+    /** @var stdClass Exported {@link \tool_policy\policy_version_exporter} to display on this page. */
+    protected $policy;
+
+    /** @var string Return URL. */
+    protected $returnurl = null;
+
+    /** @var int User id who wants to view this page. */
+    protected $behalfid = null;
+
+    /**
+     * Prepare the page for rendering.
+     *
+     * @param int $policyid The policy id for this page.
+     * @param int $versionid The version id to show. Empty tries to load the current one.
+     * @param string $returnurl URL of a page to continue after reading the policy text.
+     * @param int $behalfid The userid to view this policy version as (such as child's id).
+     * @param bool $manage View the policy as a part of the management UI.
+     * @param int $numpolicy Position of the current policy with respect to the total of policy docs to display.
+     * @param int $totalpolicies Total number of policy documents which the user has to agree to.
+     */
+    public function __construct($policyid, $versionid, $returnurl, $behalfid, $manage, $numpolicy = 0, $totalpolicies = 0) {
+
+        $this->returnurl = $returnurl;
+        $this->behalfid = $behalfid;
+        $this->manage = $manage;
+        $this->numpolicy = $numpolicy;
+        $this->totalpolicies = $totalpolicies;
+
+        $this->prepare_policy($policyid, $versionid);
+        $this->prepare_global_page_access();
+    }
+
+    /**
+     * Loads the policy version to display on the page.
+     *
+     * @param int $policyid The policy id for this page.
+     * @param int $versionid The version id to show. Empty tries to load the current one.
+     */
+    protected function prepare_policy($policyid, $versionid) {
+
+        if ($versionid) {
+            $this->policy = api::get_policy_version($versionid);
+
+        } else {
+            $this->policy = array_reduce(api::list_current_versions(), function ($carry, $current) use ($policyid) {
+                if ($current->policyid == $policyid) {
+                    return $current;
+                }
+                return $carry;
+            });
+        }
+
+        if (empty($this->policy)) {
+            // TODO Make this nicer error message.
+            throw new \moodle_exception('err_no_active_version', 'tool_policy');
+        }
+    }
+
+    /**
+     * Sets up the global $PAGE and performs the access checks.
+     */
+    protected function prepare_global_page_access() {
+        global $CFG, $PAGE, $SITE, $USER;
+
+        $myurl = new moodle_url('/admin/tool/policy/view.php', [
+            'policyid' => $this->policy->policyid,
+            'versionid' => $this->policy->id,
+            'returnurl' => $this->returnurl,
+            'behalfid' => $this->behalfid,
+            'manage' => $this->manage,
+            'numpolicy' => $this->numpolicy,
+            'totalpolicies' => $this->totalpolicies,
+        ]);
+
+        if ($this->manage) {
+            require_once($CFG->libdir.'/adminlib.php');
+            admin_externalpage_setup('tool_policy_managedocs', '', null, $myurl);
+            require_capability('tool/policy:managedocs', context_system::instance());
+            $PAGE->navbar->add(format_string($this->policy->name),
+                new moodle_url('/admin/tool/policy/managedocs.php', ['id' => $this->policy->policyid]));
+        } else {
+            if ($this->policy->status != policy_version::STATUS_ACTIVE) {
+                require_login();
+            } else if (isguestuser() || empty($USER->id) || !$USER->policyagreed) {
+                // Disable notifications for new users, guests or users who haven't agreed to the policies.
+                $PAGE->set_popup_notification_allowed(false);
+            }
+            $PAGE->set_url($myurl);
+            $PAGE->set_heading($SITE->fullname);
+            $PAGE->set_title(get_string('policiesagreements', 'tool_policy'));
+            $PAGE->navbar->add(get_string('policiesagreements', 'tool_policy'), new moodle_url('/admin/tool/policy/index.php'));
+            $PAGE->navbar->add(format_string($this->policy->name));
+        }
+
+        if (!api::can_user_view_policy_version($this->policy, $this->behalfid)) {
+            throw new moodle_exception('accessdenied', 'tool_policy');
+        }
+    }
+
+    /**
+     * Export the page data for the mustache template.
+     *
+     * @param renderer_base $output renderer to be used to render the page elements.
+     * @return stdClass
+     */
+    public function export_for_template(renderer_base $output) {
+
+        $data = (object) [
+            'pluginbaseurl' => (new moodle_url('/admin/tool/policy'))->out(false),
+            'returnurl' => $this->returnurl ? (new moodle_url($this->returnurl))->out(false) : null,
+            'editurl' => ($this->manage && $this->policy->status != policy_version::STATUS_ARCHIVED) ? (new moodle_url('/admin/tool/policy/editpolicydoc.php',
+                ['policyid' => $this->policy->policyid, 'versionid' => $this->policy->id]))->out(false) : null,
+            'numpolicy' => $this->numpolicy ? : null,
+            'totalpolicies' => $this->totalpolicies ? : null,
+        ];
+
+        $data->policy = clone($this->policy);
+
+        return $data;
+    }
+}
diff --git a/admin/tool/policy/classes/policy_version.php b/admin/tool/policy/classes/policy_version.php
new file mode 100644 (file)
index 0000000..b3bbd29
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Provides the {@link tool_policy\policy_version} persistent.
+ *
+ * @package    tool_policy
+ * @copyright  2018 Sara Arjona (sara@moodle.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_policy;
+
+defined('MOODLE_INTERNAL') || die();
+
+use core\persistent;
+
+/**
+ * Persistent model representing a single policy document version.
+ *
+ * @copyright  2018 Sara Arjona (sara@moodle.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class policy_version extends persistent {
+
+    /** @var string Table name this persistent is mapped to. */
+    const TABLE = 'tool_policy_versions';
+
+    /** @var int Site policy document. */
+    const TYPE_SITE = 0;
+
+    /** @var int Privacy policy document. */
+    const TYPE_PRIVACY = 1;
+
+    /** @var int Third party policy document. */
+    const TYPE_THIRD_PARTY = 2;
+
+    /** @var int Other policy document. */
+    const TYPE_OTHER = 99;
+
+    /** @var int Policy applies to all users. */
+    const AUDIENCE_ALL = 0;
+
+    /** @var int Policy applies to logged in users only. */
+    const AUDIENCE_LOGGEDIN = 1;
+
+    /** @var int Policy applies to guests only. */
+    const AUDIENCE_GUESTS = 2;
+
+    /** @var int Policy version is a draft. */
+    const STATUS_DRAFT = 0;
+
+    /** @var int Policy version is the active one. */
+    const STATUS_ACTIVE = 1;
+
+    /** @var int Policy version has been archived. */
+    const STATUS_ARCHIVED = 2;
+
+    /**
+     * Return the definition of the properties of this model.
+     *
+     * @return array
+     */
+    protected static function define_properties() {
+        return [
+            'name' => [
+                'type' => PARAM_TEXT,
+                'default' => '',
+            ],
+            'type' => [
+                'type' => PARAM_INT,
+                'choices' => [
+                    self::TYPE_SITE,
+                    self::TYPE_PRIVACY,
+                    self::TYPE_THIRD_PARTY,
+                    self::TYPE_OTHER,
+                ],
+                'default' => self::TYPE_SITE,
+            ],
+            'audience' => [
+                'type' => PARAM_INT,
+                'choices' => [
+                    self::AUDIENCE_ALL,
+                    self::AUDIENCE_LOGGEDIN,
+                    self::AUDIENCE_GUESTS,
+                ],
+                'default' => self::AUDIENCE_ALL,
+            ],
+            'archived' => [
+                'type' => PARAM_BOOL,
+                'default' => false,
+            ],
+            'policyid' => [
+                'type' => PARAM_INT,
+            ],
+            'revision' => [
+                'type' => PARAM_TEXT,
+                'default' => '',
+            ],
+            'summary' => [
+                'type' => PARAM_RAW,
+                'default' => '',
+            ],
+            'summaryformat' => [
+                'type' => PARAM_INT,
+                'default' => FORMAT_HTML,
+                'choices' => [
+                    FORMAT_PLAIN,
+                    FORMAT_HTML,
+                    FORMAT_MOODLE,
+                    FORMAT_MARKDOWN,
+                ],
+            ],
+            'content' => [
+                'type' => PARAM_RAW,
+                'default' => '',
+            ],
+            'contentformat' => [
+                'type' => PARAM_INT,
+                'default' => FORMAT_HTML,
+                'choices' => [
+                    FORMAT_PLAIN,
+                    FORMAT_HTML,
+                    FORMAT_MOODLE,
+                    FORMAT_MARKDOWN,
+                ],
+            ],
+        ];
+    }
+}
diff --git a/admin/tool/policy/classes/privacy/local/sitepolicy/handler.php b/admin/tool/policy/classes/privacy/local/sitepolicy/handler.php
new file mode 100644 (file)
index 0000000..dabd268
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Site policy handler class.
+ *
+ * @package    tool_policy
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_policy\privacy\local\sitepolicy;
+
+defined('MOODLE_INTERNAL') || die();
+
+use tool_policy\api;
+use tool_policy\policy_version;
+
+/**
+ * Class implementation for a site policy handler.
+ *
+ * @package    tool_policy
+ * @copyright  2018 Sara Arjona <sara@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class handler extends \core_privacy\local\sitepolicy\handler {
+
+    /**
+     * Returns URL to redirect user to when user needs to agree to site policy
+     *
+     * This is a regular interactive page for web users. It should have normal Moodle header/footers, it should
+     * allow user to view policies and accept them.
+     *
+     * @param bool $forguests
+     * @return moodle_url|null (returns null if site policy is not defined)
+     */
+    public static function get_redirect_url($forguests = false) {
+        // There is no redirect for guests, policies are shown in the popup, only return redirect url for the logged in users.
+        if (!$forguests && api::get_current_versions_ids(policy_version::AUDIENCE_LOGGEDIN)) {
+            return new \moodle_url('/admin/tool/policy/index.php');
+        }
+        return null;
+    }
+
+    /**
+     * Returns URL of the site policy that needs to be displayed to the user (inside iframe or to use in WS such as mobile app)
+     *
+     * This page should not have any header/footer, it does not also have any buttons/checkboxes. The caller needs to implement
+     * the "Accept" button and call {@link self::accept()} on completion.
+     *
+     * @param bool $forguests
+     * @return moodle_url|null
+     */
+    public static function get_embed_url($forguests = false) {
+        if (api::get_current_versions_ids($forguests ? policy_version::AUDIENCE_GUESTS : policy_version::AUDIENCE_LOGGEDIN)) {
+            return new \moodle_url('/admin/tool/policy/viewall.php');
+        }
+        return null;
+    }
+
+    /**
+     * Accept site policy for the current user
+     *
+     * @return bool - false if sitepolicy not defined, user is not logged in or user has already agreed to site policy;
+     *     true - if we have successfully marked the user as agreed to the site policy
+     */
+    public static function accept() {
+        global $USER, $DB;
+        if (!isloggedin()) {
+            return false;
+        }
+        if ($USER->policyagreed) {
+            return false;
+        }
+
+        if (!isguestuser()) {
+            // Accepts all policies with a current version for logged users on behalf of the current user.
+            if (!$versions = api::get_current_versions_ids(policy_version::AUDIENCE_LOGGEDIN)) {
+                return false;
+            }
+            api::accept_policies(array_values($versions));
+        }
+
+        if (!isguestuser()) {
+            // For the guests agreement in stored in session only, for other users - in DB.
+            $DB->set_field('user', 'policyagreed', 1, array('id' => $USER->id));
+        }
+        $USER->policyagreed = 1;
+        return true;
+    }
+
+    /**
+     * Adds "Agree to site policy" checkbox to the signup form.
+     *
+     * @param \MoodleQuickForm $mform
+     */
+    public static function signup_form($mform) {
+        if (static::is_defined()) {
+            // This plugin displays policies to the user who is signing up before the signup form is shown.
+            // By the time user has access to signup form they have already agreed to the policies.
+            $mform->addElement('hidden', 'policyagreed', 1);
+            $mform->setType('policyagreed', PARAM_INT);
+        }
+    }
+}
diff --git a/admin/tool/policy/db/services.php b/admin/tool/policy/db/services.php
new file mode 100644 (file)
index 0000000..cc3c1a0
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Tool policy external functions and service definitions.
+ *
+ * @package    tool_policy
+ * @category   external
+ * @copyright  2018 Sara Arjona (sara@moodle.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+$functions = [
+    'tool_policy_get_policy_version' => [
+        'classname'     => 'tool_policy\external',
+        'methodname'    => 'get_policy_version',
+        'classpath'     => '',
+        'description'   => 'Fetch the details of a policy version',
+        'type'          => 'read',
+        'capabilities'  => '',
+        'ajax'          => true,
+        'loginrequired' => false,
+    ],
+
+    'tool_policy_submit_accept_on_behalf' => [
+        'classname'     => 'tool_policy\external',
+        'methodname' => 'submit_accept_on_behalf',
+        'classpath' => '',
+        'description' => 'Accept policies on behalf of other users',
+        'ajax' => true,
+        'type' => 'write',
+    ],
+];
diff --git a/admin/tool/policy/index.php b/admin/tool/policy/index.php
new file mode 100644 (file)
index 0000000..d53c0c5
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Show a user the policy documents to be agreed to.
+ *
+ * Script parameters:
+ *  agreedoc=<array> Policy version id which have been accepted by the user.
+ *  behalfid=<id> The user id to view the policy version as (such as child's id).
+ *
+ * @package     tool_policy
+ * @copyright   2018 Sara Arjona (sara@moodle.com)
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+use tool_policy\api;
+use tool_policy\output\page_agreedocs;
+
+// Do not check for the site policies in require_login() to avoid the redirect loop.
+define('NO_SITEPOLICY_CHECK', true);
+
+// @codingStandardsIgnoreLine See the {@link page_agreedocs} for the access control checks.
+require(__DIR__.'/../../../config.php');
+
+$action = optional_param('action', null, PARAM_ALPHA);
+$agreedocs = optional_param_array('agreedoc', null, PARAM_INT);
+$behalfid = optional_param('userid', null, PARAM_INT);
+
+$PAGE->set_context(context_system::instance());
+$PAGE->set_url('/admin/tool/policy/index.php');
+$PAGE->set_popup_notification_allowed(false);
+
+$haspermissionagreedocs = false;
+if (!empty($USER->id)) {
+    // Existing user.
+    if (empty($behalfid) || $behalfid == $USER->id) {
+        $haspermissionagreedocs = has_capability('tool/policy:accept', context_system::instance());
+    } else {
+        $usercontext = \context_user::instance($behalfid);
+        $haspermissionagreedocs = has_capability('tool/policy:acceptbehalf', $usercontext);
+    }
+} else {
+    // New user.
+    $haspermissionagreedocs = true;
+}
+
+if (!$haspermissionagreedocs) {
+    $outputpage = new \tool_policy\output\page_nopermission($behalfid);
+} else {
+    $outputpage = new \tool_policy\output\page_agreedocs($agreedocs, $behalfid, $action);
+}
+
+$output = $PAGE->get_renderer('tool_policy');
+
+echo $output->header();
+echo $output->render($outputpage);
+echo $output->footer();
diff --git a/admin/tool/policy/styles.css b/admin/tool/policy/styles.css
new file mode 100644 (file)
index 0000000..d6da25f
--- /dev/null
@@ -0,0 +1,92 @@
+/* eupopup styles */
+.eupopup-body li {
+    display: inline;
+}
+
+.eupopup-body li:before {
+    content: ", ";
+}
+
+.eupopup-body li:first-child:before {
+    content: "";
+}
+
+.eupopup-container {
+    background-color: rgba(25, 25, 25, 0.9);
+    color: #efefef;
+    padding: 5px 20px;
+    font-size: 12px;
+    line-height: 1.2em;
+    text-align: center;
+    display: none;
+    z-index: 9999999;
+}
+
+.eupopup-container-bottom {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+.eupopup-closebutton {
+    font-size: 16px;
+    font-weight: 100;
+    line-height: 1;
+    color: #a2a2a2;
+    filter: alpha(opacity=20);
+    position: absolute;
+    font-family: helvetica, arial, verdana, sans-serif;
+    top: 0;
+    right: 0;
+    padding: 5px 10px;
+}
+
+.eupopup-closebutton:hover,
+.eupopup-closebutton:active {
+    text-decoration: none;
+}
+
+.eupopup-head {
+    font-size: 1.2em;
+    font-weight: bold;
+    padding: 7px;
+}
+
+.eupopup-body ul {
+    padding: 0;
+    margin: 0 0 3px;
+}
+
+.eupopup-buttons {
+    padding: 7px 0 5px 0;
+}
+
+.eupopup-button_1 {
+    font-weight: bold;
+    font-size: 14px;
+}
+
+.eupopup-button_2 {
+    display: none;
+}
+
+.eupopup-button {
+    margin: 0 10px;
+    color: #f6a21d;
+}
+
+.eupopup-button:hover,
+.eupopup-button:focus {
+    text-decoration: underline;
+    color: #f6a21d;
+}
+
+.policy-heading .policy-viewdoc-buttons {
+    text-align: center;
+    margin: 15px;
+}
+
+#page-admin-tool-policy-acceptances .colselect {
+    max-width: 15px;
+}
diff --git a/admin/tool/policy/templates/guestconsent.mustache b/admin/tool/policy/templates/guestconsent.mustache
new file mode 100644 (file)
index 0000000..872757e
--- /dev/null
@@ -0,0 +1,88 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template tool_policy/guestconsent
+
+    Template for the guest consent message.
+
+    Classes required for JS:
+    * eupopup
+    * policyactions
+
+    Data attributes required for JS:
+    -
+
+    Context variables required for this template:
+    * pluginbaseurl
+    * returnurl - urlencoded URL to return to
+    * haspolicies - true if there are guest policies; false otherwise
+    * policies - array of policy documents
+
+    Example context (json):
+    {
+        "pluginbaseurl": "https://example.com",
+        "returnurl": "/",
+        "haspolicies": true,
+        "policies": [
+            {
+                "id": 1,
+                "name": "Terms &amp; conditions"
+            }
+        ]
+    }
+}}
+
+{{#js}}
+
+require(['jquery', 'tool_policy/jquery-eu-cookie-law-popup', 'tool_policy/policyactions'], function($, Popup, ActionsMod) {
+        // Initialise the guest popup.
+        $(document).ready(function() {
+            // Only show message if there is some policy related to guests.
+            {{#haspolicies}}
+                // Get localised messages.
+                var textmessage = "{{# str }} guestconsentmessage, tool_policy {{/ str }}" +
+                   "<ul>{{#policies}}" +
+                   "<li>" +
+                   "<a href=\"{{pluginbaseurl}}/view.php?versionid={{id}}{{#returnurl}}&amp;returnurl={{.}}{{/returnurl}}\" " +
+                   "   data-action=\"view\" data-versionid=\"{{id}}\" data-behalfid=\"1\" >" +
+                   "{{{name}}}" +
+                   "</a>" +
+                   "</li>" +
+                   "{{/policies}}" +
+                   "</ul>";
+                var continuemessage = "{{# str }} guestconsent:continue, tool_policy {{/ str }}";
+
+                // Initialize popup.
+                $(document.body).addClass('eupopup');
+                if ($(".eupopup").length > 0) {
+                    $(document).euCookieLawPopup().init({
+                        popupPosition: 'bottom',
+                        popupTitle: '',
+                        popupText: textmessage,
+                        buttonContinueTitle: continuemessage,
+                        buttonLearnmoreTitle: '',
+                        compactStyle: true,
+                    });
+                }
+            {{/haspolicies}}
+
+            // Initialise the JS for the modal window which displays the policy versions.
+            ActionsMod.init();
+        });
+});
+
+{{/js}}
diff --git a/admin/tool/policy/templates/page_agreedocs.mustache b/admin/tool/policy/templates/page_agreedocs.mustache
new file mode 100644 (file)
index 0000000..029b433
--- /dev/null
@@ -0,0 +1,128 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template tool_policy/page_agreedocs
+
+    Template for showing to the user the policy docs to agree.
+
+    Classes required for JS:
+    * policyactions
+
+    Data attributes required for JS:
+    -
+
+    Context variables required for this template:
+    * pluginbaseurl
+    * myurl
+    * sesskey
+    * policies - policy array
+    * behalfuser - If behalfid is defined and valid, full name of the behalf user with a link to his/her profile; null otherwise
+
+
+    Example context (json):
+    {
+        "myurl": "/admin/tool/policy/index.php",
+        "sesskey": "123456",
+        "behalfuser": "Sam Student",
+        "policies": [
+            {
+                "id": 1,
+                "name": "Terms &amp; conditions",
+                "policymodal": "<a href=\"#\">Terms &amp; conditions</a>",
+                "summary": "Policy <u>summary</u>",
+                "versionagreed": false,
+                "versionlangsagreed": "Agreed",
+                "versionbehalfsagreed": ""
+            }
+        ]
+    }
+
+}}
+
+{{#messages}}{{{.}}}{{/messages}}
+
+<form id="agreedocsform" method="post" action="{{myurl}}">
+    <input type="hidden" name="sesskey" value="{{sesskey}}">
+    <input type="hidden" name="action" value="agreedocs">
+
+{{#behalfuser}}
+<div class="clearfix">
+    <div class="pull-right m-b-2">
+        {{# str }} viewconsentpageforuser, tool_policy, {{{ . }}} {{/ str }}
+    </div>
+</div>
+{{/behalfuser}}
+
+<div class="clearfix">
+    <div class="pull-left">
+        <h2>{{# str }}consentpagetitle, tool_policy{{/ str }}</h2>
+    </div>
+</div>
+
+<div class="clearfix m-t-2">
+    <h3>{{# str }}agreepolicies, tool_policy {{/ str }}</h3>
+</div>
+<hr>
+
+
+{{#policies}}
+
+<div class="agreedoc-policy clearfix m-t-2 m-b-1">
+    <h3>{{{name}}}</h3>
+    <div class="agreedoc-content">
+        <div class="agreedoc-summary m-b-2">
+          {{{summary}}}
+        </div>
+        <div class="agreedoc-msg">
+            {{# str }}refertofullpolicytext, tool_policy, {{{policymodal}}} {{/ str }}
+        </div>
+        <div class="agreedoc-form m-t-1">
+            <div class="agreedoc-checkbox">
+                <label>
+                    <input value="{{id}}" name="agreedoc[]" {{#versionagreed}}checked="{{.}}"{{/versionagreed}} type="checkbox">
+                    <strong>{{# str }}iagree, tool_policy, {{{name}}} {{/ str }}</strong>
+                    <i class="icon fa fa-exclamation-circle text-danger fa-fw" title="{{# str }} required {{/ str }}" ></i>
+                </label>
+            </div>
+            <ul class="agreedoc-msg list-unstyled">
+                    {{#versionlangsagreed}}
+                        <li><small>{{{.}}}</small></li>
+                    {{/versionlangsagreed}}
+                    {{#versionbehalfsagreed}}
+                        <li><small>{{{.}}}</small></li>
+                    {{/versionbehalfsagreed}}
+            </ul>
+        </div>
+    </div>
+</div>
+
+{{/policies}}
+
+{{# str }}somefieldsrequired, form, <i class="icon fa fa-exclamation-circle text-danger fa-fw" title="{{# str }} required {{/ str }}" ></i>{{/ str }}
+<hr>
+
+<input type="submit" class="btn btn-primary" value={{#quote}}{{#str}} next {{/str}}{{/quote}}>
+<input type="reset" class="btn btn-secondary" value={{#quote}}{{#str}} cancel {{/str}}{{/quote}}>
+
+</form>
+
+{{#js}}
+// Initialise the JS for the modal window which displays the policy versions.
+require(['tool_policy/policyactions'], function(ActionsMod) {
+    ActionsMod.init();
+});
+{{/js}}
diff --git a/admin/tool/policy/templates/page_nopermission.mustache b/admin/tool/policy/templates/page_nopermission.mustache
new file mode 100644 (file)
index 0000000..f01feaa
--- /dev/null
@@ -0,0 +1,78 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template tool_policy/page_nopermission
+
+    Template for showing to the user the policy docs to agree.
+
+    Classes required for JS:
+    * policyactions
+
+    Data attributes required for JS:
+    -
+
+    Context variables required for this template:
+    * pluginbaseurl
+    * haspermissionagreedocs - status of the capability of the user to accept policy documents
+    * messagetitle - Message title to display, with the error.
+    * messagedesc - Message description to display, with the error.
+    * supportname - name of the support contact; displayed when the user does not have permission to accept policy documents
+    * supportemail - email of the support contact; displayed when the user does not have permission to accept policy documents
+    * behalfuser - If behalfid is defined and valid, full name of the behalf user with a link to his/her profile; null otherwise
+    * policies - Array with all the links to current policies (for showing them if needed).
+
+    Example context (json):
+    {
+        "haspermissionagreedocs": false,
+        "messagetitle": "You don't have permission",
+        "messagedesc": "Sorry you don't have permission to accept",
+        "policies": [
+            "Policy 1",
+            "Policy 2"
+        ],
+        "supportname": "Admin",
+        "supportemail": "admin@yoursite.com"
+    }
+}}
+
+{{^haspermissionagreedocs}}
+    <h3>{{{messagetitle}}}</h3>
+    <p class="m-t-1 m-b-1">{{{messagedesc}}}</p>
+
+    <div class="policies">
+        <ul>
+        {{# policies }}
+            <li>{{{.}}}</li>
+        {{/ policies }}
+        </ul>
+    </div>
+
+    <p class="m-t-3">{{#str}}nopermissiontoagreedocscontact, tool_policy{{/str}}</p>
+    <div class="agreedoc-officer well">
+        <div>
+            <p class="m-b-0">{{{supportname}}}</p>
+            <p class="m-b-0"><a href="mailto:{{supportemail}}">{{supportemail}}</a></p>
+        </div>
+    </div>
+{{/haspermissionagreedocs}}
+
+{{#js}}
+// Initialise the JS for the modal window which displays the policy versions.
+require(['tool_policy/policyactions'], function(ActionsMod) {
+    ActionsMod.init();
+});
+{{/js}}
diff --git a/admin/tool/policy/templates/page_viewalldoc.mustache b/admin/tool/policy/templates/page_viewalldoc.mustache
new file mode 100644 (file)
index 0000000..df70f1a
--- /dev/null
@@ -0,0 +1,62 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template tool_policy/page_viewalldoc
+
+    Template for viewing all policy versions, one under another.
+
+    Classes required for JS:
+    -
+
+    Data attributes required for JS:
+    -
+
+    Context variables required for this template:
+    * policies - policy array
+
+    Example context (json):
+    {
+        "policies": [
+            {
+                "name": "Terms &amp; conditions",
+                "summary": "Policy <u>summary</u>",
+                "content": "Policy <em>content</em>"
+            },
+            {
+                "name": "Privacy",
+                "summary": "We keep your information private",
+                "content": "Very private"
+            }
+        ]
+    }
+}}
+
+{{#policies }}
+    <div class="policy_version m-b-3">
+        <div class="clearfix m-t-2">
+            <h1>{{{name}}}</h1>
+        </div>
+        <div class="policy_document_summary clearfix m-b-1">
+            {{{summary}}}
+        </div>
+        <div class="policy_document_content m-t-2">
+            {{{content}}}
+        </div>
+        <hr>
+    </div>
+
+{{/policies }}
diff --git a/admin/tool/policy/templates/page_viewdoc.mustache b/admin/tool/policy/templates/page_viewdoc.mustache
new file mode 100644 (file)
index 0000000..8eb290a
--- /dev/null
@@ -0,0 +1,98 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template tool_policy/page_viewdoc
+
+    Template for viewing a policy version document.
+
+    Classes required for JS:
+    -
+
+    Data attributes required for JS:
+    -
+
+    Context variables required for this template:
+    * policy - version document object.
+
+    Example context (json):
+    {
+        "numpolicy": 1,
+        "totalpolicies": 2,
+        "policy": {
+            "name": "Terms &amp; conditions",
+            "summary": "Policy <u>summary</u>",
+            "content": "Policy <em>content</em>"
+        },
+        "returnurl": "#",
+        "editurl": "#"
+    }
+}}
+
+<a id="top"></a>
+<div class="clearfix">
+    <div class="pull-left">
+        <h2>{{{policy.name}}}</h2>
+    </div>
+    {{# numpolicy }}
+        <div class="pull-right">
+            {{# str }} steppolicies, tool_policy,
+                { "numpolicy": {{# quote }}{{numpolicy}}{{/quote }}, "totalpolicies": {{# quote }}{{totalpolicies}}{{/ quote }} }
+            {{/ str }}
+        </div>
+    {{/ numpolicy }}
+</div>
+
+
+{{# numpolicy }}
+    <div class="clearfix m-t-2">
+        <h3>{{# str }}readpolicy, tool_policy, {{{policy.name}}} {{/ str }}</h3>
+    </div>
+{{/ numpolicy }}
+
+<hr>
+
+<div class="policy_document m-t-1">
+    <div class="policy_document_summary clearfix m-b-1">
+        {{{policy.summary}}}
+    </div>
+    <div class="policy_document_content m-t-2">
+        {{{policy.content}}}
+    </div>
+</div>
+
+<hr>
+
+<div class="policy_buttons">
+    {{#returnurl}}
+        {{# numpolicy }}
+            <a role="button" href="{{returnurl}}" class="btn btn-primary">{{#str}} next {{/str}}</a>
+        {{/ numpolicy }}
+        {{^ numpolicy }}
+            <a role="button" href="{{returnurl}}" class="btn btn-primary">{{#str}} back {{/str}}</a>
+        {{/ numpolicy }}
+    {{/returnurl}}
+    {{#editurl}}
+        <a role="button" href="{{editurl}}" class="btn">{{#str}} edit {{/str}}</a>
+    {{/editurl}}
+
+    <div class="pull-right">
+        <a href="#top">
+            {{# str }} backtotop, tool_policy {{/ str }}
+            <i class="icon text-primary fa fa-caret-up" title="{{# str }} backtotop, tool_policy {{/ str }}" ></i>
+        </a>
+    </div>
+</div>
diff --git a/admin/tool/policy/tests/externallib_test.php b/admin/tool/policy/tests/externallib_test.php
new file mode 100644 (file)
index 0000000..8abff00
--- /dev/null
@@ -0,0 +1,232 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * External policy webservice API tests.
+ *
+ * @package tool_policy
+ * @copyright 2018 Sara Arjona <sara@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->libdir . '/externallib.php');
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+require_once($CFG->dirroot . '/user/externallib.php');
+
+use tool_policy\api;
+use tool_policy\external;
+use tool_mobile\external as external_mobile;
+
+/**
+ * External policy webservice API tests.
+ *
+ * @package tool_policy
+ * @copyright 2018 Sara Arjona <sara@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_policy_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Setup function- we will create some policy docs.
+     */
+    public function setUp() {
+        $this->resetAfterTest(true);
+        $this->setAdminUser();
+
+        // Prepare a policy document with some versions.
+        $formdata = api::form_policydoc_data(new \tool_policy\policy_version(0));
+        $formdata->name = 'Test policy';
+        $formdata->revision = 'v1';
+        $formdata->summary_editor = ['text' => 'summary', 'format' => FORMAT_HTML, 'itemid' => 0];
+        $formdata->content_editor = ['text' => 'content', 'format' => FORMAT_HTML, 'itemid' => 0];
+        $this->policy1 = api::form_policydoc_add($formdata);
+
+        $formdata = api::form_policydoc_data($this->policy1);
+        $formdata->revision = 'v2';
+        $this->policy2 = api::form_policydoc_update_new($formdata);
+
+        $formdata = api::form_policydoc_data($this->policy1);
+        $formdata->revision = 'v3';
+        $this->policy3 = api::form_policydoc_update_new($formdata);
+
+        api::make_current($this->policy2->get('id'));
+
+        // Create users.
+        $this->child = $this->getDataGenerator()->create_user();
+        $this->parent = $this->getDataGenerator()->create_user();
+        $this->adult = $this->getDataGenerator()->create_user();
+
+        $syscontext = context_system::instance();
+        $childcontext = context_user::instance($this->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');
+
+        assign_capability('tool/policy:accept', CAP_PROHIBIT, $roleminorid, $syscontext->id);
+        assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $syscontext->id);
+
+        role_assign($roleminorid, $this->child->id, $syscontext->id);
+        role_assign($roleparentid, $this->parent->id, $childcontext->id);
+    }
+
+    /**
+     * Test for the get_policy_version() function.
+     */
+    public function test_get_policy_version() {
+        $this->setUser($this->adult);
+
+        // View current policy version.
+        $result = external::get_policy_version($this->policy2->get('id'));
+        $result = external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
+        $this->assertCount(1, $result['result']);
+        $this->assertEquals($this->policy1->get('name'), $result['result']['policy']['name']);
+        $this->assertEquals($this->policy1->get('content'), $result['result']['policy']['content']);
+
+        // View draft policy version.
+        $result = external::get_policy_version($this->policy3->get('id'));
+        $result = external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
+        $this->assertCount(0, $result['result']);
+        $this->assertCount(1, $result['warnings']);
+        $this->assertEquals(array_pop($result['warnings'])['warningcode'], 'errorusercantviewpolicyversion');
+
+        // Add test for non existing versionid.
+        $result = external::get_policy_version(999);
+        $result = external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
+        $this->assertCount(0, $result['result']);
+        $this->assertCount(1, $result['warnings']);
+        $this->assertEquals(array_pop($result['warnings'])['warningcode'], 'errorpolicyversionnotfound');
+
+        // View previous non-accepted version in behalf of a child.
+        $this->setUser($this->parent);
+        $result = external::get_policy_version($this->policy1->get('id'), $this->child->id);
+        $result = external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
+        $this->assertCount(0, $result['result']);
+        $this->assertCount(1, $result['warnings']);
+        $this->assertEquals(array_pop($result['warnings'])['warningcode'], 'errorusercantviewpolicyversion');
+
+        // Let the parent accept the policy on behalf of her child and view it again.
+        api::accept_policies($this->policy1->get('id'), $this->child->id);
+        $result = external::get_policy_version($this->policy1->get('id'), $this->child->id);
+        $result = external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
+        $this->assertCount(1, $result['result']);
+        $this->assertEquals($this->policy1->get('name'), $result['result']['policy']['name']);
+        $this->assertEquals($this->policy1->get('content'), $result['result']['policy']['content']);
+
+        // Only parent is able to view the child policy version accepted by her child.
+        $this->setUser($this->adult);
+        $result = external::get_policy_version($this->policy1->get('id'), $this->child->id);
+        $result = external_api::clean_returnvalue(external::get_policy_version_returns(), $result);
+        $this->assertCount(0, $result['result']);
+        $this->assertCount(1, $result['warnings']);
+        $this->assertEquals(array_pop($result['warnings'])['warningcode'], 'errorusercantviewpolicyversion');
+    }
+
+    /**
+     * Test tool_mobile\external callback to site_policy_handler.
+     */
+    public function test_get_config_with_site_policy_handler() {
+        global $CFG;
+
+        $this->resetAfterTest(true);
+
+        // Set the handler for the site policy, make sure it substitutes link to the sitepolicy.
+        $CFG->sitepolicyhandler = 'tool_policy';
+        $sitepolicymanager = new core_privacy\local\sitepolicy\manager();
+        $result = external_mobile::get_config();
+        $result = external_api::clean_returnvalue(external_mobile::get_config_returns(), $result);
+        $toolsitepolicy = $sitepolicymanager->get_embed_url();
+        foreach (array_values($result['settings']) as $r) {
+            if ($r['name'] == 'sitepolicy') {
+                $configsitepolicy = $r['value'];
+            }
+        }
+        $this->assertEquals($toolsitepolicy, $configsitepolicy);
+    }
+
+    /**
+     * Test for core_privacy\sitepolicy\manager::accept() when site policy handler is set.
+     */
+    public function test_agree_site_policy_with_handler() {
+        global $CFG, $DB, $USER;
+
+        $this->resetAfterTest(true);
+        $user = self::getDataGenerator()->create_user();
+        $this->setUser($user);
+
+        // Set mock site policy handler. See function tool_phpunit_site_policy_handler() below.
+        $CFG->sitepolicyhandler = 'tool_policy';
+        $this->assertEquals(0, $USER->policyagreed);
+        $sitepolicymanager = new core_privacy\local\sitepolicy\manager();
+
+        // Make sure user can not login.
+        $toolconsentpage = $sitepolicymanager->get_redirect_url();
+        $this->expectException('moodle_exception');
+        $this->expectExceptionMessage(get_string('sitepolicynotagreed', 'error', $toolconsentpage->out()));
+        core_user_external::validate_context(context_system::instance());
+
+        // Call WS to agree to the site policy. It will call tool_policy handler.
+        $result = core_user_external::agree_site_policy();
+        $result = external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);
+        $this->assertTrue($result['status']);
+        $this->assertCount(0, $result['warnings']);
+        $this->assertEquals(1, $USER->policyagreed);
+        $this->assertEquals(1, $DB->get_field('user', 'policyagreed', array('id' => $USER->id)));
+
+        // Try again, we should get a warning.
+        $result = core_user_external::agree_site_policy();
+        $result = external_api::clean_returnvalue(core_user_external::agree_site_policy_returns(), $result);
+        $this->assertFalse($result['status']);
+        $this->assertCount(1, $result['warnings']);
+        $this->assertEquals('alreadyagreed', $result['warnings'][0]['warningcode']);
+    }
+
+    /**
+     * Test for core_privacy\sitepolicy\manager::accept() when site policy handler is set.
+     */
+    public function test_checkcanaccept_with_handler() {
+        global $CFG;
+
+        $this->resetAfterTest(true);
+        $CFG->sitepolicyhandler = 'tool_policy';
+        $syscontext = context_system::instance();
+        $sitepolicymanager = new core_privacy\local\sitepolicy\manager();
+
+        $adult = $this->getDataGenerator()->create_user();
+
+        $child = $this->getDataGenerator()->create_user();
+        $rolechildid = create_role('Child', 'child', 'Not old enough to accept site policies themselves');
+        assign_capability('tool/policy:accept', CAP_PROHIBIT, $rolechildid, $syscontext->id);
+        role_assign($rolechildid, $child->id, $syscontext->id);
+
+        // Default user can accept policies.
+        $this->setUser($adult);
+        $result = external_mobile::get_config();
+        $result = external_api::clean_returnvalue(external_mobile::get_config_returns(), $result);
+        $toolsitepolicy = $sitepolicymanager->accept();
+        $this->assertTrue($toolsitepolicy);
+
+        // Child user can not accept policies.
+        $this->setUser($child);
+        $result = external_mobile::get_config();
+        $result = external_api::clean_returnvalue(external_mobile::get_config_returns(), $result);
+        $this->expectException('required_capability_exception');
+        $sitepolicymanager->accept();
+    }
+}
diff --git a/admin/tool/policy/thirdpartylibs.xml b/admin/tool/policy/thirdpartylibs.xml
new file mode 100644 (file)
index 0000000..151961b
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<libraries>
+  <library>
+    <location>amd/src/jquery-eu-cookie-law-popup.js</location>
+    <name>jQuery EU Cookie Law popups</name>
+    <license>MIT</license>
+    <version>1.0.1</version>
+    <licenseversion></licenseversion>
+  </library>
+</libraries>
diff --git a/admin/tool/policy/view.php b/admin/tool/policy/view.php
new file mode 100644 (file)
index 0000000..f2b4a77
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * View current document policy version.
+ *
+ * Script parameters:
+ *  versionid=<int> Policy version id, defaults to the current one.
+ *  policyid=<int> Policy document id, defaults to the one matching the version.
+ *  returnurl=<local url> URL to continue to after reading the policy document.
+ *  behalfid=<id> The user id to view the policy version as (such as child's id).
+ *  manage=<bool> View the policy as a part of the management UI (managedocs.php).
+ *
+ * @package     tool_policy
+ * @copyright   2018 Sara Arjona (sara@moodle.com)
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+use tool_policy\api;
+use tool_policy\output\page_viewdoc;
+
+// Do not check for the site policies in require_login() to avoid the redirect loop.
+define('NO_SITEPOLICY_CHECK', true);
+
+// @codingStandardsIgnoreLine See the {@link page_viewdoc} for the access control checks.
+require(__DIR__.'/../../../config.php');
+
+$versionid = optional_param('versionid', null, PARAM_INT);
+$policyid = $versionid ? optional_param('policyid', null, PARAM_INT) : required_param('policyid', PARAM_INT);
+$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
+$behalfid = optional_param('behalfid', null, PARAM_INT);
+$manage = optional_param('manage', false, PARAM_BOOL);
+$numpolicy = optional_param('numpolicy', null, PARAM_INT);
+$totalpolicies = optional_param('totalpolicies', null, PARAM_INT);
+
+$PAGE->set_context(context_system::instance());
+$PAGE->set_pagelayout('standard');
+
+$viewpage = new page_viewdoc($policyid, $versionid, $returnurl, $behalfid, $manage, $numpolicy, $totalpolicies);
+
+$output = $PAGE->get_renderer('tool_policy');
+
+echo $output->header();
+echo $output->render($viewpage);
+echo $output->footer();
diff --git a/admin/tool/policy/viewall.php b/admin/tool/policy/viewall.php
new file mode 100644 (file)
index 0000000..9f5528c
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * View all document policy with a version, one under another.
+ *
+ * Script parameters:
+ *  -
+ *
+ * @package     tool_policy
+ * @copyright   2018 Sara Arjona (sara@moodle.com)
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+use tool_policy\api;
+use tool_policy\output\page_viewalldoc;
+
+// Do not check for the site policies in require_login() to avoid the redirect loop.
+define('NO_SITEPOLICY_CHECK', true);
+
+// @codingStandardsIgnoreLine See the {@link page_viewalldoc} for the access control checks.
+require(__DIR__.'/../../../config.php');
+
+$viewallpage = new page_viewalldoc();
+
+$output = $PAGE->get_renderer('tool_policy');
+
+echo $output->header();
+echo $output->render($viewallpage);
+echo $output->footer();