From cd7baa11c9ab5b1935f5032bfb2be03ba01868d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Massart?= Date: Sat, 28 Apr 2018 14:01:04 +0800 Subject: [PATCH] MDL-62248 core_block: Implement privacy API --- blocks/classes/privacy/provider.php | 227 +++++++++++++++++ blocks/tests/privacy_test.php | 364 ++++++++++++++++++++++++++++ lang/en/block.php | 4 + 3 files changed, 595 insertions(+) create mode 100644 blocks/classes/privacy/provider.php create mode 100644 blocks/tests/privacy_test.php diff --git a/blocks/classes/privacy/provider.php b/blocks/classes/privacy/provider.php new file mode 100644 index 0000000000000..cbee6444deeb3 --- /dev/null +++ b/blocks/classes/privacy/provider.php @@ -0,0 +1,227 @@ +. + +/** + * Data provider. + * + * @package core_block + * @copyright 2018 Frédéric Massart + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_block\privacy; +defined('MOODLE_INTERNAL') || die(); + +use context; +use context_block; +use core_privacy\local\metadata\collection; +use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\transform; +use core_privacy\local\request\writer; + +/** + * Data provider class. + * + * @package core_block + * @copyright 2018 Frédéric Massart + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements + \core_privacy\local\metadata\provider, + \core_privacy\local\request\subsystem\provider, + \core_privacy\local\request\user_preference_provider { + + /** + * Returns metadata. + * + * @param collection $collection The initialised collection to add items to. + * @return collection A listing of user data stored through this system. + */ + public static function get_metadata(collection $collection) : collection { + $collection->add_user_preference('blockIDhidden', 'privacy:metadata:userpref:hiddenblock'); + $collection->add_user_preference('docked_block_instance_ID', 'privacy:metadata:userpref:dockedinstance'); + 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 contextlist containing the list of contexts used in this plugin. + */ + public static function get_contexts_for_userid(int $userid) : \core_privacy\local\request\contextlist { + global $DB; + $contextlist = new \core_privacy\local\request\contextlist(); + + // Fetch the block instance IDs. + $likehidden = $DB->sql_like('name', ':hidden', false, false); + $likedocked = $DB->sql_like('name', ':docked', false, false); + $sql = "userid = :userid AND ($likehidden OR $likedocked)"; + $params = [ + 'userid' => $userid, + 'hidden' => 'block%hidden', + 'docked' => 'docked_block_instance_%', + ]; + $prefs = $DB->get_fieldset_select('user_preferences', 'name', $sql, $params); + + $instanceids = array_unique(array_map(function($prefname) { + if (preg_match('/^block(\d+)hidden$/', $prefname, $matches)) { + return $matches[1]; + } else if (preg_match('/^docked_block_instance_(\d+)$/', $prefname, $matches)) { + return $matches[1]; + } + return 0; + }, $prefs)); + + // Find the context of the instances. + if (!empty($instanceids)) { + list($insql, $inparams) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED); + $sql = " + SELECT ctx.id + FROM {context} ctx + WHERE ctx.instanceid $insql + AND ctx.contextlevel = :blocklevel"; + $params = array_merge($inparams, ['blocklevel' => CONTEXT_BLOCK]); + $contextlist->add_from_sql($sql, $params); + } + + return $contextlist; + } + + /** + * Export all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts to export information for. + */ + public static function export_user_data(approved_contextlist $contextlist) { + global $DB; + $userid = $contextlist->get_user()->id; + + // Extract the block instance IDs. + $instanceids = array_reduce($contextlist->get_contexts(), function($carry, $context) { + if ($context->contextlevel == CONTEXT_BLOCK) { + $carry[] = $context->instanceid; + } + return $carry; + }, []); + if (empty($instanceids)) { + return; + } + + // Query the blocks and their preferences. + list($insql, $inparams) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED); + $hiddenkey = $DB->sql_concat("'block'", 'bi.id', "'hidden'"); + $dockedkey = $DB->sql_concat("'docked_block_instance_'", 'bi.id'); + $sql = " + SELECT bi.id, h.value AS prefhidden, d.value AS prefdocked + FROM {block_instances} bi + LEFT JOIN {user_preferences} h + ON h.userid = :userid1 + AND h.name = $hiddenkey + LEFT JOIN {user_preferences} d + ON d.userid = :userid2 + AND d.name = $dockedkey + WHERE bi.id $insql + AND (h.id IS NOT NULL + OR d.id IS NOT NULL)"; + $params = array_merge($inparams, [ + 'userid1' => $userid, + 'userid2' => $userid, + ]); + + // Export all the things. + $dockedstr = get_string('privacy:request:blockisdocked', 'core_block'); + $hiddenstr = get_string('privacy:request:blockishidden', 'core_block'); + $recordset = $DB->get_recordset_sql($sql, $params); + foreach ($recordset as $record) { + $context = context_block::instance($record->id); + if ($record->prefdocked !== null) { + writer::with_context($context)->export_user_preference( + 'core_block', + 'block_is_docked', + transform::yesno($record->prefdocked), + $dockedstr + ); + } + if ($record->prefhidden !== null) { + writer::with_context($context)->export_user_preference( + 'core_block', + 'block_is_hidden', + transform::yesno($record->prefhidden), + $hiddenstr + ); + } + } + $recordset->close(); + } + + /** + * Export all user preferences for the plugin. + * + * @param int $userid The userid of the user whose data is to be exported. + */ + public static function export_user_preferences(int $userid) { + // Our preferences aren't site-wide so they are exported in export_user_data. + } + + /** + * Delete all data for all users in the specified context. + * + * @param context $context The specific context to delete data for. + */ + public static function delete_data_for_all_users_in_context(context $context) { + global $DB; + if ($context->contextlevel != CONTEXT_BLOCK) { + return; + } + + // Delete the user preferences. + $instanceid = $context->instanceid; + $DB->delete_records_list('user_preferences', 'name', [ + "block{$instanceid}hidden", + "docked_block_instance_{$instanceid}" + ]); + } + + /** + * 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; + $userid = $contextlist->get_user()->id; + $prefnames = array_reduce($contextlist->get_contexts(), function($carry, $context) { + if ($context->contextlevel == CONTEXT_BLOCK) { + $carry[] = "block{$context->instanceid}hidden"; + $carry[] = "docked_block_instance_{$context->instanceid}"; + } + return $carry; + }, []); + + if (empty($prefnames)) { + return; + } + + list($insql, $inparams) = $DB->get_in_or_equal($prefnames, SQL_PARAMS_NAMED); + $sql = "userid = :userid AND name $insql"; + $params = array_merge($inparams, ['userid' => $userid]); + $DB->delete_records_select('user_preferences', $sql, $params); + } + +} diff --git a/blocks/tests/privacy_test.php b/blocks/tests/privacy_test.php new file mode 100644 index 0000000000000..d8384a3f79a2e --- /dev/null +++ b/blocks/tests/privacy_test.php @@ -0,0 +1,364 @@ +. + +/** + * Data provider tests. + * + * @package core_block + * @category test + * @copyright 2018 Frédéric Massart + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); +global $CFG; + +use core_privacy\tests\provider_testcase; +use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\transform; +use core_privacy\local\request\writer; +use core_block\privacy\provider; + +/** + * Data provider testcase class. + * + * @package core_block + * @category test + * @copyright 2018 Frédéric Massart + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_block_privacy_testcase extends provider_testcase { + + public function setUp() { + $this->resetAfterTest(); + } + + public function test_get_contexts_for_userid() { + $dg = $this->getDataGenerator(); + $c1 = $dg->create_course(); + $c2 = $dg->create_course(); + $u1 = $dg->create_user(); + $u2 = $dg->create_user(); + $c1ctx = context_course::instance($c1->id); + $c2ctx = context_course::instance($c2->id); + $u1ctx = context_user::instance($u1->id); + $u2ctx = context_user::instance($u2->id); + + $manager = $this->get_block_manager(['region-a'], $c1ctx); + $manager->add_block('myprofile', 'region-a', 0, false); + $manager->load_blocks(); + $blockmyprofile = $manager->get_blocks_for_region('region-a')[0]; + + $manager = $this->get_block_manager(['region-a'], $c2ctx); + $manager->add_block('login', 'region-a', 0, false); + $manager->add_block('mentees', 'region-a', 1, false); + $manager->load_blocks(); + list($blocklogin, $blockmentees) = $manager->get_blocks_for_region('region-a'); + + $manager = $this->get_block_manager(['region-a'], $u1ctx); + $manager->add_block('private_files', 'region-a', 0, false); + $manager->load_blocks(); + $blockprivatefiles = $manager->get_blocks_for_region('region-a')[0]; + + $this->set_hidden_pref($blocklogin, true, $u1->id); + $this->set_hidden_pref($blockprivatefiles, true, $u1->id); + $this->set_docked_pref($blockmyprofile, true, $u1->id); + $this->set_docked_pref($blockmentees, true, $u1->id); + $this->set_docked_pref($blockmentees, true, $u2->id); + + $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids(); + $this->assertCount(4, $contextids); + $this->assertTrue(in_array($blocklogin->context->id, $contextids)); + $this->assertTrue(in_array($blockprivatefiles->context->id, $contextids)); + $this->assertTrue(in_array($blockmyprofile->context->id, $contextids)); + $this->assertTrue(in_array($blockmentees->context->id, $contextids)); + + $contextids = provider::get_contexts_for_userid($u2->id)->get_contextids(); + $this->assertCount(1, $contextids); + $this->assertTrue(in_array($blockmentees->context->id, $contextids)); + } + + public function test_delete_data_for_user() { + global $DB; + $dg = $this->getDataGenerator(); + $c1 = $dg->create_course(); + $c2 = $dg->create_course(); + $u1 = $dg->create_user(); + $u2 = $dg->create_user(); + $c1ctx = context_course::instance($c1->id); + $c2ctx = context_course::instance($c2->id); + $u1ctx = context_user::instance($u1->id); + $u2ctx = context_user::instance($u2->id); + + $manager = $this->get_block_manager(['region-a'], $c1ctx); + $manager->add_block('myprofile', 'region-a', 0, false); + $manager->load_blocks(); + $blockmyprofile = $manager->get_blocks_for_region('region-a')[0]; + + $manager = $this->get_block_manager(['region-a'], $c2ctx); + $manager->add_block('login', 'region-a', 0, false); + $manager->add_block('mentees', 'region-a', 1, false); + $manager->load_blocks(); + list($blocklogin, $blockmentees) = $manager->get_blocks_for_region('region-a'); + + $manager = $this->get_block_manager(['region-a'], $u1ctx); + $manager->add_block('private_files', 'region-a', 0, false); + $manager->load_blocks(); + $blockprivatefiles = $manager->get_blocks_for_region('region-a')[0]; + + $this->set_hidden_pref($blocklogin, true, $u1->id); + $this->set_hidden_pref($blocklogin, true, $u2->id); + $this->set_hidden_pref($blockprivatefiles, true, $u1->id); + $this->set_hidden_pref($blockmyprofile, true, $u1->id); + $this->set_docked_pref($blockmyprofile, true, $u1->id); + $this->set_docked_pref($blockmentees, true, $u1->id); + $this->set_docked_pref($blockmentees, true, $u2->id); + + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockprivatefiles->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockmyprofile->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmyprofile->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + + provider::delete_data_for_user(new approved_contextlist($u1, 'core_block', [$blocklogin->context->id, + $blockmyprofile->context->id, $blockmentees->context->id])); + + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockprivatefiles->instance->id}hidden"])); + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockmyprofile->instance->id}hidden"])); + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmyprofile->instance->id}"])); + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + } + + public function test_delete_data_for_all_users_in_context() { + global $DB; + $dg = $this->getDataGenerator(); + $c1 = $dg->create_course(); + $c2 = $dg->create_course(); + $u1 = $dg->create_user(); + $u2 = $dg->create_user(); + $c1ctx = context_course::instance($c1->id); + $c2ctx = context_course::instance($c2->id); + $u1ctx = context_user::instance($u1->id); + $u2ctx = context_user::instance($u2->id); + + $manager = $this->get_block_manager(['region-a'], $c1ctx); + $manager->add_block('myprofile', 'region-a', 0, false); + $manager->load_blocks(); + $blockmyprofile = $manager->get_blocks_for_region('region-a')[0]; + + $manager = $this->get_block_manager(['region-a'], $c2ctx); + $manager->add_block('login', 'region-a', 0, false); + $manager->add_block('mentees', 'region-a', 1, false); + $manager->load_blocks(); + list($blocklogin, $blockmentees) = $manager->get_blocks_for_region('region-a'); + + $manager = $this->get_block_manager(['region-a'], $u1ctx); + $manager->add_block('private_files', 'region-a', 0, false); + $manager->load_blocks(); + $blockprivatefiles = $manager->get_blocks_for_region('region-a')[0]; + + $this->set_hidden_pref($blocklogin, true, $u1->id); + $this->set_hidden_pref($blocklogin, true, $u2->id); + $this->set_hidden_pref($blockprivatefiles, true, $u1->id); + $this->set_hidden_pref($blockmyprofile, true, $u1->id); + $this->set_docked_pref($blockmyprofile, true, $u1->id); + $this->set_docked_pref($blockmentees, true, $u1->id); + $this->set_docked_pref($blockmentees, true, $u2->id); + + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockprivatefiles->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockmyprofile->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmyprofile->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + + // Nothing happens. + provider::delete_data_for_all_users_in_context($c1ctx); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockprivatefiles->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockmyprofile->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmyprofile->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + + // Delete one block. + provider::delete_data_for_all_users_in_context($blocklogin->context); + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockprivatefiles->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockmyprofile->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmyprofile->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + + // Delete another block. + provider::delete_data_for_all_users_in_context($blockmyprofile->context); + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "block{$blocklogin->instance->id}hidden"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockprivatefiles->instance->id}hidden"])); + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "block{$blockmyprofile->instance->id}hidden"])); + $this->assertFalse($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmyprofile->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u1->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + $this->assertTrue($DB->record_exists('user_preferences', ['userid' => $u2->id, + 'name' => "docked_block_instance_{$blockmentees->instance->id}"])); + } + + public function test_export_data_for_user() { + global $DB; + $dg = $this->getDataGenerator(); + $c1 = $dg->create_course(); + $c2 = $dg->create_course(); + $u1 = $dg->create_user(); + $u2 = $dg->create_user(); + $c1ctx = context_course::instance($c1->id); + $c2ctx = context_course::instance($c2->id); + $u1ctx = context_user::instance($u1->id); + $u2ctx = context_user::instance($u2->id); + $yes = transform::yesno(true); + $no = transform::yesno(false); + + $manager = $this->get_block_manager(['region-a'], $c1ctx); + $manager->add_block('myprofile', 'region-a', 0, false); + $manager->add_block('login', 'region-a', 1, false); + $manager->add_block('mentees', 'region-a', 2, false); + $manager->add_block('private_files', 'region-a', 3, false); + $manager->load_blocks(); + list($bmyprofile, $blogin, $bmentees, $bprivatefiles) = $manager->get_blocks_for_region('region-a'); + + // Set some user preferences. + $this->set_hidden_pref($blogin, true, $u1->id); + $this->set_docked_pref($blogin, false, $u1->id); + $this->set_docked_pref($blogin, true, $u2->id); + $this->set_hidden_pref($bprivatefiles, false, $u1->id); + $this->set_docked_pref($bprivatefiles, true, $u2->id); + $this->set_docked_pref($bmyprofile, true, $u1->id); + $this->set_docked_pref($bmentees, true, $u2->id); + + // Export data. + provider::export_user_data(new approved_contextlist($u1, 'core_block', [$bmyprofile->context->id, $blogin->context->id, + $bmentees->context->id, $bprivatefiles->context->id])); + $prefs = writer::with_context($bmentees->context)->get_user_preferences('core_block'); + $this->assertEmpty((array) $prefs); + + $prefs = writer::with_context($blogin->context)->get_user_preferences('core_block'); + $this->assertEquals($no, $prefs->block_is_docked->value); + $this->assertEquals($yes, $prefs->block_is_hidden->value); + + $prefs = writer::with_context($bprivatefiles->context)->get_user_preferences('core_block'); + $this->assertObjectNotHasAttribute('block_is_docked', $prefs); + $this->assertEquals($no, $prefs->block_is_hidden->value); + + $prefs = writer::with_context($bmyprofile->context)->get_user_preferences('core_block'); + $this->assertEquals($yes, $prefs->block_is_docked->value); + $this->assertObjectNotHasAttribute('block_is_hidden', $prefs); + } + + /** + * Get the block manager. + * + * @param array $regions The regions. + * @param context $context The context. + * @param string $pagetype The page type. + * @param string $subpage The sub page. + * @return block_manager + */ + protected function get_block_manager($regions, $context, $pagetype = 'page-type', $subpage = '') { + $page = new moodle_page(); + $page->set_context($context); + $page->set_pagetype($pagetype); + $page->set_subpage($subpage); + $page->set_url(new moodle_url('/')); + + $blockmanager = new block_manager($page); + $blockmanager->add_regions($regions, false); + $blockmanager->set_default_region($regions[0]); + + return $blockmanager; + } + + /** + * Set a docked preference. + * + * @param block_base $block The block. + * @param bool $value The value. + * @param int $userid The user ID. + */ + protected function set_docked_pref($block, $value, $userid) { + set_user_preference("docked_block_instance_{$block->instance->id}", $value, $userid); + } + + /** + * Set a hidden preference. + * + * @param block_base $block The block. + * @param bool $value The value. + * @param int $userid The user ID. + */ + protected function set_hidden_pref($block, $value, $userid) { + set_user_preference("block{$block->instance->id}hidden", $value, $userid); + } + +} diff --git a/lang/en/block.php b/lang/en/block.php index ad04aef94cc49..806aa16053644 100644 --- a/lang/en/block.php +++ b/lang/en/block.php @@ -56,6 +56,10 @@ $string['onthispage'] = 'On this page'; $string['pagetypes'] = 'Page types'; $string['pagetypewarning'] = 'The previously specified page type is no longer selectable. Please choose the most appropriate page type below.'; +$string['privacy:metadata:userpref:dockedinstance'] = 'Records when the user docked a block'; +$string['privacy:metadata:userpref:hiddenblock'] = 'Records when the user collapsed/hid a block'; +$string['privacy:request:blockisdocked'] = 'Indicates whether the block was docked'; +$string['privacy:request:blockishidden'] = 'Indicates whether the block was hidden/collapsed'; $string['region'] = 'Region'; $string['showblock'] = 'Show {$a} block'; $string['showoncontextandsubs'] = 'Display on \'{$a}\' and any pages within it';