Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

<?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/>.

namespace mod_questionnaire\privacy;

use \core_privacy\local\metadata\collection;
use \core_privacy\local\request\contextlist;
use \core_privacy\local\request\userlist;
use \core_privacy\local\request\approved_contextlist;
use \core_privacy\local\request\approved_userlist;

/**
 * Contains class mod_questionnaire\privacy\provider
 *
 * @package    mod_questionnaire
 * @copyright  2018 onward Mike Churchward (mike.churchward@poetopensource.org)
 * @author     Mike Churchward
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class provider implements
    // This plugin has data.
    \core_privacy\local\metadata\provider,

    // This plugin is capable of determining which users have data within it.
    \core_privacy\local\request\core_userlist_provider,

    // This plugin currently implements the original plugin_provider interface.
    \core_privacy\local\request\plugin\provider {

    /**
     * Returns meta data about this system.
     *
     * @param   collection $collection The collection to add metadata to.
     * @return  collection  The array of metadata
     */
    public static function get_metadata(collection $collection): collection {

        // Add all of the relevant tables and fields to the collection.
        $collection->add_database_table('questionnaire_response', [
            'userid' => 'privacy:metadata:questionnaire_response:userid',
            'questionnaireid' => 'privacy:metadata:questionnaire_response:questionnaireid',
            'complete' => 'privacy:metadata:questionnaire_response:complete',
            'grade' => 'privacy:metadata:questionnaire_response:grade',
            'submitted' => 'privacy:metadata:questionnaire_response:submitted',
        ], 'privacy:metadata:questionnaire_response');

        $collection->add_database_table('questionnaire_response_bool', [
            'response_id' => 'privacy:metadata:questionnaire_response_bool:response_id',
            'question_id' => 'privacy:metadata:questionnaire_response_bool:question_id',
            'choice_id' => 'privacy:metadata:questionnaire_response_bool:choice_id',
        ], 'privacy:metadata:questionnaire_response_bool');

        $collection->add_database_table('questionnaire_response_date', [
            'response_id' => 'privacy:metadata:questionnaire_response_date:response_id',
            'question_id' => 'privacy:metadata:questionnaire_response_date:question_id',
            'response' => 'privacy:metadata:questionnaire_response_date:response',
        ], 'privacy:metadata:questionnaire_response_date');

        $collection->add_database_table('questionnaire_response_other', [
            'response_id' => 'privacy:metadata:questionnaire_response_other:response_id',
            'question_id' => 'privacy:metadata:questionnaire_response_other:question_id',
            'choice_id' => 'privacy:metadata:questionnaire_response_other:choice_id',
            'response' => 'privacy:metadata:questionnaire_response_other:response',
        ], 'privacy:metadata:questionnaire_response_other');

        $collection->add_database_table('questionnaire_response_rank', [
            'response_id' => 'privacy:metadata:questionnaire_response_rank:response_id',
            'question_id' => 'privacy:metadata:questionnaire_response_rank:question_id',
            'choice_id' => 'privacy:metadata:questionnaire_response_rank:choice_id',
            'rank' => 'privacy:metadata:questionnaire_response_rank:rankvalue',
        ], 'privacy:metadata:questionnaire_response_rank');

        $collection->add_database_table('questionnaire_response_text', [
            'response_id' => 'privacy:metadata:questionnaire_response_text:response_id',
            'question_id' => 'privacy:metadata:questionnaire_response_text:question_id',
            'response' => 'privacy:metadata:questionnaire_response_text:response',
        ], 'privacy:metadata:questionnaire_response_text');

        $collection->add_database_table('questionnaire_resp_multiple', [
            'response_id' => 'privacy:metadata:questionnaire_resp_multiple:response_id',
            'question_id' => 'privacy:metadata:questionnaire_resp_multiple:question_id',
            'choice_id' => 'privacy:metadata:questionnaire_resp_multiple:choice_id',
        ], 'privacy:metadata:questionnaire_resp_multiple');

        $collection->add_database_table('questionnaire_resp_single', [
            'response_id' => 'privacy:metadata:questionnaire_resp_single:response_id',
            'question_id' => 'privacy:metadata:questionnaire_resp_single:question_id',
            'choice_id' => 'privacy:metadata:questionnaire_resp_single:choice_id',
        ], 'privacy:metadata:questionnaire_resp_single');

        return $collection;
    }

    /**
     * Get the list of contexts that contain user information for the specified user.
     *
     * @param   int $userid The user to search.
     * @return  contextlist   $contextlist  The list of contexts used in this plugin.
     */
    public static function get_contexts_for_userid(int $userid): contextlist {
        $contextlist = new contextlist();

        $sql = "SELECT c.id
             FROM {context} c
       INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
       INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
       INNER JOIN {questionnaire} q ON q.id = cm.instance
       INNER JOIN {questionnaire_response} qr ON qr.questionnaireid = q.id
            WHERE qr.userid = :attemptuserid
       ";

        $params = [
            'modname' => 'questionnaire',
            'contextlevel' => CONTEXT_MODULE,
            'attemptuserid' => $userid,
        ];

        $contextlist->add_from_sql($sql, $params);

        return $contextlist;
    }

    /**
     * Get the list of users who have data within a context.
     *
     * @param \core_privacy\local\request\userlist $userlist The userlist containing the list of users who have data in this
     * context/plugin combination.
     */
    public static function get_users_in_context(userlist $userlist) {

        $context = $userlist->get_context();
        if (!$context instanceof \context_module) {
            return;
        }

        $params = ['modulename' => 'questionnaire', 'instanceid' => $context->instanceid];

        // Questionnaire respondents.
        $sql = "SELECT qr.userid
              FROM {course_modules} cm
              JOIN {modules} m ON m.id = cm.module AND m.name = :modulename
              JOIN {questionnaire} q ON q.id = cm.instance
              JOIN {questionnaire_response} qr ON qr.questionnaireid = q.id
             WHERE cm.id = :instanceid";
        $userlist->add_from_sql('userid', $sql, $params);
    }

    /**
     * Export all user data for the specified user, in the specified contexts, using the supplied exporter instance.
     *
     * @param   approved_contextlist $contextlist The approved contexts to export information for.
     */
    public static function export_user_data(approved_contextlist $contextlist) {
        global $DB, $CFG;
        require_once($CFG->dirroot . '/mod/questionnaire/questionnaire.class.php');

        if (empty($contextlist->count())) {
            return;
        }

        $user = $contextlist->get_user();

        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);

        $sql = "SELECT cm.id AS cmid,
                   q.id AS qid, q.course AS qcourse,
                   qr.id AS responseid, qr.submitted AS lastsaved, qr.complete AS complete
              FROM {context} c
        INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
        INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
        INNER JOIN {questionnaire} q ON q.id = cm.instance
        INNER JOIN {questionnaire_response} qr ON qr.questionnaireid = q.id
             WHERE c.id {$contextsql}
                   AND qr.userid = :userid
          ORDER BY cm.id, qr.id ASC";

        $params = ['modname' => 'questionnaire', 'contextlevel' => CONTEXT_MODULE, 'userid' => $user->id] + $contextparams;

        // There can be more than one attempt per instance, so we'll gather them by cmid.
        $lastcmid = 0;
        $responsedata = [];
        $responses = $DB->get_recordset_sql($sql, $params);
        foreach ($responses as $response) {
            // If we've moved to a new choice, then write the last choice data and reinit the choice data array.
            if ($lastcmid != $response->cmid) {
                if (!empty($responsedata)) {
                    $context = \context_module::instance($lastcmid);
                    // Fetch the generic module data for the questionnaire.
                    $contextdata = \core_privacy\local\request\helper::get_context_data($context, $user);
                    // Merge with attempt data and write it.
                    $contextdata = (object)array_merge((array)$contextdata, $responsedata);
                    \core_privacy\local\request\writer::with_context($context)->export_data([], $contextdata);
                }
                $responsedata = [];
                $lastcmid = $response->cmid;
                $course = $DB->get_record("course", ["id" => $response->qcourse]);
                $cm = get_coursemodule_from_instance("questionnaire", $response->qid, $course->id);
                $questionnaire = new \questionnaire($course, $cm, $response->qid, null);
            }
            $responsedata['responses'][] = [
                'complete' => (($response->complete == 'y') ? get_string('yes') : get_string('no')),
                'lastsaved' => \core_privacy\local\request\transform::datetime($response->lastsaved),
                'questions' => $questionnaire->get_structured_response($response->responseid),
            ];
        }
        $responses->close();

        // The data for the last activity won't have been written yet, so make sure to write it now!
        if (!empty($responsedata)) {
            $context = \context_module::instance($lastcmid);
            // Fetch the generic module data for the questionnaire.
            $contextdata = \core_privacy\local\request\helper::get_context_data($context, $user);
            // Merge with attempt data and write it.
            $contextdata = (object)array_merge((array)$contextdata, $responsedata);
            \core_privacy\local\request\writer::with_context($context)->export_data([], $contextdata);
        }
    }

    /**
     * Delete all personal data for all users in the specified context.
     *
     * @param context $context Context to delete data from.
     */
    public static function delete_data_for_all_users_in_context(\context $context) {
        global $DB;

        if (!($context instanceof \context_module)) {
            return;
        }

        if (!$cm = get_coursemodule_from_id('questionnaire', $context->instanceid)) {
            return;
        }

        if (!($questionnaire = $DB->get_record('questionnaire', ['id' => $cm->instance]))) {
            return;
        }

        if ($responses = $DB->get_recordset('questionnaire_response', ['questionnaireid' => $questionnaire->id])) {
            self::delete_responses($responses);
        }
        $responses->close();
        $DB->delete_records('questionnaire_response', ['questionnaireid' => $questionnaire->id]);
    }

    /**
     * Delete all user data for the specified user, in the specified contexts.
     *
     * @param   approved_contextlist $contextlist The approved contexts and user information to delete information for.
     */
    public static function delete_data_for_user(approved_contextlist $contextlist) {
        global $DB;

        if (empty($contextlist->count())) {
            return;
        }

        $userid = $contextlist->get_user()->id;
        foreach ($contextlist->get_contexts() as $context) {
            if (!($context instanceof \context_module)) {
                continue;
            }
            if (!$cm = get_coursemodule_from_id('questionnaire', $context->instanceid)) {
                continue;
            }

            if (!($questionnaire = $DB->get_record('questionnaire', ['id' => $cm->instance]))) {
                continue;
            }

            if ($responses = $DB->get_recordset('questionnaire_response',
                ['questionnaireid' => $questionnaire->id, 'userid' => $userid])) {
                self::delete_responses($responses);
            }
            $responses->close();
            $DB->delete_records('questionnaire_response', ['questionnaireid' => $questionnaire->id, 'userid' => $userid]);
        }
    }

    /**
     * Delete multiple users within a single context.
     *
     * @param \core_privacy\local\request\approved_userlist $userlist The approved context and user information to delete
     * information for.
     */
    public static function delete_data_for_users(approved_userlist $userlist) {
        global $DB;

        $context = $userlist->get_context();
        if (!$cm = get_coursemodule_from_id('questionnaire', $context->instanceid)) {
            return;
        }
        if (!($questionnaire = $DB->get_record('questionnaire', ['id' => $cm->instance]))) {
            return;
        }

        list($userinsql, $userinparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
        $params = array_merge(['questionnaireid' => $questionnaire->id], $userinparams);
        $select = 'questionnaireid = :questionnaireid AND userid ' . $userinsql;
        if ($responses = $DB->get_recordset_select('questionnaire_response', $select, $params)) {
            self::delete_responses($responses);
        }
        $responses->close();
        $DB->delete_records_select('questionnaire_response', $select, $params);
    }

    /**
     * Helper function to delete all the response records for a recordset array of responses.
     *
     * @param \moodle_recordset $responses The list of response records to delete for.
     */
    private static function delete_responses(\moodle_recordset $responses) {
        global $DB;

        foreach ($responses as $response) {
            $DB->delete_records('questionnaire_response_bool', ['response_id' => $response->id]);
            $DB->delete_records('questionnaire_response_date', ['response_id' => $response->id]);
            $DB->delete_records('questionnaire_resp_multiple', ['response_id' => $response->id]);
            $DB->delete_records('questionnaire_response_other', ['response_id' => $response->id]);
            $DB->delete_records('questionnaire_response_rank', ['response_id' => $response->id]);
            $DB->delete_records('questionnaire_resp_single', ['response_id' => $response->id]);
            $DB->delete_records('questionnaire_response_text', ['response_id' => $response->id]);
        }
    }
}