| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 3 | //
 | 
        
           |  |  | 4 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 5 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 6 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 7 | // (at your option) any later version.
 | 
        
           |  |  | 8 | //
 | 
        
           |  |  | 9 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 12 | // GNU General Public License for more details.
 | 
        
           |  |  | 13 | //
 | 
        
           |  |  | 14 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 15 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | /**
 | 
        
           |  |  | 18 |  * My Moodle -- a user's personal dashboard
 | 
        
           |  |  | 19 |  *
 | 
        
           |  |  | 20 |  * This file contains common functions for the dashboard and profile pages.
 | 
        
           |  |  | 21 |  *
 | 
        
           |  |  | 22 |  * @package    moodlecore
 | 
        
           |  |  | 23 |  * @subpackage my
 | 
        
           |  |  | 24 |  * @copyright  2010 Remote-Learner.net
 | 
        
           |  |  | 25 |  * @author     Hubert Chathi <hubert@remote-learner.net>
 | 
        
           |  |  | 26 |  * @author     Olav Jordan <olav.jordan@remote-learner.net>
 | 
        
           |  |  | 27 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 28 |  */
 | 
        
           |  |  | 29 |   | 
        
           |  |  | 30 | define('MY_PAGE_PUBLIC', 0);
 | 
        
           |  |  | 31 | define('MY_PAGE_PRIVATE', 1);
 | 
        
           |  |  | 32 | define('MY_PAGE_DEFAULT', '__default');
 | 
        
           |  |  | 33 | define('MY_PAGE_COURSES', '__courses');
 | 
        
           |  |  | 34 |   | 
        
           |  |  | 35 | require_once("$CFG->libdir/blocklib.php");
 | 
        
           |  |  | 36 |   | 
        
           |  |  | 37 | /**
 | 
        
           |  |  | 38 |  * For a given user, this returns the $page information for their My Moodle page
 | 
        
           |  |  | 39 |  *
 | 
        
           |  |  | 40 |  * @param int|null $userid the id of the user whose page should be retrieved
 | 
        
           |  |  | 41 |  * @param int|null $private either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC
 | 
        
           |  |  | 42 |  * @param string|null $pagename Differentiate between standard /my or /courses pages.
 | 
        
           |  |  | 43 |  */
 | 
        
           |  |  | 44 | function my_get_page(?int $userid, int $private = MY_PAGE_PRIVATE, string $pagename = MY_PAGE_DEFAULT) {
 | 
        
           |  |  | 45 |     global $DB, $CFG;
 | 
        
           |  |  | 46 |   | 
        
           |  |  | 47 |     if (empty($CFG->forcedefaultmymoodle) && $userid) {  // Ignore custom My Moodle pages if admin has forced them
 | 
        
           |  |  | 48 |         // Does the user have their own page defined?  If so, return it.
 | 
        
           |  |  | 49 |         if ($customised = $DB->get_record(
 | 
        
           |  |  | 50 |             'my_pages',
 | 
        
           |  |  | 51 |             array('userid' => $userid, 'private' => $private, 'name' => $pagename),
 | 
        
           |  |  | 52 |             '*',
 | 
        
           |  |  | 53 |             IGNORE_MULTIPLE
 | 
        
           |  |  | 54 |         )) {
 | 
        
           |  |  | 55 |             return $customised;
 | 
        
           |  |  | 56 |         }
 | 
        
           |  |  | 57 |     }
 | 
        
           |  |  | 58 |   | 
        
           |  |  | 59 |     // Otherwise return the system default page
 | 
        
           |  |  | 60 |     return $DB->get_record('my_pages', array('userid' => null, 'name' => $pagename, 'private' => $private), '*', IGNORE_MULTIPLE);
 | 
        
           |  |  | 61 | }
 | 
        
           |  |  | 62 |   | 
        
           |  |  | 63 |   | 
        
           |  |  | 64 | /**
 | 
        
           |  |  | 65 |  * This copies a system default page to the current user
 | 
        
           |  |  | 66 |  *
 | 
        
           |  |  | 67 |  * @param int $userid the id of the user whose page should be reset
 | 
        
           |  |  | 68 |  * @param int $private either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC
 | 
        
           |  |  | 69 |  * @param string $pagetype either my-index or user-profile
 | 
        
           |  |  | 70 |  * @param string $pagename Differentiate between standard /my or /courses pages.
 | 
        
           |  |  | 71 |  */
 | 
        
           |  |  | 72 | function my_copy_page(
 | 
        
           |  |  | 73 |     int $userid,
 | 
        
           |  |  | 74 |     int $private = MY_PAGE_PRIVATE,
 | 
        
           |  |  | 75 |     string $pagetype = 'my-index',
 | 
        
           |  |  | 76 |     string $pagename = MY_PAGE_DEFAULT
 | 
        
           |  |  | 77 | ) {
 | 
        
           |  |  | 78 |     global $DB;
 | 
        
           |  |  | 79 |   | 
        
           |  |  | 80 |     if ($customised = $DB->get_record(
 | 
        
           |  |  | 81 |         'my_pages',
 | 
        
           |  |  | 82 |         array('userid' => $userid, 'name' => $pagename, 'private' => $private),
 | 
        
           |  |  | 83 |         '*',
 | 
        
           |  |  | 84 |         IGNORE_MULTIPLE
 | 
        
           |  |  | 85 |     )) {
 | 
        
           |  |  | 86 |         return $customised;  // We're done!
 | 
        
           |  |  | 87 |     }
 | 
        
           |  |  | 88 |   | 
        
           |  |  | 89 |     // Get the system default page
 | 
        
           |  |  | 90 |     if (!$systempage = $DB->get_record(
 | 
        
           |  |  | 91 |         'my_pages',
 | 
        
           |  |  | 92 |         array('userid' => null, 'name' => $pagename, 'private' => $private),
 | 
        
           |  |  | 93 |         '*',
 | 
        
           |  |  | 94 |         IGNORE_MULTIPLE
 | 
        
           |  |  | 95 |     )) {
 | 
        
           |  |  | 96 |         return false;  // error
 | 
        
           |  |  | 97 |     }
 | 
        
           |  |  | 98 |   | 
        
           |  |  | 99 |     // Clone the basic system page record
 | 
        
           |  |  | 100 |     $page = clone($systempage);
 | 
        
           |  |  | 101 |     unset($page->id);
 | 
        
           |  |  | 102 |     $page->userid = $userid;
 | 
        
           |  |  | 103 |     $page->id = $DB->insert_record('my_pages', $page);
 | 
        
           |  |  | 104 |   | 
        
           |  |  | 105 |     // Clone ALL the associated blocks as well
 | 
        
           |  |  | 106 |     $systemcontext = context_system::instance();
 | 
        
           |  |  | 107 |     $usercontext = context_user::instance($userid);
 | 
        
           |  |  | 108 |   | 
        
           |  |  | 109 |     $blockinstances = $DB->get_records('block_instances', array('parentcontextid' => $systemcontext->id,
 | 
        
           |  |  | 110 |                                                                 'pagetypepattern' => $pagetype,
 | 
        
           |  |  | 111 |                                                                 'subpagepattern' => $systempage->id));
 | 
        
           |  |  | 112 |     $roles = get_all_roles();
 | 
        
           |  |  | 113 |     $newblockinstanceids = [];
 | 
        
           |  |  | 114 |     foreach ($blockinstances as $instance) {
 | 
        
           |  |  | 115 |         $originalid = $instance->id;
 | 
        
           |  |  | 116 |         $originalcontext = context_block::instance($originalid);
 | 
        
           |  |  | 117 |         unset($instance->id);
 | 
        
           |  |  | 118 |         $instance->parentcontextid = $usercontext->id;
 | 
        
           |  |  | 119 |         $instance->subpagepattern = $page->id;
 | 
        
           |  |  | 120 |         $instance->timecreated = time();
 | 
        
           |  |  | 121 |         $instance->timemodified = $instance->timecreated;
 | 
        
           |  |  | 122 |         $instance->id = $DB->insert_record('block_instances', $instance);
 | 
        
           |  |  | 123 |         $newblockinstanceids[$originalid] = $instance->id;
 | 
        
           |  |  | 124 |         $blockcontext = context_block::instance($instance->id);  // Just creates the context record
 | 
        
           |  |  | 125 |         $block = block_instance($instance->blockname, $instance);
 | 
        
           |  |  | 126 |         if (empty($block) || !$block->instance_copy($originalid)) {
 | 
        
           |  |  | 127 |             debugging("Unable to copy block-specific data for original block
 | 
        
           |  |  | 128 |                 instance: $originalid to new block instance: $instance->id for
 | 
        
           |  |  | 129 |                 block: $instance->blockname", DEBUG_DEVELOPER);
 | 
        
           |  |  | 130 |         }
 | 
        
           |  |  | 131 |         // Check if there are any overrides on this block instance.
 | 
        
           |  |  | 132 |         // We check against all roles, not just roles assigned to the user.
 | 
        
           |  |  | 133 |         // This is so any overrides that are applied to the system default page
 | 
        
           |  |  | 134 |         // will be applied to the user's page as well, even if their role assignment changes in the future.
 | 
        
           |  |  | 135 |         foreach ($roles as $role) {
 | 
        
           |  |  | 136 |             $rolecapabilities = get_capabilities_from_role_on_context($role, $originalcontext);
 | 
        
           |  |  | 137 |             // If there are overrides, then apply them to the new block instance.
 | 
        
           |  |  | 138 |             foreach ($rolecapabilities as $rolecapability) {
 | 
        
           |  |  | 139 |                 role_change_permission(
 | 
        
           |  |  | 140 |                     $rolecapability->roleid,
 | 
        
           |  |  | 141 |                     $blockcontext,
 | 
        
           |  |  | 142 |                     $rolecapability->capability,
 | 
        
           |  |  | 143 |                     $rolecapability->permission
 | 
        
           |  |  | 144 |                 );
 | 
        
           |  |  | 145 |             }
 | 
        
           |  |  | 146 |         }
 | 
        
           |  |  | 147 |     }
 | 
        
           |  |  | 148 |   | 
        
           |  |  | 149 |     // Clone block position overrides.
 | 
        
           |  |  | 150 |     if ($blockpositions = $DB->get_records('block_positions',
 | 
        
           |  |  | 151 |             ['subpage' => $systempage->id, 'pagetype' => $pagetype, 'contextid' => $systemcontext->id])) {
 | 
        
           |  |  | 152 |         foreach ($blockpositions as &$positions) {
 | 
        
           |  |  | 153 |             $positions->subpage = $page->id;
 | 
        
           |  |  | 154 |             $positions->contextid = $usercontext->id;
 | 
        
           |  |  | 155 |             if (array_key_exists($positions->blockinstanceid, $newblockinstanceids)) {
 | 
        
           |  |  | 156 |                 // For block instances that were defined on the default dashboard and copied to the user dashboard
 | 
        
           |  |  | 157 |                 // use the new blockinstanceid.
 | 
        
           |  |  | 158 |                 $positions->blockinstanceid = $newblockinstanceids[$positions->blockinstanceid];
 | 
        
           |  |  | 159 |             }
 | 
        
           |  |  | 160 |             unset($positions->id);
 | 
        
           |  |  | 161 |         }
 | 
        
           |  |  | 162 |         $DB->insert_records('block_positions', $blockpositions);
 | 
        
           |  |  | 163 |     }
 | 
        
           |  |  | 164 |   | 
        
           |  |  | 165 |     return $page;
 | 
        
           |  |  | 166 | }
 | 
        
           |  |  | 167 |   | 
        
           |  |  | 168 | /**
 | 
        
           |  |  | 169 |  * For a given user, this deletes their My Moodle page and returns them to the system default.
 | 
        
           |  |  | 170 |  *
 | 
        
           |  |  | 171 |  * @param int $userid the id of the user whose page should be reset
 | 
        
           |  |  | 172 |  * @param int $private either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC
 | 
        
           |  |  | 173 |  * @param string $pagetype either my-index or user-profile
 | 
        
           |  |  | 174 |  * @param string $pagename Differentiate between standard /my or /courses pages.
 | 
        
           |  |  | 175 |  * @return mixed system page, or false on error
 | 
        
           |  |  | 176 |  */
 | 
        
           |  |  | 177 | function my_reset_page(
 | 
        
           |  |  | 178 |     int $userid,
 | 
        
           |  |  | 179 |     int $private = MY_PAGE_PRIVATE,
 | 
        
           |  |  | 180 |     string $pagetype='my-index',
 | 
        
           |  |  | 181 |     string $pagename = MY_PAGE_DEFAULT
 | 
        
           |  |  | 182 | ) {
 | 
        
           |  |  | 183 |     global $DB, $CFG;
 | 
        
           |  |  | 184 |   | 
        
           |  |  | 185 |     $page = my_get_page($userid, $private, $pagename);
 | 
        
           |  |  | 186 |     if ($page->userid == $userid) {
 | 
        
           |  |  | 187 |         $context = context_user::instance($userid);
 | 
        
           |  |  | 188 |         if ($blocks = $DB->get_records('block_instances', array('parentcontextid' => $context->id,
 | 
        
           |  |  | 189 |                 'pagetypepattern' => $pagetype))) {
 | 
        
           |  |  | 190 |             foreach ($blocks as $block) {
 | 
        
           |  |  | 191 |                 if (is_null($block->subpagepattern) || $block->subpagepattern == $page->id) {
 | 
        
           |  |  | 192 |                     blocks_delete_instance($block);
 | 
        
           |  |  | 193 |                 }
 | 
        
           |  |  | 194 |             }
 | 
        
           |  |  | 195 |         }
 | 
        
           |  |  | 196 |         $DB->delete_records('block_positions', ['subpage' => $page->id, 'pagetype' => $pagetype, 'contextid' => $context->id]);
 | 
        
           |  |  | 197 |         $DB->delete_records('my_pages', array('id' => $page->id, 'name' => $pagename));
 | 
        
           |  |  | 198 |     }
 | 
        
           |  |  | 199 |   | 
        
           |  |  | 200 |     // Get the system default page
 | 
        
           |  |  | 201 |     if (!$systempage = $DB->get_record(
 | 
        
           |  |  | 202 |         'my_pages',
 | 
        
           |  |  | 203 |         array('userid' => null, 'name' => $pagename, 'private' => $private),
 | 
        
           |  |  | 204 |         '*',
 | 
        
           |  |  | 205 |         IGNORE_MULTIPLE
 | 
        
           |  |  | 206 |     )) {
 | 
        
           |  |  | 207 |         return false; // error
 | 
        
           |  |  | 208 |     }
 | 
        
           |  |  | 209 |   | 
        
           |  |  | 210 |     // Trigger dashboard has been reset event.
 | 
        
           |  |  | 211 |     $eventparams = array(
 | 
        
           |  |  | 212 |         'context' => context_user::instance($userid),
 | 
        
           |  |  | 213 |         'other' => array(
 | 
        
           |  |  | 214 |             'private' => $private,
 | 
        
           |  |  | 215 |             'pagetype' => $pagetype,
 | 
        
           |  |  | 216 |         ),
 | 
        
           |  |  | 217 |     );
 | 
        
           |  |  | 218 |     $event = \core\event\dashboard_reset::create($eventparams);
 | 
        
           |  |  | 219 |     $event->trigger();
 | 
        
           |  |  | 220 |     return $systempage;
 | 
        
           |  |  | 221 | }
 | 
        
           |  |  | 222 |   | 
        
           |  |  | 223 | /**
 | 
        
           |  |  | 224 |  * Resets the page customisations for all users.
 | 
        
           |  |  | 225 |  *
 | 
        
           |  |  | 226 |  * @param int $private Either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC.
 | 
        
           |  |  | 227 |  * @param string $pagetype Either my-index or user-profile.
 | 
        
           |  |  | 228 |  * @param progress_bar|null $progressbar A progress bar to update.
 | 
        
           |  |  | 229 |  * @param string $pagename Differentiate between standard /my or /courses pages.
 | 
        
           |  |  | 230 |  * @return void
 | 
        
           |  |  | 231 |  */
 | 
        
           |  |  | 232 | function my_reset_page_for_all_users(
 | 
        
           |  |  | 233 |     int $private = MY_PAGE_PRIVATE,
 | 
        
           |  |  | 234 |     string $pagetype = 'my-index',
 | 
        
           |  |  | 235 |     ?progress_bar $progressbar = null,
 | 
        
           |  |  | 236 |     string $pagename = MY_PAGE_DEFAULT
 | 
        
           |  |  | 237 | ) {
 | 
        
           |  |  | 238 |     global $DB;
 | 
        
           |  |  | 239 |   | 
        
           |  |  | 240 |     // This may take a while. Raise the execution time limit.
 | 
        
           |  |  | 241 |     core_php_time_limit::raise();
 | 
        
           |  |  | 242 |   | 
        
           |  |  | 243 |     $users = $DB->get_fieldset_select(
 | 
        
           |  |  | 244 |         'my_pages',
 | 
        
           |  |  | 245 |         'DISTINCT(userid)',
 | 
        
           |  |  | 246 |         'userid IS NOT NULL AND private = :private',
 | 
        
           |  |  | 247 |         ['private' => $private]
 | 
        
           |  |  | 248 |     );
 | 
        
           |  |  | 249 |     $chunks = array_chunk($users, 20);
 | 
        
           |  |  | 250 |   | 
        
           |  |  | 251 |     if (!empty($progressbar) && count($chunks) > 0) {
 | 
        
           |  |  | 252 |         $count = count($chunks);
 | 
        
           |  |  | 253 |         $message = get_string('inprogress');
 | 
        
           |  |  | 254 |         $progressbar->update(0, $count, $message);
 | 
        
           |  |  | 255 |     }
 | 
        
           |  |  | 256 |   | 
        
           |  |  | 257 |     foreach ($chunks as $key => $userchunk) {
 | 
        
           |  |  | 258 |         list($infragment, $inparams) = $DB->get_in_or_equal($userchunk,  SQL_PARAMS_NAMED);
 | 
        
           |  |  | 259 |         // Find all the user pages and all block instances in them.
 | 
        
           |  |  | 260 |         $sql = "SELECT bi.id
 | 
        
           |  |  | 261 |                   FROM {my_pages} p
 | 
        
           |  |  | 262 |                   JOIN {context} ctx ON ctx.instanceid = p.userid AND ctx.contextlevel = :usercontextlevel
 | 
        
           |  |  | 263 |                   JOIN {block_instances} bi ON bi.parentcontextid = ctx.id
 | 
        
           |  |  | 264 |                    AND bi.pagetypepattern = :pagetypepattern
 | 
        
           |  |  | 265 |                    AND (bi.subpagepattern IS NULL OR bi.subpagepattern = " . $DB->sql_cast_to_char('p.id') . ")
 | 
        
           |  |  | 266 |                  WHERE p.private = :private
 | 
        
           |  |  | 267 |                    AND p.name = :name
 | 
        
           |  |  | 268 |                    AND p.userid $infragment";
 | 
        
           |  |  | 269 |   | 
        
           |  |  | 270 |         $params = array_merge([
 | 
        
           |  |  | 271 |             'private' => $private,
 | 
        
           |  |  | 272 |             'usercontextlevel' => CONTEXT_USER,
 | 
        
           |  |  | 273 |             'pagetypepattern' => $pagetype,
 | 
        
           |  |  | 274 |             'name' => $pagename
 | 
        
           |  |  | 275 |         ], $inparams);
 | 
        
           |  |  | 276 |         $blockids = $DB->get_fieldset_sql($sql, $params);
 | 
        
           |  |  | 277 |   | 
        
           |  |  | 278 |         // Wrap the SQL queries in a transaction.
 | 
        
           |  |  | 279 |         $transaction = $DB->start_delegated_transaction();
 | 
        
           |  |  | 280 |   | 
        
           |  |  | 281 |         // Delete the block instances.
 | 
        
           |  |  | 282 |         if (!empty($blockids)) {
 | 
        
           |  |  | 283 |             blocks_delete_instances($blockids);
 | 
        
           |  |  | 284 |         }
 | 
        
           |  |  | 285 |   | 
        
           |  |  | 286 |         // Finally delete the pages.
 | 
        
           |  |  | 287 |         $DB->delete_records_select(
 | 
        
           |  |  | 288 |             'my_pages',
 | 
        
           |  |  | 289 |             "userid $infragment AND private = :private",
 | 
        
           |  |  | 290 |             array_merge(['private' => $private], $inparams)
 | 
        
           |  |  | 291 |         );
 | 
        
           |  |  | 292 |   | 
        
           |  |  | 293 |         // We should be good to go now.
 | 
        
           |  |  | 294 |         $transaction->allow_commit();
 | 
        
           |  |  | 295 |   | 
        
           |  |  | 296 |         if (!empty($progressbar)) {
 | 
        
           |  |  | 297 |             $progressbar->update(((int) $key + 1), $count, $message);
 | 
        
           |  |  | 298 |         }
 | 
        
           |  |  | 299 |     }
 | 
        
           |  |  | 300 |   | 
        
           |  |  | 301 |     // Trigger dashboard has been reset event.
 | 
        
           |  |  | 302 |     $eventparams = array(
 | 
        
           |  |  | 303 |         'context' => context_system::instance(),
 | 
        
           |  |  | 304 |         'other' => array(
 | 
        
           |  |  | 305 |             'private' => $private,
 | 
        
           |  |  | 306 |             'pagetype' => $pagetype,
 | 
        
           |  |  | 307 |         ),
 | 
        
           |  |  | 308 |     );
 | 
        
           |  |  | 309 |     $event = \core\event\dashboards_reset::create($eventparams);
 | 
        
           |  |  | 310 |     $event->trigger();
 | 
        
           |  |  | 311 |   | 
        
           |  |  | 312 |     if (!empty($progressbar)) {
 | 
        
           |  |  | 313 |         $progressbar->update(1, 1, get_string('completed'));
 | 
        
           |  |  | 314 |     }
 | 
        
           |  |  | 315 | }
 | 
        
           |  |  | 316 |   | 
        
           |  |  | 317 | class my_syspage_block_manager extends block_manager {
 | 
        
           |  |  | 318 |     // HACK WARNING!
 | 
        
           |  |  | 319 |     // TODO: figure out a better way to do this
 | 
        
           |  |  | 320 |     /**
 | 
        
           |  |  | 321 |      * Load blocks using the system context, rather than the user's context.
 | 
        
           |  |  | 322 |      *
 | 
        
           |  |  | 323 |      * This is needed because the My Moodle pages set the page context to the
 | 
        
           |  |  | 324 |      * user's context for access control, etc.  But the blocks for the system
 | 
        
           |  |  | 325 |      * pages are stored in the system context.
 | 
        
           |  |  | 326 |      */
 | 
        
           |  |  | 327 |     public function load_blocks($includeinvisible = null) {
 | 
        
           |  |  | 328 |         $origcontext = $this->page->context;
 | 
        
           |  |  | 329 |         $this->page->context = context_system::instance();
 | 
        
           |  |  | 330 |         parent::load_blocks($includeinvisible);
 | 
        
           |  |  | 331 |         $this->page->context = $origcontext;
 | 
        
           |  |  | 332 |     }
 | 
        
           |  |  | 333 | }
 |