From 4e72d5916cfd25d89d67cdf6cc74c12bf8dd7f81 Mon Sep 17 00:00:00 2001 From: Peter Burnett Date: Thu, 7 Jan 2021 11:15:05 +1000 Subject: [PATCH] MDL-70444 my: Improved robustness of bulk dashboard reset --- lang/en/moodle.php | 2 ++ lang/en/my.php | 1 + my/indexsys.php | 21 +++++++++--- my/lib.php | 82 ++++++++++++++++++++++++++++++++-------------- 4 files changed, 78 insertions(+), 28 deletions(-) diff --git a/lang/en/moodle.php b/lang/en/moodle.php index d58d87d4ad9a1..65be797db77e1 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -267,6 +267,7 @@ $string['commentsrequirelogin'] = 'You need to log in to view the comments.'; $string['comparelanguage'] = 'Compare and edit current language'; $string['complete'] = 'Complete'; +$string['completed'] = 'Completed'; $string['completereport'] = 'Complete report'; $string['configuration'] = 'Configuration'; $string['confirm'] = 'Confirm'; @@ -1115,6 +1116,7 @@ $string['indicator:userforumstracking'] = 'User is tracking forums'; $string['indicator:userforumstracking_help'] = 'This indicator represents whether or not the student has tracking turned on in the forums.'; $string['info'] = 'Information'; +$string['inprogress'] = 'In progress'; $string['institution'] = 'Institution'; $string['instudentview'] = 'in student view'; $string['interests'] = 'Interests'; diff --git a/lang/en/my.php b/lang/en/my.php index 02b2a82651459..97a6bfa46359e 100644 --- a/lang/en/my.php +++ b/lang/en/my.php @@ -31,6 +31,7 @@ $string['defaultprofilepage'] = 'Default profile page'; $string['addpage'] = 'Add page'; $string['alldashboardswerereset'] = 'All Dashboard pages have been reset to default.'; +$string['resettingdashboards'] = 'Resetting user dashboards to default...'; $string['allprofileswerereset'] = 'All profile pages have been reset to default.'; $string['delpage'] = 'Delete page'; $string['managepages'] = 'Manage pages'; diff --git a/my/indexsys.php b/my/indexsys.php index a7bcdc27e0367..09515a4dc6604 100644 --- a/my/indexsys.php +++ b/my/indexsys.php @@ -35,20 +35,32 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +define('NO_OUTPUT_BUFFERING', true); require_once(__DIR__ . '/../config.php'); require_once($CFG->dirroot . '/my/lib.php'); require_once($CFG->libdir.'/adminlib.php'); -$resetall = optional_param('resetall', null, PARAM_BOOL); +$resetall = optional_param('resetall', false, PARAM_BOOL); $header = "$SITE->shortname: ".get_string('myhome')." (".get_string('mypage', 'admin').")"; $PAGE->set_blocks_editing_capability('moodle/my:configsyspages'); admin_externalpage_setup('mypage', '', null, '', array('pagelayout' => 'mydashboard')); +// If we are resetting all, just output a progress bar. if ($resetall && confirm_sesskey()) { - my_reset_page_for_all_users(MY_PAGE_PRIVATE, 'my-index'); - redirect($PAGE->url, get_string('alldashboardswerereset', 'my')); + echo $OUTPUT->header($header); + echo $OUTPUT->heading(get_string('resettingdashboards', 'my'), 3); + + $progressbar = new progress_bar(); + $progressbar->create(); + + \core\session\manager::write_close(); + my_reset_page_for_all_users(MY_PAGE_PRIVATE, 'my-index', $progressbar); + core\notification::success(get_string('alldashboardswerereset', 'my')); + echo $OUTPUT->continue_button($PAGE->url); + echo $OUTPUT->footer(); + die(); } // Override pagetype to show blocks properly. @@ -65,7 +77,8 @@ $PAGE->set_subpage($currentpage->id); // Display a button to reset everyone's dashboard. -$url = new moodle_url($PAGE->url, array('resetall' => 1)); +$url = $PAGE->url; +$url->params(['resetall' => true, 'sesskey' => sesskey()]); $button = $OUTPUT->single_button($url, get_string('reseteveryonesdashboard', 'my')); $PAGE->set_button($button . $PAGE->button); diff --git a/my/lib.php b/my/lib.php index 85ea1e8555988..1d16289b6f6da 100644 --- a/my/lib.php +++ b/my/lib.php @@ -166,40 +166,70 @@ function my_reset_page($userid, $private=MY_PAGE_PRIVATE, $pagetype='my-index') * * @param int $private Either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC. * @param string $pagetype Either my-index or user-profile. + * @param progress_bar $progressbar A progress bar to update. * @return void */ -function my_reset_page_for_all_users($private = MY_PAGE_PRIVATE, $pagetype = 'my-index') { +function my_reset_page_for_all_users($private = MY_PAGE_PRIVATE, $pagetype = 'my-index', $progressbar = null) { global $DB; // This may take a while. Raise the execution time limit. core_php_time_limit::raise(); - // Find all the user pages and all block instances in them. - $sql = "SELECT bi.id - FROM {my_pages} p - JOIN {context} ctx ON ctx.instanceid = p.userid AND ctx.contextlevel = :usercontextlevel - JOIN {block_instances} bi ON bi.parentcontextid = ctx.id AND - bi.pagetypepattern = :pagetypepattern AND - (bi.subpagepattern IS NULL OR bi.subpagepattern = " . $DB->sql_concat("''", 'p.id') . ") - WHERE p.private = :private"; - $params = array('private' => $private, - 'usercontextlevel' => CONTEXT_USER, - 'pagetypepattern' => $pagetype); - $blockids = $DB->get_fieldset_sql($sql, $params); - - // Wrap the SQL queries in a transaction. - $transaction = $DB->start_delegated_transaction(); - - // Delete the block instances. - if (!empty($blockids)) { - blocks_delete_instances($blockids); + $users = $DB->get_fieldset_select( + 'my_pages', + 'DISTINCT(userid)', + 'userid IS NOT NULL AND private = :private', + ['private' => $private] + ); + $chunks = array_chunk($users, 20); + + if (!empty($progressbar) && count($chunks) > 0) { + $count = count($chunks); + $message = get_string('inprogress'); + $progressbar->update(0, $count, $message); } - // Finally delete the pages. - $DB->delete_records_select('my_pages', 'userid IS NOT NULL AND private = :private', ['private' => $private]); + foreach ($chunks as $key => $userchunk) { + list($infragment, $inparams) = $DB->get_in_or_equal($userchunk, SQL_PARAMS_NAMED); + // Find all the user pages and all block instances in them. + $sql = "SELECT bi.id + FROM {my_pages} p + JOIN {context} ctx ON ctx.instanceid = p.userid AND ctx.contextlevel = :usercontextlevel + JOIN {block_instances} bi ON bi.parentcontextid = ctx.id + AND bi.pagetypepattern = :pagetypepattern + AND (bi.subpagepattern IS NULL OR bi.subpagepattern = " . $DB->sql_concat("''", 'p.id') . ") + WHERE p.private = :private + AND p.userid $infragment"; + + $params = array_merge([ + 'private' => $private, + 'usercontextlevel' => CONTEXT_USER, + 'pagetypepattern' => $pagetype + ], $inparams); + $blockids = $DB->get_fieldset_sql($sql, $params); + + // Wrap the SQL queries in a transaction. + $transaction = $DB->start_delegated_transaction(); + + // Delete the block instances. + if (!empty($blockids)) { + blocks_delete_instances($blockids); + } - // We should be good to go now. - $transaction->allow_commit(); + // Finally delete the pages. + $DB->delete_records_select( + 'my_pages', + "userid $infragment AND private = :private", + array_merge(['private' => $private], $inparams) + ); + + // We should be good to go now. + $transaction->allow_commit(); + + if (!empty($progressbar)) { + $progressbar->update(((int) $key + 1), $count, $message); + } + } // Trigger dashboard has been reset event. $eventparams = array( @@ -211,6 +241,10 @@ function my_reset_page_for_all_users($private = MY_PAGE_PRIVATE, $pagetype = 'my ); $event = \core\event\dashboards_reset::create($eventparams); $event->trigger(); + + if (!empty($progressbar)) { + $progressbar->update(1, 1, get_string('completed')); + } } class my_syspage_block_manager extends block_manager {