From ad347f6874652a6e3101fa5c536f7dcde2296bea Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Thu, 27 Aug 2015 18:20:59 +0800 Subject: [PATCH 1/3] MDL-46878 my: Allow admins to reset everyone's dashboard --- lang/en/my.php | 4 ++++ lib/blocklib.php | 25 ++++++++++++++++++++++ my/indexsys.php | 12 ++++++++++- my/lib.php | 51 +++++++++++++++++++++++++++++++++++++++++++++ user/profilesys.php | 10 ++++++++- 5 files changed, 100 insertions(+), 2 deletions(-) diff --git a/lang/en/my.php b/lang/en/my.php index b1c8f86aca2..ea1e7347edd 100644 --- a/lang/en/my.php +++ b/lang/en/my.php @@ -30,7 +30,11 @@ $string['pinblocksexplan'] = 'Any block settings you configure here will be visi $string['defaultpage'] = 'Default My Moodle page'; $string['defaultprofilepage'] = 'Default profile page'; $string['addpage'] = 'Add page'; +$string['alldashboardswerereset'] = 'All Dashboard pages have been reset to default.'; +$string['allprofileswerereset'] = 'All profile pages have been reset to default.'; $string['delpage'] = 'Delete page'; $string['managepages'] = 'Manage pages'; +$string['reseteveryonesdashboard'] = 'Reset Dashboard for all users'; +$string['reseteveryonesprofile'] = 'Reset profile for all users'; $string['resetpage'] = 'Reset page to default'; $string['reseterror'] = 'There was an error resetting your page'; diff --git a/lib/blocklib.php b/lib/blocklib.php index 4866dc23d37..56208b6fdc6 100644 --- a/lib/blocklib.php +++ b/lib/blocklib.php @@ -2055,6 +2055,31 @@ function blocks_delete_instance($instance, $nolongerused = false, $skipblockstab } } +/** + * Delete multiple blocks at once. + * + * @param array $instanceids A list of block instance ID. + */ +function blocks_delete_instances($instanceids) { + global $DB; + + $instances = $DB->get_recordset_list('block_instances', 'id', $instanceids); + foreach ($instances as $instance) { + blocks_delete_instance($instance, false, true); + } + $instances->close(); + + $DB->delete_records_list('block_positions', 'blockinstanceid', $instanceids); + $DB->delete_records_list('block_instances', 'id', $instanceids); + + $preferences = array(); + foreach ($instanceids as $instanceid) { + $preferences[] = 'block' . $instanceid . 'hidden'; + $preferences[] = 'docked_block_instance_' . $instanceid; + } + $DB->delete_records_list('user_preferences', 'name', $preferences); +} + /** * Delete all the blocks that belong to a particular context. * diff --git a/my/indexsys.php b/my/indexsys.php index f21e5ac61fb..c3c2c124220 100644 --- a/my/indexsys.php +++ b/my/indexsys.php @@ -39,7 +39,7 @@ require_once(dirname(__FILE__) . '/../config.php'); require_once($CFG->dirroot . '/my/lib.php'); require_once($CFG->libdir.'/adminlib.php'); -$edit = optional_param('edit', null, PARAM_BOOL); // Turn editing on and off +$resetall = optional_param('resetall', null, PARAM_BOOL); require_login(); @@ -48,6 +48,11 @@ $header = "$SITE->shortname: ".get_string('myhome')." (".get_string('mypage', 'a $PAGE->set_blocks_editing_capability('moodle/my:configsyspages'); admin_externalpage_setup('mypage', '', null, '', array('pagelayout' => 'mydashboard')); +if ($resetall && confirm_sesskey()) { + my_reset_page_for_all_users(MY_PAGE_PRIVATE, 'my-index'); + redirect($PAGE->url, get_string('alldashboardswerereset', 'my')); +} + // Override pagetype to show blocks properly. $PAGE->set_pagetype('my-index'); @@ -61,6 +66,11 @@ if (!$currentpage = my_get_page(null, MY_PAGE_PRIVATE)) { } $PAGE->set_subpage($currentpage->id); +// Display a button to reset everyone's dashboard. +$url = new moodle_url($PAGE->url, array('resetall' => 1)); +$button = $OUTPUT->single_button($url, get_string('reseteveryonesdashboard', 'my')); +$PAGE->set_button($button . $PAGE->button); + echo $OUTPUT->header(); echo $OUTPUT->custom_block_region('content'); diff --git a/my/lib.php b/my/lib.php index 017abc2281f..bd3219d440f 100644 --- a/my/lib.php +++ b/my/lib.php @@ -136,6 +136,57 @@ function my_reset_page($userid, $private=MY_PAGE_PRIVATE, $pagetype='my-index') return $systempage; } +/** + * Resets the page customisations for all users. + * + * @param int $private Either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC. + * @param string $pagetype Either my-index or user-profile. + * @return void + */ +function my_reset_page_for_all_users($private = MY_PAGE_PRIVATE, $pagetype = 'my-index') { + global $DB; + + // Find all the user pages. + $where = 'userid IS NOT NULL AND private = :private'; + $params = array('private' => $private); + $pages = $DB->get_recordset_select('my_pages', $where, $params, 'id, userid'); + $pageids = array(); + $blockids = array(); + + foreach ($pages as $page) { + $pageids[] = $page->id; + $usercontext = context_user::instance($page->userid); + + // Find all block instances in that page. + $blocks = $DB->get_recordset('block_instances', array('parentcontextid' => $usercontext->id, + 'pagetypepattern' => $pagetype), '', 'id, subpagepattern'); + foreach ($blocks as $block) { + if (is_null($block->subpagepattern) || $block->subpagepattern == $page->id) { + $blockids[] = $block->id; + } + } + $blocks->close(); + } + $pages->close(); + + // Wrap the SQL queries in a transaction. + $transaction = $DB->start_delegated_transaction(); + + // Delete the block instances. + if (!empty($blockids)) { + blocks_delete_instances($blockids); + } + + // Finally delete the pages. + if (!empty($pageids)) { + list($insql, $inparams) = $DB->get_in_or_equal($pageids); + $DB->delete_records_select('my_pages', "id $insql", $pageids); + } + + // We should be good to go now. + $transaction->allow_commit(); +} + class my_syspage_block_manager extends block_manager { // HACK WARNING! // TODO: figure out a better way to do this diff --git a/user/profilesys.php b/user/profilesys.php index 63dd4269ea1..302f5cdd16d 100644 --- a/user/profilesys.php +++ b/user/profilesys.php @@ -31,7 +31,7 @@ require_once(dirname(__FILE__) . '/../config.php'); require_once($CFG->dirroot . '/my/lib.php'); require_once($CFG->libdir.'/adminlib.php'); -$edit = optional_param('edit', null, PARAM_BOOL); // Turn editing on and off. +$resetall = optional_param('resetall', null, PARAM_BOOL); require_login(); @@ -40,6 +40,11 @@ $header = "$SITE->shortname: ".get_string('publicprofile')." (".get_string('mypr $PAGE->set_blocks_editing_capability('moodle/my:configsyspages'); admin_externalpage_setup('profilepage', '', null, '', array('pagelayout' => 'mypublic')); +if ($resetall && confirm_sesskey()) { + my_reset_page_for_all_users(MY_PAGE_PUBLIC, 'user-profile'); + redirect($PAGE->url, get_string('allprofileswerereset', 'my')); +} + // Override pagetype to show blocks properly. $PAGE->set_pagetype('user-profile'); @@ -53,6 +58,9 @@ if (!$currentpage = my_get_page(null, MY_PAGE_PUBLIC)) { } $PAGE->set_subpage($currentpage->id); +$url = new moodle_url($PAGE->url, array('resetall' => 1)); +$button = $OUTPUT->single_button($url, get_string('reseteveryonesprofile', 'my')); +$PAGE->set_button($button . $PAGE->button); echo $OUTPUT->header(); From 080707079d0711b2daf57a7b59de2d431695ae84 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Fri, 28 Aug 2015 14:10:37 +0800 Subject: [PATCH 2/3] MDL-46878 my: Behat tests to cover reset of all customised pages --- my/tests/behat/reset_all_pages.feature | 129 +++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 my/tests/behat/reset_all_pages.feature diff --git a/my/tests/behat/reset_all_pages.feature b/my/tests/behat/reset_all_pages.feature new file mode 100644 index 00000000000..607012605f5 --- /dev/null +++ b/my/tests/behat/reset_all_pages.feature @@ -0,0 +1,129 @@ +@core @core_my +Feature: Reset all personalised pages to default + In order to reset everyone's personalised pages + As an admin + I need to press a button on the pages to customise the default pages + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | student1 | Student | 1 | student1@example.com | + | student2 | Student | 2 | student2@example.com | + | student3 | Student | 3 | student3@example.com | + And I log in as "admin" + And I set the following system permissions of "Authenticated user" role: + | block/myprofile:addinstance | Allow | + | moodle/block:edit | Allow | + And I log out + + And I log in as "student1" + And I follow "Dashboard" in the user menu + And I press "Customise this page" + And I add the "Comments" block + And I press "Stop customising this page" + And I should see "Comments" + And I log out + + And I log in as "student2" + And I follow "Profile" in the user menu + And I should not see "Logged in user" + And I press "Customise this page" + And I add the "Logged in user" block + And I press "Stop customising this page" + And I should see "Logged in user" + And I log out + + And I log in as "student3" + And I follow "Dashboard" in the user menu + And I should not see "Comments" + And I follow "Profile" in the user menu + And I should not see "Logged in user" + And I log out + + Scenario: Reset Dashboard for all users + Given I log in as "admin" + And I navigate to "Default Dashboard page" node in "Site administration > Appearance" + And I press "Blocks editing on" + And I add the "Latest news" block + And I open the "Online users" blocks action menu + And I follow "Delete Online users" + And I press "Yes" + And I press "Blocks editing off" + And I log out + + And I log in as "student1" + And I follow "Dashboard" in the user menu + And I should not see "Latest news" + And I should see "Online users" + And I log out + + And I log in as "student3" + And I follow "Dashboard" in the user menu + And I should not see "Latest news" + And I should see "Online users" + And I log out + + And I log in as "admin" + And I navigate to "Default Dashboard page" node in "Site administration > Appearance" + When I press "Reset Dashboard for all users" + And I follow "Continue" + And I log out + + And I log in as "student1" + And I follow "Dashboard" in the user menu + Then I should see "Latest news" + And I should not see "Comments" + And I should not see "Online users" + And I log out + + And I log in as "student3" + And I follow "Dashboard" in the user menu + And I should see "Latest news" + And I should not see "Online users" + And I log out + + # Check that this did not affect the customised profiles. + And I log in as "student2" + And I follow "Profile" in the user menu + And I should see "Logged in user" + And I should not see "Latest news" + + Scenario: Reset profile for all users + Given I log in as "admin" + And I navigate to "Default profile page" node in "Site administration > Appearance" + And I press "Blocks editing on" + And I add the "Latest news" block + And I log out + + And I log in as "student2" + And I follow "Profile" in the user menu + And I should not see "Latest news" + And I log out + + And I log in as "student3" + And I follow "Profile" in the user menu + And I should not see "Latest news" + And I log out + + And I log in as "admin" + And I navigate to "Default profile page" node in "Site administration > Appearance" + When I press "Reset profile for all users" + And I follow "Continue" + And I log out + + And I log in as "student2" + And I follow "Profile" in the user menu + Then I should see "Latest news" + And I should not see "Logged in user" + And I log out + + And I log in as "student3" + And I follow "Profile" in the user menu + And I should see "Latest news" + And I log out + + # Check that this did not affect the customised dashboards. + And I log in as "student1" + And I follow "Dashboard" in the user menu + And I should see "Comments" + And I should not see "Latest news" From d569c0dae26fcf5bdd65cbf169e51e80867d4bd4 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Fri, 28 Aug 2015 17:41:44 +0800 Subject: [PATCH 3/3] MDL-46878 blocklib: Testing blocks_delete_instances() --- lib/tests/blocklib_test.php | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/lib/tests/blocklib_test.php b/lib/tests/blocklib_test.php index 38e3790c0f6..533eeaa7770 100644 --- a/lib/tests/blocklib_test.php +++ b/lib/tests/blocklib_test.php @@ -478,6 +478,70 @@ class core_blocklib_testcase extends advanced_testcase { $expected = array('user-profile', 'user-profile-*', 'user-*', '*'); $this->assertEquals($expected, array_values(matching_page_type_patterns_from_pattern($pattern))); } + + public function test_delete_instances() { + global $DB; + $this->purge_blocks(); + $this->setAdminUser(); + + $regionname = 'a-region'; + $blockname = $this->get_a_known_block_type(); + $context = context_system::instance(); + + list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname), + $context, 'page-type'); + + $blockmanager->add_blocks(array($regionname => array($blockname, $blockname, $blockname)), null, null, false, 3); + $blockmanager->load_blocks(); + + $blocks = $blockmanager->get_blocks_for_region($regionname); + $blockids = array(); + $preferences = array(); + + // Create block related data. + foreach ($blocks as $block) { + $instance = $block->instance; + $pref = 'block' . $instance->id . 'hidden'; + set_user_preference($pref, '123', 123); + $preferences[] = $pref; + $pref = 'docked_block_instance_' . $instance->id; + set_user_preference($pref, '123', 123); + $preferences[] = $pref; + blocks_set_visibility($instance, $page, 1); + $blockids[] = $instance->id; + } + + // Confirm what has been set. + $this->assertCount(3, $blockids); + list($insql, $inparams) = $DB->get_in_or_equal($blockids); + $this->assertEquals(3, $DB->count_records_select('block_positions', "blockinstanceid $insql", $inparams)); + list($insql, $inparams) = $DB->get_in_or_equal($preferences); + $this->assertEquals(6, $DB->count_records_select('user_preferences', "name $insql", $inparams)); + + // Keep a block on the side. + $allblockids = $blockids; + $tokeep = array_pop($blockids); + + // Delete and confirm what should have happened. + blocks_delete_instances($blockids); + + // Reload the manager. + list($page, $blockmanager) = $this->get_a_page_and_block_manager(array($regionname), + $context, 'page-type'); + $blockmanager->load_blocks(); + $blocks = $blockmanager->get_blocks_for_region($regionname); + + $this->assertCount(1, $blocks); + list($insql, $inparams) = $DB->get_in_or_equal($allblockids); + $this->assertEquals(1, $DB->count_records_select('block_positions', "blockinstanceid $insql", $inparams)); + list($insql, $inparams) = $DB->get_in_or_equal($preferences); + $this->assertEquals(2, $DB->count_records_select('user_preferences', "name $insql", $inparams)); + + $this->assertFalse(context_block::instance($blockids[0], IGNORE_MISSING)); + $this->assertFalse(context_block::instance($blockids[1], IGNORE_MISSING)); + context_block::instance($tokeep); // Would throw an exception if it was deleted. + } + } /**