Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
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 * Tour manager. 19 * 20 * @package tool_usertours 21 * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace tool_usertours; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 use tool_usertours\local\forms; 30 use tool_usertours\local\table; 31 use core\notification; 32 33 /** 34 * Tour manager. 35 * 36 * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk> 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 */ 39 class manager { 40 41 /** 42 * @var ACTION_LISTTOURS The action to get the list of tours. 43 */ 44 const ACTION_LISTTOURS = 'listtours'; 45 46 /** 47 * @var ACTION_NEWTOUR The action to create a new tour. 48 */ 49 const ACTION_NEWTOUR = 'newtour'; 50 51 /** 52 * @var ACTION_EDITTOUR The action to edit the tour. 53 */ 54 const ACTION_EDITTOUR = 'edittour'; 55 56 /** 57 * @var ACTION_MOVETOUR The action to move a tour up or down. 58 */ 59 const ACTION_MOVETOUR = 'movetour'; 60 61 /** 62 * @var ACTION_EXPORTTOUR The action to export the tour. 63 */ 64 const ACTION_EXPORTTOUR = 'exporttour'; 65 66 /** 67 * @var ACTION_IMPORTTOUR The action to import the tour. 68 */ 69 const ACTION_IMPORTTOUR = 'importtour'; 70 71 /** 72 * @var ACTION_DELETETOUR The action to delete the tour. 73 */ 74 const ACTION_DELETETOUR = 'deletetour'; 75 76 /** 77 * @var ACTION_VIEWTOUR The action to view the tour. 78 */ 79 const ACTION_VIEWTOUR = 'viewtour'; 80 81 /** 82 * @var ACTION_DUPLICATETOUR The action to duplicate the tour. 83 */ 84 const ACTION_DUPLICATETOUR = 'duplicatetour'; 85 86 /** 87 * @var ACTION_NEWSTEP The action to create a new step. 88 */ 89 const ACTION_NEWSTEP = 'newstep'; 90 91 /** 92 * @var ACTION_EDITSTEP The action to edit step configuration. 93 */ 94 const ACTION_EDITSTEP = 'editstep'; 95 96 /** 97 * @var ACTION_MOVESTEP The action to move a step up or down. 98 */ 99 const ACTION_MOVESTEP = 'movestep'; 100 101 /** 102 * @var ACTION_DELETESTEP The action to delete a step. 103 */ 104 const ACTION_DELETESTEP = 'deletestep'; 105 106 /** 107 * @var ACTION_VIEWSTEP The action to view a step. 108 */ 109 const ACTION_VIEWSTEP = 'viewstep'; 110 111 /** 112 * @var ACTION_HIDETOUR The action to hide a tour. 113 */ 114 const ACTION_HIDETOUR = 'hidetour'; 115 116 /** 117 * @var ACTION_SHOWTOUR The action to show a tour. 118 */ 119 const ACTION_SHOWTOUR = 'showtour'; 120 121 /** 122 * @var ACTION_RESETFORALL 123 */ 124 const ACTION_RESETFORALL = 'resetforall'; 125 126 /** 127 * @var CONFIG_SHIPPED_TOUR 128 */ 129 const CONFIG_SHIPPED_TOUR = 'shipped_tour'; 130 131 /** 132 * @var CONFIG_SHIPPED_FILENAME 133 */ 134 const CONFIG_SHIPPED_FILENAME = 'shipped_filename'; 135 136 /** 137 * @var CONFIG_SHIPPED_VERSION 138 */ 139 const CONFIG_SHIPPED_VERSION = 'shipped_version'; 140 141 /** 142 * Helper method to initialize admin page, setting appropriate extra URL parameters 143 * 144 * @param string $action 145 */ 146 protected function setup_admin_externalpage(string $action): void { 147 admin_externalpage_setup('tool_usertours/tours', '', array_filter([ 148 'action' => $action, 149 'id' => optional_param('id', 0, PARAM_INT), 150 'tourid' => optional_param('tourid', 0, PARAM_INT), 151 'direction' => optional_param('direction', 0, PARAM_INT), 152 ])); 153 } 154 155 /** 156 * This is the entry point for this controller class. 157 * 158 * @param string $action The action to perform. 159 */ 160 public function execute($action) { 161 $this->setup_admin_externalpage($action); 162 163 // Add the main content. 164 switch($action) { 165 case self::ACTION_NEWTOUR: 166 case self::ACTION_EDITTOUR: 167 $this->edit_tour(optional_param('id', null, PARAM_INT)); 168 break; 169 170 case self::ACTION_MOVETOUR: 171 $this->move_tour(required_param('id', PARAM_INT)); 172 break; 173 174 case self::ACTION_EXPORTTOUR: 175 $this->export_tour(required_param('id', PARAM_INT)); 176 break; 177 178 case self::ACTION_IMPORTTOUR: 179 $this->import_tour(); 180 break; 181 182 case self::ACTION_VIEWTOUR: 183 $this->view_tour(required_param('id', PARAM_INT)); 184 break; 185 186 case self::ACTION_DUPLICATETOUR: 187 $this->duplicate_tour(required_param('id', PARAM_INT)); 188 break; 189 190 case self::ACTION_HIDETOUR: 191 $this->hide_tour(required_param('id', PARAM_INT)); 192 break; 193 194 case self::ACTION_SHOWTOUR: 195 $this->show_tour(required_param('id', PARAM_INT)); 196 break; 197 198 case self::ACTION_DELETETOUR: 199 $this->delete_tour(required_param('id', PARAM_INT)); 200 break; 201 202 case self::ACTION_RESETFORALL: 203 $this->reset_tour_for_all(required_param('id', PARAM_INT)); 204 break; 205 206 case self::ACTION_NEWSTEP: 207 case self::ACTION_EDITSTEP: 208 $this->edit_step(optional_param('id', null, PARAM_INT)); 209 break; 210 211 case self::ACTION_MOVESTEP: 212 $this->move_step(required_param('id', PARAM_INT)); 213 break; 214 215 case self::ACTION_DELETESTEP: 216 $this->delete_step(required_param('id', PARAM_INT)); 217 break; 218 219 case self::ACTION_LISTTOURS: 220 default: 221 $this->print_tour_list(); 222 break; 223 } 224 } 225 226 /** 227 * Print out the page header. 228 * 229 * @param string $title The title to display. 230 */ 231 protected function header($title = null) { 232 global $OUTPUT; 233 234 // Print the page heading. 235 echo $OUTPUT->header(); 236 237 if ($title === null) { 238 $title = get_string('tours', 'tool_usertours'); 239 } 240 241 echo $OUTPUT->heading($title); 242 } 243 244 /** 245 * Print out the page footer. 246 * 247 * @return void 248 */ 249 protected function footer() { 250 global $OUTPUT; 251 252 echo $OUTPUT->footer(); 253 } 254 255 /** 256 * Print the the list of tours. 257 */ 258 protected function print_tour_list() { 259 global $PAGE, $OUTPUT; 260 261 $this->header(); 262 echo \html_writer::span(get_string('tourlist_explanation', 'tool_usertours')); 263 $table = new table\tour_list(); 264 $tours = helper::get_tours(); 265 foreach ($tours as $tour) { 266 $table->add_data_keyed($table->format_row($tour)); 267 } 268 269 $table->finish_output(); 270 $actions = [ 271 (object) [ 272 'link' => helper::get_edit_tour_link(), 273 'linkproperties' => [], 274 'img' => 'b/tour-new', 275 'title' => get_string('newtour', 'tool_usertours'), 276 ], 277 (object) [ 278 'link' => helper::get_import_tour_link(), 279 'linkproperties' => [], 280 'img' => 'b/tour-import', 281 'title' => get_string('importtour', 'tool_usertours'), 282 ], 283 (object) [ 284 'link' => new \moodle_url('https://archive.moodle.net/tours'), 285 'linkproperties' => [ 286 'target' => '_blank', 287 ], 288 'img' => 'b/tour-shared', 289 'title' => get_string('sharedtourslink', 'tool_usertours'), 290 ], 291 ]; 292 293 echo \html_writer::start_tag('div', [ 294 'class' => 'tour-actions', 295 ]); 296 297 echo \html_writer::start_tag('ul'); 298 foreach ($actions as $config) { 299 $action = \html_writer::start_tag('li'); 300 $linkproperties = $config->linkproperties; 301 $linkproperties['href'] = $config->link; 302 $action .= \html_writer::start_tag('a', $linkproperties); 303 $action .= $OUTPUT->pix_icon($config->img, $config->title, 'tool_usertours'); 304 $action .= \html_writer::div($config->title); 305 $action .= \html_writer::end_tag('a'); 306 $action .= \html_writer::end_tag('li'); 307 echo $action; 308 } 309 echo \html_writer::end_tag('ul'); 310 echo \html_writer::end_tag('div'); 311 312 // JS for Tour management. 313 $PAGE->requires->js_call_amd('tool_usertours/managetours', 'setup'); 314 $this->footer(); 315 } 316 317 /** 318 * Return the edit tour link. 319 * 320 * @param int $id The ID of the tour 321 * @return string 322 */ 323 protected function get_edit_tour_link($id = null) { 324 $addlink = helper::get_edit_tour_link($id); 325 return \html_writer::link($addlink, get_string('newtour', 'tool_usertours')); 326 } 327 328 /** 329 * Print the edit tour link. 330 * 331 * @param int $id The ID of the tour 332 */ 333 protected function print_edit_tour_link($id = null) { 334 echo $this->get_edit_tour_link($id); 335 } 336 337 /** 338 * Get the import tour link. 339 * 340 * @return string 341 */ 342 protected function get_import_tour_link() { 343 $importlink = helper::get_import_tour_link(); 344 return \html_writer::link($importlink, get_string('importtour', 'tool_usertours')); 345 } 346 347 /** 348 * Print the edit tour page. 349 * 350 * @param int $id The ID of the tour 351 */ 352 protected function edit_tour($id = null) { 353 global $PAGE; 354 if ($id) { 355 $tour = tour::instance($id); 356 $PAGE->navbar->add($tour->get_name(), $tour->get_edit_link()); 357 358 } else { 359 $tour = new tour(); 360 $PAGE->navbar->add(get_string('newtour', 'tool_usertours'), $tour->get_edit_link()); 361 } 362 363 $form = new forms\edittour($tour); 364 365 if ($form->is_cancelled()) { 366 redirect(helper::get_list_tour_link()); 367 } else if ($data = $form->get_data()) { 368 // Creating a new tour. 369 $tour->set_name($data->name); 370 $tour->set_description($data->description); 371 $tour->set_pathmatch($data->pathmatch); 372 $tour->set_enabled(!empty($data->enabled)); 373 374 foreach (configuration::get_defaultable_keys() as $key) { 375 $tour->set_config($key, $data->$key); 376 } 377 378 // Save filter values. 379 foreach (helper::get_all_filters() as $filterclass) { 380 $filterclass::save_filter_values_from_form($tour, $data); 381 } 382 383 $tour->persist(); 384 385 redirect(helper::get_list_tour_link()); 386 } else { 387 if (empty($tour)) { 388 $this->header('newtour'); 389 } else { 390 if (!empty($tour->get_config(self::CONFIG_SHIPPED_TOUR))) { 391 notification::add(get_string('modifyshippedtourwarning', 'tool_usertours'), notification::WARNING); 392 } 393 394 $this->header($tour->get_name()); 395 $data = $tour->prepare_data_for_form(); 396 397 // Prepare filter values for the form. 398 foreach (helper::get_all_filters() as $filterclass) { 399 $filterclass::prepare_filter_values_for_form($tour, $data); 400 } 401 $form->set_data($data); 402 } 403 404 $form->display(); 405 $this->footer(); 406 } 407 } 408 409 /** 410 * Print the export tour page. 411 * 412 * @param int $id The ID of the tour 413 */ 414 protected function export_tour($id) { 415 $tour = tour::instance($id); 416 417 // Grab the full data record. 418 $export = $tour->to_record(); 419 420 // Remove the id. 421 unset($export->id); 422 423 // Set the version. 424 $export->version = get_config('tool_usertours', 'version'); 425 426 // Step export. 427 $export->steps = []; 428 foreach ($tour->get_steps() as $step) { 429 $record = $step->to_record(); 430 unset($record->id); 431 unset($record->tourid); 432 433 $export->steps[] = $record; 434 } 435 436 $exportstring = json_encode($export); 437 438 $filename = 'tour_export_' . $tour->get_id() . '_' . time() . '.json'; 439 440 // Force download. 441 send_file($exportstring, $filename, 0, 0, true, true); 442 } 443 444 /** 445 * Handle tour import. 446 */ 447 protected function import_tour() { 448 global $PAGE; 449 $PAGE->navbar->add(get_string('importtour', 'tool_usertours'), helper::get_import_tour_link()); 450 451 $form = new forms\importtour(); 452 453 if ($form->is_cancelled()) { 454 redirect(helper::get_list_tour_link()); 455 } else if ($form->get_data()) { 456 // Importing a tour. 457 $tourconfigraw = $form->get_file_content('tourconfig'); 458 $tour = self::import_tour_from_json($tourconfigraw); 459 460 redirect($tour->get_view_link()); 461 } else { 462 $this->header(); 463 $form->display(); 464 $this->footer(); 465 } 466 } 467 468 /** 469 * Print the view tour page. 470 * 471 * @param int $tourid The ID of the tour to display. 472 */ 473 protected function view_tour($tourid) { 474 global $PAGE; 475 $tour = helper::get_tour($tourid); 476 477 $PAGE->navbar->add($tour->get_name(), $tour->get_view_link()); 478 479 $this->header($tour->get_name()); 480 echo \html_writer::span(get_string('viewtour_info', 'tool_usertours', [ 481 'tourname' => $tour->get_name(), 482 'path' => $tour->get_pathmatch(), 483 ])); 484 echo \html_writer::div(get_string('viewtour_edit', 'tool_usertours', [ 485 'editlink' => $tour->get_edit_link()->out(), 486 'resetlink' => $tour->get_reset_link()->out(), 487 ])); 488 489 $table = new table\step_list($tourid); 490 foreach ($tour->get_steps() as $step) { 491 $table->add_data_keyed($table->format_row($step)); 492 } 493 494 $table->finish_output(); 495 $this->print_edit_step_link($tourid); 496 497 // JS for Step management. 498 $PAGE->requires->js_call_amd('tool_usertours/managesteps', 'setup'); 499 500 $this->footer(); 501 } 502 503 /** 504 * Duplicate an existing tour. 505 * 506 * @param int $tourid The ID of the tour to duplicate. 507 */ 508 protected function duplicate_tour($tourid) { 509 $tour = helper::get_tour($tourid); 510 511 $export = $tour->to_record(); 512 // Remove the id. 513 unset($export->id); 514 515 // Set the version. 516 $export->version = get_config('tool_usertours', 'version'); 517 518 $export->name = get_string('duplicatetour_name', 'tool_usertours', $export->name); 519 520 // Step export. 521 $export->steps = []; 522 foreach ($tour->get_steps() as $step) { 523 $record = $step->to_record(); 524 unset($record->id); 525 unset($record->tourid); 526 527 $export->steps[] = $record; 528 } 529 530 $exportstring = json_encode($export); 531 $newtour = self::import_tour_from_json($exportstring); 532 533 redirect($newtour->get_view_link()); 534 } 535 536 /** 537 * Show the tour. 538 * 539 * @param int $tourid The ID of the tour to display. 540 */ 541 protected function show_tour($tourid) { 542 $this->show_hide_tour($tourid, 1); 543 } 544 545 /** 546 * Hide the tour. 547 * 548 * @param int $tourid The ID of the tour to display. 549 */ 550 protected function hide_tour($tourid) { 551 $this->show_hide_tour($tourid, 0); 552 } 553 554 /** 555 * Show or Hide the tour. 556 * 557 * @param int $tourid The ID of the tour to display. 558 * @param int $visibility The intended visibility. 559 */ 560 protected function show_hide_tour($tourid, $visibility) { 561 global $DB; 562 563 require_sesskey(); 564 565 $tour = $DB->get_record('tool_usertours_tours', array('id' => $tourid)); 566 $tour->enabled = $visibility; 567 $DB->update_record('tool_usertours_tours', $tour); 568 569 redirect(helper::get_list_tour_link()); 570 } 571 572 /** 573 * Delete the tour. 574 * 575 * @param int $tourid The ID of the tour to remove. 576 */ 577 protected function delete_tour($tourid) { 578 require_sesskey(); 579 580 $tour = tour::instance($tourid); 581 $tour->remove(); 582 583 redirect(helper::get_list_tour_link()); 584 } 585 586 /** 587 * Reset the tour state for all users. 588 * 589 * @param int $tourid The ID of the tour to remove. 590 */ 591 protected function reset_tour_for_all($tourid) { 592 require_sesskey(); 593 594 $tour = tour::instance($tourid); 595 $tour->mark_major_change(); 596 597 redirect(helper::get_view_tour_link($tourid), get_string('tour_resetforall', 'tool_usertours')); 598 } 599 600 /** 601 * Get all tours for the current page URL. 602 * 603 * @param bool $reset Forcibly update the current tours 604 * @return array 605 */ 606 public static function get_current_tours($reset = false): array { 607 global $PAGE; 608 609 static $tours = false; 610 611 if ($tours === false || $reset) { 612 $tours = self::get_matching_tours($PAGE->url); 613 } 614 615 return $tours; 616 } 617 618 /** 619 * Get all tours matching the specified URL. 620 * 621 * @param moodle_url $pageurl The URL to match. 622 * @return array 623 */ 624 public static function get_matching_tours(\moodle_url $pageurl): array { 625 global $PAGE, $USER; 626 627 // The following three checks make sure that the user is fully ready to use the site. If not, we do not show any tours. 628 // We need the user to get properly set up so that all require_login() and other bits work as expected. 629 630 if (user_not_fully_set_up($USER)) { 631 return []; 632 } 633 634 if (get_user_preferences('auth_forcepasswordchange', false)) { 635 return []; 636 } 637 638 if (empty($USER->policyagreed) && !is_siteadmin()) { 639 $manager = new \core_privacy\local\sitepolicy\manager(); 640 641 if ($manager->is_defined(isguestuser())) { 642 return []; 643 } 644 } 645 646 $tours = cache::get_matching_tourdata($pageurl); 647 648 $matches = []; 649 if ($tours) { 650 $filters = helper::get_all_filters(); 651 foreach ($tours as $record) { 652 $tour = tour::load_from_record($record); 653 if ($tour->is_enabled() && $tour->matches_all_filters($PAGE->context, $filters)) { 654 $matches[] = $tour; 655 } 656 } 657 } 658 659 return $matches; 660 } 661 662 /** 663 * Import the provided tour JSON. 664 * 665 * @param string $json The tour configuration. 666 * @return tour 667 */ 668 public static function import_tour_from_json($json) { 669 $tourconfig = json_decode($json); 670 671 // We do not use this yet - we may do in the future. 672 unset($tourconfig->version); 673 674 $steps = $tourconfig->steps; 675 unset($tourconfig->steps); 676 677 $tourconfig->id = null; 678 $tourconfig->sortorder = null; 679 $tour = tour::load_from_record($tourconfig, true); 680 $tour->persist(true); 681 682 // Ensure that steps are orderered by their sortorder. 683 \core_collator::asort_objects_by_property($steps, 'sortorder', \core_collator::SORT_NUMERIC); 684 685 foreach ($steps as $stepconfig) { 686 $stepconfig->id = null; 687 $stepconfig->tourid = $tour->get_id(); 688 $step = step::load_from_record($stepconfig, true); 689 $step->persist(true); 690 } 691 692 return $tour; 693 } 694 695 /** 696 * Helper to fetch the renderer. 697 * 698 * @return renderer 699 */ 700 protected function get_renderer() { 701 global $PAGE; 702 return $PAGE->get_renderer('tool_usertours'); 703 } 704 705 /** 706 * Print the edit step link. 707 * 708 * @param int $tourid The ID of the tour. 709 * @param int $stepid The ID of the step. 710 * @return string 711 */ 712 protected function print_edit_step_link($tourid, $stepid = null) { 713 $addlink = helper::get_edit_step_link($tourid, $stepid); 714 $attributes = []; 715 if (empty($stepid)) { 716 $attributes['class'] = 'createstep'; 717 } 718 echo \html_writer::link($addlink, get_string('newstep', 'tool_usertours'), $attributes); 719 } 720 721 /** 722 * Display the edit step form for the specified step. 723 * 724 * @param int $id The step to edit. 725 */ 726 protected function edit_step($id) { 727 global $PAGE; 728 729 if (isset($id)) { 730 $step = step::instance($id); 731 } else { 732 $step = new step(); 733 $step->set_tourid(required_param('tourid', PARAM_INT)); 734 } 735 736 $tour = $step->get_tour(); 737 738 if (!empty($tour->get_config(self::CONFIG_SHIPPED_TOUR))) { 739 notification::add(get_string('modifyshippedtourwarning', 'tool_usertours'), notification::WARNING); 740 } 741 742 $PAGE->navbar->add($tour->get_name(), $tour->get_view_link()); 743 if (isset($id)) { 744 $PAGE->navbar->add($step->get_title(), $step->get_edit_link()); 745 } else { 746 $PAGE->navbar->add(get_string('newstep', 'tool_usertours'), $step->get_edit_link()); 747 } 748 749 $form = new forms\editstep($step->get_edit_link(), $step); 750 if ($form->is_cancelled()) { 751 redirect($step->get_tour()->get_view_link()); 752 } else if ($data = $form->get_data()) { 753 $step->handle_form_submission($form, $data); 754 $step->get_tour()->reset_step_sortorder(); 755 redirect($step->get_tour()->get_view_link()); 756 } else { 757 if (empty($id)) { 758 $this->header(get_string('newstep', 'tool_usertours')); 759 } else { 760 $this->header(get_string('editstep', 'tool_usertours', $step->get_title())); 761 } 762 $form->set_data($step->prepare_data_for_form()); 763 764 $form->display(); 765 $this->footer(); 766 } 767 } 768 769 /** 770 * Move a tour up or down and redirect once complete. 771 * 772 * @param int $id The tour to move. 773 */ 774 protected function move_tour($id) { 775 require_sesskey(); 776 777 $direction = required_param('direction', PARAM_INT); 778 779 $tour = tour::instance($id); 780 self::_move_tour($tour, $direction); 781 782 redirect(helper::get_list_tour_link()); 783 } 784 785 /** 786 * Move a tour up or down. 787 * 788 * @param tour $tour The tour to move. 789 * 790 * @param int $direction 791 */ 792 protected static function _move_tour(tour $tour, $direction) { 793 // We can't move the first tour higher, nor the last tour any lower. 794 if (($tour->is_first_tour() && $direction == helper::MOVE_UP) || 795 ($tour->is_last_tour() && $direction == helper::MOVE_DOWN)) { 796 797 return; 798 } 799 800 $currentsortorder = $tour->get_sortorder(); 801 $targetsortorder = $currentsortorder + $direction; 802 803 $swapwith = helper::get_tour_from_sortorder($targetsortorder); 804 805 // Set the sort order to something out of the way. 806 $tour->set_sortorder(-1); 807 $tour->persist(); 808 809 // Swap the two sort orders. 810 $swapwith->set_sortorder($currentsortorder); 811 $swapwith->persist(); 812 813 $tour->set_sortorder($targetsortorder); 814 $tour->persist(); 815 } 816 817 /** 818 * Move a step up or down. 819 * 820 * @param int $id The step to move. 821 */ 822 protected function move_step($id) { 823 require_sesskey(); 824 825 $direction = required_param('direction', PARAM_INT); 826 827 $step = step::instance($id); 828 $currentsortorder = $step->get_sortorder(); 829 $targetsortorder = $currentsortorder + $direction; 830 831 $tour = $step->get_tour(); 832 $swapwith = helper::get_step_from_sortorder($tour->get_id(), $targetsortorder); 833 834 // Set the sort order to something out of the way. 835 $step->set_sortorder(-1); 836 $step->persist(); 837 838 // Swap the two sort orders. 839 $swapwith->set_sortorder($currentsortorder); 840 $swapwith->persist(); 841 842 $step->set_sortorder($targetsortorder); 843 $step->persist(); 844 845 // Reset the sort order. 846 $tour->reset_step_sortorder(); 847 redirect($tour->get_view_link()); 848 } 849 850 /** 851 * Delete the step. 852 * 853 * @param int $stepid The ID of the step to remove. 854 */ 855 protected function delete_step($stepid) { 856 require_sesskey(); 857 858 $step = step::instance($stepid); 859 $tour = $step->get_tour(); 860 861 $step->remove(); 862 redirect($tour->get_view_link()); 863 } 864 865 /** 866 * Make sure all of the default tours that are shipped with Moodle are created 867 * and up to date with the latest version. 868 */ 869 public static function update_shipped_tours() { 870 global $DB, $CFG; 871 872 // A list of tours that are shipped with Moodle. They are in 873 // the format filename => version. The version value needs to 874 // be increased if the tour has been updated. 875 $shippedtours = [ 876 '311_activity_information_activity_page_student.json' => 2, 877 '311_activity_information_activity_page_teacher.json' => 2, 878 '311_activity_information_course_page_student.json' => 2, 879 '311_activity_information_course_page_teacher.json' => 2 880 ]; 881 882 // These are tours that we used to ship but don't ship any longer. 883 // We do not remove them, but we do disable them. 884 $unshippedtours = [ 885 // Formerly included in Moodle 3.2.0. 886 'boost_administrator.json' => 1, 887 'boost_course_view.json' => 1, 888 889 // Formerly included in Moodle 3.6.0. 890 '36_dashboard.json' => 3, 891 '36_messaging.json' => 3, 892 ]; 893 894 $existingtourrecords = $DB->get_recordset('tool_usertours_tours'); 895 896 // Get all of the existing shipped tours and check if they need to be 897 // updated. 898 foreach ($existingtourrecords as $tourrecord) { 899 $tour = tour::load_from_record($tourrecord); 900 901 if (!empty($tour->get_config(self::CONFIG_SHIPPED_TOUR))) { 902 $filename = $tour->get_config(self::CONFIG_SHIPPED_FILENAME); 903 $version = $tour->get_config(self::CONFIG_SHIPPED_VERSION); 904 905 // If we know about this tour (otherwise leave it as is). 906 if (isset($shippedtours[$filename])) { 907 // And the version in the DB is an older version. 908 if ($version < $shippedtours[$filename]) { 909 // Remove the old version because it's been updated 910 // and needs to be recreated. 911 $tour->remove(); 912 } else { 913 // The tour has not been updated so we don't need to 914 // do anything with it. 915 unset($shippedtours[$filename]); 916 } 917 } 918 919 if (isset($unshippedtours[$filename])) { 920 if ($version <= $unshippedtours[$filename]) { 921 $tour = tour::instance($tour->get_id()); 922 $tour->set_enabled(tour::DISABLED); 923 $tour->persist(); 924 } 925 } 926 } 927 } 928 $existingtourrecords->close(); 929 930 // Ensure we correct the sortorder in any existing tours, prior to adding latest shipped tours. 931 helper::reset_tour_sortorder(); 932 933 foreach (array_reverse($shippedtours) as $filename => $version) { 934 $filepath = $CFG->dirroot . "/{$CFG->admin}/tool/usertours/tours/" . $filename; 935 $tourjson = file_get_contents($filepath); 936 $tour = self::import_tour_from_json($tourjson); 937 938 // Set some additional config data to record that this tour was 939 // added as a shipped tour. 940 $tour->set_config(self::CONFIG_SHIPPED_TOUR, true); 941 $tour->set_config(self::CONFIG_SHIPPED_FILENAME, $filename); 942 $tour->set_config(self::CONFIG_SHIPPED_VERSION, $version); 943 944 // Bump new tours to the top of the list. 945 while ($tour->get_sortorder() > 0) { 946 self::_move_tour($tour, helper::MOVE_UP); 947 } 948 949 if (defined('BEHAT_SITE_RUNNING') || (defined('PHPUNIT_TEST') && PHPUNIT_TEST)) { 950 // Disable this tour if this is behat or phpunit. 951 $tour->set_enabled(false); 952 } 953 954 $tour->persist(); 955 } 956 } 957 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body