MDL-56595 tool_usertours: set page params when configuring tours/steps.
[moodle.git] / admin / tool / usertours / classes / manager.php
CommitLineData
001fc061
AN
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
25namespace tool_usertours;
26
27defined('MOODLE_INTERNAL') || die();
28
29use tool_usertours\local\forms;
30use tool_usertours\local\table;
3eb3d916 31use core\notification;
001fc061
AN
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 */
39class 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
c48fbb6c
MM
81 /**
82 * @var ACTION_DUPLICATETOUR The action to duplicate the tour.
83 */
84 const ACTION_DUPLICATETOUR = 'duplicatetour';
85
001fc061
AN
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
3eb3d916
RW
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
863fb96f
PH
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
001fc061
AN
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) {
863fb96f
PH
161 $this->setup_admin_externalpage($action);
162
001fc061
AN
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
c48fbb6c
MM
186 case self::ACTION_DUPLICATETOUR:
187 $this->duplicate_tour(required_param('id', PARAM_INT));
188 break;
189
001fc061
AN
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) [
3d2aa2eb 284 'link' => new \moodle_url('https://archive.moodle.net/tours'),
001fc061
AN
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);
663640f5 303 $action .= $OUTPUT->pix_icon($config->img, $config->title, 'tool_usertours');
001fc061
AN
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 {
3eb3d916
RW
390 if (!empty($tour->get_config(self::CONFIG_SHIPPED_TOUR))) {
391 notification::add(get_string('modifyshippedtourwarning', 'tool_usertours'), notification::WARNING);
392 }
393
001fc061
AN
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 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
442 header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
443 header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . 'GMT');
444 header('Pragma: no-cache');
445 header('Accept-Ranges: none');
446 header('Content-disposition: attachment; filename=' . $filename);
447 header('Content-length: ' . strlen($exportstring));
448 header('Content-type: text/calendar; charset=utf-8');
449
450 echo $exportstring;
451 die;
452 }
453
454 /**
455 * Handle tour import.
456 */
457 protected function import_tour() {
458 global $PAGE;
459 $PAGE->navbar->add(get_string('importtour', 'tool_usertours'), helper::get_import_tour_link());
460
461 $form = new forms\importtour();
462
463 if ($form->is_cancelled()) {
464 redirect(helper::get_list_tour_link());
465 } else if ($form->get_data()) {
466 // Importing a tour.
467 $tourconfigraw = $form->get_file_content('tourconfig');
468 $tour = self::import_tour_from_json($tourconfigraw);
469
470 redirect($tour->get_view_link());
471 } else {
472 $this->header();
473 $form->display();
474 $this->footer();
475 }
476 }
477
478 /**
479 * Print the view tour page.
480 *
481 * @param int $tourid The ID of the tour to display.
482 */
483 protected function view_tour($tourid) {
484 global $PAGE;
485 $tour = helper::get_tour($tourid);
486
487 $PAGE->navbar->add($tour->get_name(), $tour->get_view_link());
488
489 $this->header($tour->get_name());
490 echo \html_writer::span(get_string('viewtour_info', 'tool_usertours', [
491 'tourname' => $tour->get_name(),
492 'path' => $tour->get_pathmatch(),
493 ]));
494 echo \html_writer::div(get_string('viewtour_edit', 'tool_usertours', [
495 'editlink' => $tour->get_edit_link()->out(),
496 'resetlink' => $tour->get_reset_link()->out(),
497 ]));
498
499 $table = new table\step_list($tourid);
500 foreach ($tour->get_steps() as $step) {
501 $table->add_data_keyed($table->format_row($step));
502 }
503
504 $table->finish_output();
505 $this->print_edit_step_link($tourid);
506
507 // JS for Step management.
508 $PAGE->requires->js_call_amd('tool_usertours/managesteps', 'setup');
509
510 $this->footer();
511 }
512
c48fbb6c
MM
513 /**
514 * Duplicate an existing tour.
515 *
516 * @param int $tourid The ID of the tour to duplicate.
517 */
518 protected function duplicate_tour($tourid) {
519 $tour = helper::get_tour($tourid);
520
521 $export = $tour->to_record();
522 // Remove the id.
523 unset($export->id);
524
525 // Set the version.
526 $export->version = get_config('tool_usertours', 'version');
527
528 $export->name = get_string('duplicatetour_name', 'tool_usertours', $export->name);
529
530 // Step export.
531 $export->steps = [];
532 foreach ($tour->get_steps() as $step) {
533 $record = $step->to_record();
534 unset($record->id);
535 unset($record->tourid);
536
537 $export->steps[] = $record;
538 }
539
540 $exportstring = json_encode($export);
541 $newtour = self::import_tour_from_json($exportstring);
542
543 redirect($newtour->get_view_link());
544 }
545
001fc061
AN
546 /**
547 * Show the tour.
548 *
549 * @param int $tourid The ID of the tour to display.
550 */
551 protected function show_tour($tourid) {
552 $this->show_hide_tour($tourid, 1);
553 }
554
555 /**
556 * Hide the tour.
557 *
558 * @param int $tourid The ID of the tour to display.
559 */
560 protected function hide_tour($tourid) {
561 $this->show_hide_tour($tourid, 0);
562 }
563
564 /**
565 * Show or Hide the tour.
566 *
567 * @param int $tourid The ID of the tour to display.
568 * @param int $visibility The intended visibility.
569 */
570 protected function show_hide_tour($tourid, $visibility) {
571 global $DB;
572
573 require_sesskey();
574
575 $tour = $DB->get_record('tool_usertours_tours', array('id' => $tourid));
576 $tour->enabled = $visibility;
577 $DB->update_record('tool_usertours_tours', $tour);
578
579 redirect(helper::get_list_tour_link());
580 }
581
582 /**
583 * Delete the tour.
584 *
585 * @param int $tourid The ID of the tour to remove.
586 */
587 protected function delete_tour($tourid) {
588 require_sesskey();
589
590 $tour = tour::instance($tourid);
591 $tour->remove();
592
593 redirect(helper::get_list_tour_link());
594 }
595
596 /**
597 * Reset the tour state for all users.
598 *
599 * @param int $tourid The ID of the tour to remove.
600 */
601 protected function reset_tour_for_all($tourid) {
602 require_sesskey();
603
604 $tour = tour::instance($tourid);
605 $tour->mark_major_change();
606
607 redirect(helper::get_view_tour_link($tourid), get_string('tour_resetforall', 'tool_usertours'));
608 }
609
610 /**
611 * Get the first tour matching the current page URL.
612 *
613 * @param bool $reset Forcibly update the current tour
614 * @return tour
615 */
616 public static function get_current_tour($reset = false) {
617 global $PAGE;
618
619 static $tour = false;
620
621 if ($tour === false || $reset) {
622 $tour = self::get_matching_tours($PAGE->url);
623 }
624
625 return $tour;
626 }
627
628 /**
629 * Get the first tour matching the specified URL.
630 *
631 * @param moodle_url $pageurl The URL to match.
632 * @return tour
633 */
634 public static function get_matching_tours(\moodle_url $pageurl) {
1093e43b 635 global $PAGE;
001fc061 636
c6b44215 637 $tours = cache::get_matching_tourdata($pageurl);
001fc061
AN
638
639 foreach ($tours as $record) {
640 $tour = tour::load_from_record($record);
1093e43b 641 if ($tour->is_enabled() && $tour->matches_all_filters($PAGE->context)) {
001fc061
AN
642 return $tour;
643 }
644 }
645
646 return null;
647 }
648
649 /**
650 * Import the provided tour JSON.
651 *
652 * @param string $json The tour configuration.
653 * @return tour
654 */
655 public static function import_tour_from_json($json) {
656 $tourconfig = json_decode($json);
657
658 // We do not use this yet - we may do in the future.
659 unset($tourconfig->version);
660
661 $steps = $tourconfig->steps;
662 unset($tourconfig->steps);
663
664 $tourconfig->id = null;
665 $tourconfig->sortorder = null;
666 $tour = tour::load_from_record($tourconfig, true);
667 $tour->persist(true);
668
669 // Ensure that steps are orderered by their sortorder.
670 \core_collator::asort_objects_by_property($steps, 'sortorder', \core_collator::SORT_NUMERIC);
671
672 foreach ($steps as $stepconfig) {
673 $stepconfig->id = null;
674 $stepconfig->tourid = $tour->get_id();
675 $step = step::load_from_record($stepconfig, true);
676 $step->persist(true);
677 }
678
679 return $tour;
680 }
681
682 /**
683 * Helper to fetch the renderer.
684 *
685 * @return renderer
686 */
687 protected function get_renderer() {
688 global $PAGE;
689 return $PAGE->get_renderer('tool_usertours');
690 }
691
692 /**
693 * Print the edit step link.
694 *
695 * @param int $tourid The ID of the tour.
696 * @param int $stepid The ID of the step.
697 * @return string
698 */
699 protected function print_edit_step_link($tourid, $stepid = null) {
700 $addlink = helper::get_edit_step_link($tourid, $stepid);
701 $attributes = [];
702 if (empty($stepid)) {
703 $attributes['class'] = 'createstep';
704 }
705 echo \html_writer::link($addlink, get_string('newstep', 'tool_usertours'), $attributes);
706 }
707
708 /**
709 * Display the edit step form for the specified step.
710 *
711 * @param int $id The step to edit.
712 */
713 protected function edit_step($id) {
714 global $PAGE;
715
716 if (isset($id)) {
717 $step = step::instance($id);
718 } else {
719 $step = new step();
720 $step->set_tourid(required_param('tourid', PARAM_INT));
721 }
722
723 $tour = $step->get_tour();
3eb3d916
RW
724
725 if (!empty($tour->get_config(self::CONFIG_SHIPPED_TOUR))) {
726 notification::add(get_string('modifyshippedtourwarning', 'tool_usertours'), notification::WARNING);
727 }
728
001fc061
AN
729 $PAGE->navbar->add($tour->get_name(), $tour->get_view_link());
730 if (isset($id)) {
731 $PAGE->navbar->add($step->get_title(), $step->get_edit_link());
732 } else {
733 $PAGE->navbar->add(get_string('newstep', 'tool_usertours'), $step->get_edit_link());
734 }
735
736 $form = new forms\editstep($step->get_edit_link(), $step);
737 if ($form->is_cancelled()) {
738 redirect($step->get_tour()->get_view_link());
739 } else if ($data = $form->get_data()) {
740 $step->handle_form_submission($form, $data);
741 $step->get_tour()->reset_step_sortorder();
742 redirect($step->get_tour()->get_view_link());
743 } else {
744 if (empty($id)) {
745 $this->header(get_string('newstep', 'tool_usertours'));
746 } else {
747 $this->header(get_string('editstep', 'tool_usertours', $step->get_title()));
748 }
749 $form->set_data($step->prepare_data_for_form());
750
751 $form->display();
752 $this->footer();
753 }
754 }
755
756 /**
fbb21961 757 * Move a tour up or down and redirect once complete.
001fc061
AN
758 *
759 * @param int $id The tour to move.
760 */
761 protected function move_tour($id) {
762 require_sesskey();
763
764 $direction = required_param('direction', PARAM_INT);
765
766 $tour = tour::instance($id);
fbb21961
ED
767 self::_move_tour($tour, $direction);
768
769 redirect(helper::get_list_tour_link());
770 }
771
772 /**
773 * Move a tour up or down.
774 *
775 * @param tour $tour The tour to move.
776 *
777 * @param int $direction
778 */
779 protected static function _move_tour(tour $tour, $direction) {
8dabade6
PH
780 // We can't move the first tour higher, nor the last tour any lower.
781 if (($tour->is_first_tour() && $direction == helper::MOVE_UP) ||
782 ($tour->is_last_tour() && $direction == helper::MOVE_DOWN)) {
783
784 return;
785 }
786
001fc061
AN
787 $currentsortorder = $tour->get_sortorder();
788 $targetsortorder = $currentsortorder + $direction;
789
790 $swapwith = helper::get_tour_from_sortorder($targetsortorder);
791
792 // Set the sort order to something out of the way.
793 $tour->set_sortorder(-1);
794 $tour->persist();
795
796 // Swap the two sort orders.
797 $swapwith->set_sortorder($currentsortorder);
798 $swapwith->persist();
799
800 $tour->set_sortorder($targetsortorder);
801 $tour->persist();
001fc061
AN
802 }
803
804 /**
805 * Move a step up or down.
806 *
807 * @param int $id The step to move.
808 */
809 protected function move_step($id) {
810 require_sesskey();
811
812 $direction = required_param('direction', PARAM_INT);
813
814 $step = step::instance($id);
815 $currentsortorder = $step->get_sortorder();
816 $targetsortorder = $currentsortorder + $direction;
817
818 $tour = $step->get_tour();
819 $swapwith = helper::get_step_from_sortorder($tour->get_id(), $targetsortorder);
820
821 // Set the sort order to something out of the way.
822 $step->set_sortorder(-1);
823 $step->persist();
824
825 // Swap the two sort orders.
826 $swapwith->set_sortorder($currentsortorder);
827 $swapwith->persist();
828
829 $step->set_sortorder($targetsortorder);
830 $step->persist();
831
832 // Reset the sort order.
833 $tour->reset_step_sortorder();
834 redirect($tour->get_view_link());
835 }
836
837 /**
838 * Delete the step.
839 *
840 * @param int $stepid The ID of the step to remove.
841 */
842 protected function delete_step($stepid) {
843 require_sesskey();
844
845 $step = step::instance($stepid);
846 $tour = $step->get_tour();
847
848 $step->remove();
849 redirect($tour->get_view_link());
850 }
3eb3d916
RW
851
852 /**
853 * Make sure all of the default tours that are shipped with Moodle are created
854 * and up to date with the latest version.
855 */
856 public static function update_shipped_tours() {
857 global $DB, $CFG;
858
859 // A list of tours that are shipped with Moodle. They are in
860 // the format filename => version. The version value needs to
861 // be increased if the tour has been updated.
10df630e 862 $shippedtours = [
fbb21961
ED
863 ];
864
865 // These are tours that we used to ship but don't ship any longer.
866 // We do not remove them, but we do disable them.
867 $unshippedtours = [
b9310489 868 // Formerly included in Moodle 3.2.0.
10df630e
RW
869 'boost_administrator.json' => 1,
870 'boost_course_view.json' => 1,
3eb3d916 871
b9310489
AN
872 // Formerly included in Moodle 3.6.0.
873 '36_dashboard.json' => 3,
874 '36_messaging.json' => 3,
875 ];
8ca99a92 876
3eb3d916
RW
877 $existingtourrecords = $DB->get_recordset('tool_usertours_tours');
878
879 // Get all of the existing shipped tours and check if they need to be
880 // updated.
881 foreach ($existingtourrecords as $tourrecord) {
882 $tour = tour::load_from_record($tourrecord);
883
884 if (!empty($tour->get_config(self::CONFIG_SHIPPED_TOUR))) {
885 $filename = $tour->get_config(self::CONFIG_SHIPPED_FILENAME);
886 $version = $tour->get_config(self::CONFIG_SHIPPED_VERSION);
887
888 // If we know about this tour (otherwise leave it as is).
889 if (isset($shippedtours[$filename])) {
890 // And the version in the DB is an older version.
891 if ($version < $shippedtours[$filename]) {
892 // Remove the old version because it's been updated
893 // and needs to be recreated.
894 $tour->remove();
895 } else {
896 // The tour has not been updated so we don't need to
897 // do anything with it.
898 unset($shippedtours[$filename]);
899 }
900 }
fbb21961
ED
901
902 if (isset($unshippedtours[$filename])) {
903 if ($version <= $unshippedtours[$filename]) {
84dad5bf 904 $tour = tour::instance($tour->get_id());
fbb21961
ED
905 $tour->set_enabled(tour::DISABLED);
906 $tour->persist();
907 }
908 }
3eb3d916
RW
909 }
910 }
3eb3d916
RW
911 $existingtourrecords->close();
912
091758d5
PH
913 // Ensure we correct the sortorder in any existing tours, prior to adding latest shipped tours.
914 helper::reset_tour_sortorder();
915
fbb21961 916 foreach (array_reverse($shippedtours) as $filename => $version) {
7ccb6bb3 917 $filepath = $CFG->dirroot . "/{$CFG->admin}/tool/usertours/tours/" . $filename;
3eb3d916
RW
918 $tourjson = file_get_contents($filepath);
919 $tour = self::import_tour_from_json($tourjson);
920
921 // Set some additional config data to record that this tour was
922 // added as a shipped tour.
923 $tour->set_config(self::CONFIG_SHIPPED_TOUR, true);
924 $tour->set_config(self::CONFIG_SHIPPED_FILENAME, $filename);
925 $tour->set_config(self::CONFIG_SHIPPED_VERSION, $version);
926
fbb21961
ED
927 // Bump new tours to the top of the list.
928 while ($tour->get_sortorder() > 0) {
929 self::_move_tour($tour, helper::MOVE_UP);
930 }
931
3eb3d916
RW
932 if (defined('BEHAT_SITE_RUNNING') || (defined('PHPUNIT_TEST') && PHPUNIT_TEST)) {
933 // Disable this tour if this is behat or phpunit.
934 $tour->set_enabled(false);
935 }
936
937 $tour->persist();
938 }
939 }
001fc061 940}