PageRenderTime 36ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/my/lib.php

https://bitbucket.org/moodle/moodle
PHP | 314 lines | 196 code | 33 blank | 85 comment | 21 complexity | a79ee0b403993c4da7e2c986db817926 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0
  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. * My Moodle -- a user's personal dashboard
  18. *
  19. * This file contains common functions for the dashboard and profile pages.
  20. *
  21. * @package moodlecore
  22. * @subpackage my
  23. * @copyright 2010 Remote-Learner.net
  24. * @author Hubert Chathi <hubert@remote-learner.net>
  25. * @author Olav Jordan <olav.jordan@remote-learner.net>
  26. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  27. */
  28. define('MY_PAGE_PUBLIC', 0);
  29. define('MY_PAGE_PRIVATE', 1);
  30. define('MY_PAGE_DEFAULT', '__default');
  31. define('MY_PAGE_COURSES', '__courses');
  32. require_once("$CFG->libdir/blocklib.php");
  33. /**
  34. * For a given user, this returns the $page information for their My Moodle page
  35. *
  36. * @param int|null $userid the id of the user whose page should be retrieved
  37. * @param int|null $private either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC
  38. * @param string|null $pagename Differentiate between standard /my or /courses pages.
  39. */
  40. function my_get_page(?int $userid, int $private = MY_PAGE_PRIVATE, string $pagename = MY_PAGE_DEFAULT) {
  41. global $DB, $CFG;
  42. if (empty($CFG->forcedefaultmymoodle) && $userid) { // Ignore custom My Moodle pages if admin has forced them
  43. // Does the user have their own page defined? If so, return it.
  44. if ($customised = $DB->get_record(
  45. 'my_pages',
  46. array('userid' => $userid, 'private' => $private, 'name' => $pagename),
  47. '*',
  48. IGNORE_MULTIPLE
  49. )) {
  50. return $customised;
  51. }
  52. }
  53. // Otherwise return the system default page
  54. return $DB->get_record('my_pages', array('userid' => null, 'name' => $pagename, 'private' => $private), '*', IGNORE_MULTIPLE);
  55. }
  56. /**
  57. * This copies a system default page to the current user
  58. *
  59. * @param int $userid the id of the user whose page should be reset
  60. * @param int $private either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC
  61. * @param string $pagetype either my-index or user-profile
  62. * @param string $pagename Differentiate between standard /my or /courses pages.
  63. */
  64. function my_copy_page(
  65. int $userid,
  66. int $private = MY_PAGE_PRIVATE,
  67. string $pagetype = 'my-index',
  68. string $pagename = MY_PAGE_DEFAULT
  69. ) {
  70. global $DB;
  71. if ($customised = $DB->get_record(
  72. 'my_pages',
  73. array('userid' => $userid, 'name' => $pagename, 'private' => $private),
  74. '*',
  75. IGNORE_MULTIPLE
  76. )) {
  77. return $customised; // We're done!
  78. }
  79. // Get the system default page
  80. if (!$systempage = $DB->get_record(
  81. 'my_pages',
  82. array('userid' => null, 'name' => $pagename, 'private' => $private),
  83. '*',
  84. IGNORE_MULTIPLE
  85. )) {
  86. return false; // error
  87. }
  88. // Clone the basic system page record
  89. $page = clone($systempage);
  90. unset($page->id);
  91. $page->userid = $userid;
  92. $page->id = $DB->insert_record('my_pages', $page);
  93. // Clone ALL the associated blocks as well
  94. $systemcontext = context_system::instance();
  95. $usercontext = context_user::instance($userid);
  96. $blockinstances = $DB->get_records('block_instances', array('parentcontextid' => $systemcontext->id,
  97. 'pagetypepattern' => $pagetype,
  98. 'subpagepattern' => $systempage->id));
  99. $newblockinstanceids = [];
  100. foreach ($blockinstances as $instance) {
  101. $originalid = $instance->id;
  102. unset($instance->id);
  103. $instance->parentcontextid = $usercontext->id;
  104. $instance->subpagepattern = $page->id;
  105. $instance->timecreated = time();
  106. $instance->timemodified = $instance->timecreated;
  107. $instance->id = $DB->insert_record('block_instances', $instance);
  108. $newblockinstanceids[$originalid] = $instance->id;
  109. $blockcontext = context_block::instance($instance->id); // Just creates the context record
  110. $block = block_instance($instance->blockname, $instance);
  111. if (!$block->instance_copy($originalid)) {
  112. debugging("Unable to copy block-specific data for original block instance: $originalid
  113. to new block instance: $instance->id", DEBUG_DEVELOPER);
  114. }
  115. }
  116. // Clone block position overrides.
  117. if ($blockpositions = $DB->get_records('block_positions',
  118. ['subpage' => $systempage->id, 'pagetype' => $pagetype, 'contextid' => $systemcontext->id])) {
  119. foreach ($blockpositions as &$positions) {
  120. $positions->subpage = $page->id;
  121. $positions->contextid = $usercontext->id;
  122. if (array_key_exists($positions->blockinstanceid, $newblockinstanceids)) {
  123. // For block instances that were defined on the default dashboard and copied to the user dashboard
  124. // use the new blockinstanceid.
  125. $positions->blockinstanceid = $newblockinstanceids[$positions->blockinstanceid];
  126. }
  127. unset($positions->id);
  128. }
  129. $DB->insert_records('block_positions', $blockpositions);
  130. }
  131. return $page;
  132. }
  133. /**
  134. * For a given user, this deletes their My Moodle page and returns them to the system default.
  135. *
  136. * @param int $userid the id of the user whose page should be reset
  137. * @param int $private either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC
  138. * @param string $pagetype either my-index or user-profile
  139. * @param string $pagename Differentiate between standard /my or /courses pages.
  140. * @return mixed system page, or false on error
  141. */
  142. function my_reset_page(
  143. int $userid,
  144. int $private = MY_PAGE_PRIVATE,
  145. string $pagetype='my-index',
  146. string $pagename = MY_PAGE_DEFAULT
  147. ) {
  148. global $DB, $CFG;
  149. $page = my_get_page($userid, $private, $pagename);
  150. if ($page->userid == $userid) {
  151. $context = context_user::instance($userid);
  152. if ($blocks = $DB->get_records('block_instances', array('parentcontextid' => $context->id,
  153. 'pagetypepattern' => $pagetype))) {
  154. foreach ($blocks as $block) {
  155. if (is_null($block->subpagepattern) || $block->subpagepattern == $page->id) {
  156. blocks_delete_instance($block);
  157. }
  158. }
  159. }
  160. $DB->delete_records('block_positions', ['subpage' => $page->id, 'pagetype' => $pagetype, 'contextid' => $context->id]);
  161. $DB->delete_records('my_pages', array('id' => $page->id, 'name' => $pagename));
  162. }
  163. // Get the system default page
  164. if (!$systempage = $DB->get_record(
  165. 'my_pages',
  166. array('userid' => null, 'name' => $pagename, 'private' => $private),
  167. '*',
  168. IGNORE_MULTIPLE
  169. )) {
  170. return false; // error
  171. }
  172. // Trigger dashboard has been reset event.
  173. $eventparams = array(
  174. 'context' => context_user::instance($userid),
  175. 'other' => array(
  176. 'private' => $private,
  177. 'pagetype' => $pagetype,
  178. ),
  179. );
  180. $event = \core\event\dashboard_reset::create($eventparams);
  181. $event->trigger();
  182. return $systempage;
  183. }
  184. /**
  185. * Resets the page customisations for all users.
  186. *
  187. * @param int $private Either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC.
  188. * @param string $pagetype Either my-index or user-profile.
  189. * @param progress_bar|null $progressbar A progress bar to update.
  190. * @param string $pagename Differentiate between standard /my or /courses pages.
  191. * @return void
  192. */
  193. function my_reset_page_for_all_users(
  194. int $private = MY_PAGE_PRIVATE,
  195. string $pagetype = 'my-index',
  196. ?progress_bar $progressbar = null,
  197. string $pagename = MY_PAGE_DEFAULT
  198. ) {
  199. global $DB;
  200. // This may take a while. Raise the execution time limit.
  201. core_php_time_limit::raise();
  202. $users = $DB->get_fieldset_select(
  203. 'my_pages',
  204. 'DISTINCT(userid)',
  205. 'userid IS NOT NULL AND private = :private',
  206. ['private' => $private]
  207. );
  208. $chunks = array_chunk($users, 20);
  209. if (!empty($progressbar) && count($chunks) > 0) {
  210. $count = count($chunks);
  211. $message = get_string('inprogress');
  212. $progressbar->update(0, $count, $message);
  213. }
  214. foreach ($chunks as $key => $userchunk) {
  215. list($infragment, $inparams) = $DB->get_in_or_equal($userchunk, SQL_PARAMS_NAMED);
  216. // Find all the user pages and all block instances in them.
  217. $sql = "SELECT bi.id
  218. FROM {my_pages} p
  219. JOIN {context} ctx ON ctx.instanceid = p.userid AND ctx.contextlevel = :usercontextlevel
  220. JOIN {block_instances} bi ON bi.parentcontextid = ctx.id
  221. AND bi.pagetypepattern = :pagetypepattern
  222. AND (bi.subpagepattern IS NULL OR bi.subpagepattern = " . $DB->sql_concat("''", 'p.id') . ")
  223. WHERE p.private = :private
  224. AND p.name = :name
  225. AND p.userid $infragment";
  226. $params = array_merge([
  227. 'private' => $private,
  228. 'usercontextlevel' => CONTEXT_USER,
  229. 'pagetypepattern' => $pagetype,
  230. 'name' => $pagename
  231. ], $inparams);
  232. $blockids = $DB->get_fieldset_sql($sql, $params);
  233. // Wrap the SQL queries in a transaction.
  234. $transaction = $DB->start_delegated_transaction();
  235. // Delete the block instances.
  236. if (!empty($blockids)) {
  237. blocks_delete_instances($blockids);
  238. }
  239. // Finally delete the pages.
  240. $DB->delete_records_select(
  241. 'my_pages',
  242. "userid $infragment AND private = :private",
  243. array_merge(['private' => $private], $inparams)
  244. );
  245. // We should be good to go now.
  246. $transaction->allow_commit();
  247. if (!empty($progressbar)) {
  248. $progressbar->update(((int) $key + 1), $count, $message);
  249. }
  250. }
  251. // Trigger dashboard has been reset event.
  252. $eventparams = array(
  253. 'context' => context_system::instance(),
  254. 'other' => array(
  255. 'private' => $private,
  256. 'pagetype' => $pagetype,
  257. ),
  258. );
  259. $event = \core\event\dashboards_reset::create($eventparams);
  260. $event->trigger();
  261. if (!empty($progressbar)) {
  262. $progressbar->update(1, 1, get_string('completed'));
  263. }
  264. }
  265. class my_syspage_block_manager extends block_manager {
  266. // HACK WARNING!
  267. // TODO: figure out a better way to do this
  268. /**
  269. * Load blocks using the system context, rather than the user's context.
  270. *
  271. * This is needed because the My Moodle pages set the page context to the
  272. * user's context for access control, etc. But the blocks for the system
  273. * pages are stored in the system context.
  274. */
  275. public function load_blocks($includeinvisible = null) {
  276. $origcontext = $this->page->context;
  277. $this->page->context = context_system::instance();
  278. parent::load_blocks($includeinvisible);
  279. $this->page->context = $origcontext;
  280. }
  281. }