2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
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
25 namespace tool_usertours;
27 defined('MOODLE_INTERNAL') || die();
29 use tool_usertours\local\forms;
30 use tool_usertours\local\table;
35 * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41 * @var ACTION_LISTTOURS The action to get the list of tours.
43 const ACTION_LISTTOURS = 'listtours';
46 * @var ACTION_NEWTOUR The action to create a new tour.
48 const ACTION_NEWTOUR = 'newtour';
51 * @var ACTION_EDITTOUR The action to edit the tour.
53 const ACTION_EDITTOUR = 'edittour';
56 * @var ACTION_MOVETOUR The action to move a tour up or down.
58 const ACTION_MOVETOUR = 'movetour';
61 * @var ACTION_EXPORTTOUR The action to export the tour.
63 const ACTION_EXPORTTOUR = 'exporttour';
66 * @var ACTION_IMPORTTOUR The action to import the tour.
68 const ACTION_IMPORTTOUR = 'importtour';
71 * @var ACTION_DELETETOUR The action to delete the tour.
73 const ACTION_DELETETOUR = 'deletetour';
76 * @var ACTION_VIEWTOUR The action to view the tour.
78 const ACTION_VIEWTOUR = 'viewtour';
81 * @var ACTION_NEWSTEP The action to create a new step.
83 const ACTION_NEWSTEP = 'newstep';
86 * @var ACTION_EDITSTEP The action to edit step configuration.
88 const ACTION_EDITSTEP = 'editstep';
91 * @var ACTION_MOVESTEP The action to move a step up or down.
93 const ACTION_MOVESTEP = 'movestep';
96 * @var ACTION_DELETESTEP The action to delete a step.
98 const ACTION_DELETESTEP = 'deletestep';
101 * @var ACTION_VIEWSTEP The action to view a step.
103 const ACTION_VIEWSTEP = 'viewstep';
106 * @var ACTION_HIDETOUR The action to hide a tour.
108 const ACTION_HIDETOUR = 'hidetour';
111 * @var ACTION_SHOWTOUR The action to show a tour.
113 const ACTION_SHOWTOUR = 'showtour';
116 * @var ACTION_RESETFORALL
118 const ACTION_RESETFORALL = 'resetforall';
121 * This is the entry point for this controller class.
123 * @param string $action The action to perform.
125 public function execute($action) {
126 admin_externalpage_setup('tool_usertours/tours');
127 // Add the main content.
129 case self::ACTION_NEWTOUR:
130 case self::ACTION_EDITTOUR:
131 $this->edit_tour(optional_param('id', null, PARAM_INT));
134 case self::ACTION_MOVETOUR:
135 $this->move_tour(required_param('id', PARAM_INT));
138 case self::ACTION_EXPORTTOUR:
139 $this->export_tour(required_param('id', PARAM_INT));
142 case self::ACTION_IMPORTTOUR:
143 $this->import_tour();
146 case self::ACTION_VIEWTOUR:
147 $this->view_tour(required_param('id', PARAM_INT));
150 case self::ACTION_HIDETOUR:
151 $this->hide_tour(required_param('id', PARAM_INT));
154 case self::ACTION_SHOWTOUR:
155 $this->show_tour(required_param('id', PARAM_INT));
158 case self::ACTION_DELETETOUR:
159 $this->delete_tour(required_param('id', PARAM_INT));
162 case self::ACTION_RESETFORALL:
163 $this->reset_tour_for_all(required_param('id', PARAM_INT));
166 case self::ACTION_NEWSTEP:
167 case self::ACTION_EDITSTEP:
168 $this->edit_step(optional_param('id', null, PARAM_INT));
171 case self::ACTION_MOVESTEP:
172 $this->move_step(required_param('id', PARAM_INT));
175 case self::ACTION_DELETESTEP:
176 $this->delete_step(required_param('id', PARAM_INT));
179 case self::ACTION_LISTTOURS:
181 $this->print_tour_list();
187 * Print out the page header.
189 * @param string $title The title to display.
191 protected function header($title = null) {
194 // Print the page heading.
195 echo $OUTPUT->header();
197 if ($title === null) {
198 $title = get_string('tours', 'tool_usertours');
201 echo $OUTPUT->heading($title);
205 * Print out the page footer.
209 protected function footer() {
212 echo $OUTPUT->footer();
216 * Print the the list of tours.
218 protected function print_tour_list() {
219 global $PAGE, $OUTPUT;
222 echo \html_writer::span(get_string('tourlist_explanation', 'tool_usertours'));
223 $table = new table\tour_list();
224 $tours = helper::get_tours();
225 foreach ($tours as $tour) {
226 $table->add_data_keyed($table->format_row($tour));
229 $table->finish_output();
232 'link' => helper::get_edit_tour_link(),
233 'linkproperties' => [],
234 'img' => 'b/tour-new',
235 'title' => get_string('newtour', 'tool_usertours'),
238 'link' => helper::get_import_tour_link(),
239 'linkproperties' => [],
240 'img' => 'b/tour-import',
241 'title' => get_string('importtour', 'tool_usertours'),
244 'link' => new \moodle_url('https://moodle.net/mod/data/view.php', [
247 'linkproperties' => [
248 'target' => '_blank',
250 'img' => 'b/tour-shared',
251 'title' => get_string('sharedtourslink', 'tool_usertours'),
255 echo \html_writer::start_tag('div', [
256 'class' => 'tour-actions',
259 echo \html_writer::start_tag('ul');
260 foreach ($actions as $config) {
261 $action = \html_writer::start_tag('li');
262 $linkproperties = $config->linkproperties;
263 $linkproperties['href'] = $config->link;
264 $action .= \html_writer::start_tag('a', $linkproperties);
265 $action .= \html_writer::img(
266 $OUTPUT->pix_url($config->img, 'tool_usertours'),
268 $action .= \html_writer::div($config->title);
269 $action .= \html_writer::end_tag('a');
270 $action .= \html_writer::end_tag('li');
273 echo \html_writer::end_tag('ul');
274 echo \html_writer::end_tag('div');
276 // JS for Tour management.
277 $PAGE->requires->js_call_amd('tool_usertours/managetours', 'setup');
282 * Return the edit tour link.
284 * @param int $id The ID of the tour
287 protected function get_edit_tour_link($id = null) {
288 $addlink = helper::get_edit_tour_link($id);
289 return \html_writer::link($addlink, get_string('newtour', 'tool_usertours'));
293 * Print the edit tour link.
295 * @param int $id The ID of the tour
297 protected function print_edit_tour_link($id = null) {
298 echo $this->get_edit_tour_link($id);
302 * Get the import tour link.
306 protected function get_import_tour_link() {
307 $importlink = helper::get_import_tour_link();
308 return \html_writer::link($importlink, get_string('importtour', 'tool_usertours'));
312 * Print the edit tour page.
314 * @param int $id The ID of the tour
316 protected function edit_tour($id = null) {
319 $tour = tour::instance($id);
320 $PAGE->navbar->add($tour->get_name(), $tour->get_edit_link());
324 $PAGE->navbar->add(get_string('newtour', 'tool_usertours'), $tour->get_edit_link());
327 $form = new forms\edittour($tour);
329 if ($form->is_cancelled()) {
330 redirect(helper::get_list_tour_link());
331 } else if ($data = $form->get_data()) {
332 // Creating a new tour.
333 $tour->set_name($data->name);
334 $tour->set_description($data->description);
335 $tour->set_pathmatch($data->pathmatch);
336 $tour->set_enabled(!empty($data->enabled));
338 foreach (configuration::get_defaultable_keys() as $key) {
339 $tour->set_config($key, $data->$key);
342 // Save filter values.
343 foreach (helper::get_all_filters() as $filterclass) {
344 $filterclass::save_filter_values_from_form($tour, $data);
349 redirect(helper::get_list_tour_link());
352 $this->header('newtour');
354 $this->header($tour->get_name());
355 $data = $tour->prepare_data_for_form();
357 // Prepare filter values for the form.
358 foreach (helper::get_all_filters() as $filterclass) {
359 $filterclass::prepare_filter_values_for_form($tour, $data);
361 $form->set_data($data);
370 * Print the export tour page.
372 * @param int $id The ID of the tour
374 protected function export_tour($id) {
375 $tour = tour::instance($id);
377 // Grab the full data record.
378 $export = $tour->to_record();
384 $export->version = get_config('tool_usertours', 'version');
388 foreach ($tour->get_steps() as $step) {
389 $record = $step->to_record();
391 unset($record->tourid);
393 $export->steps[] = $record;
396 $exportstring = json_encode($export);
398 $filename = 'tour_export_' . $tour->get_id() . '_' . time() . '.json';
401 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
402 header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
403 header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . 'GMT');
404 header('Pragma: no-cache');
405 header('Accept-Ranges: none');
406 header('Content-disposition: attachment; filename=' . $filename);
407 header('Content-length: ' . strlen($exportstring));
408 header('Content-type: text/calendar; charset=utf-8');
415 * Handle tour import.
417 protected function import_tour() {
419 $PAGE->navbar->add(get_string('importtour', 'tool_usertours'), helper::get_import_tour_link());
421 $form = new forms\importtour();
423 if ($form->is_cancelled()) {
424 redirect(helper::get_list_tour_link());
425 } else if ($form->get_data()) {
427 $tourconfigraw = $form->get_file_content('tourconfig');
428 $tour = self::import_tour_from_json($tourconfigraw);
430 redirect($tour->get_view_link());
439 * Print the view tour page.
441 * @param int $tourid The ID of the tour to display.
443 protected function view_tour($tourid) {
445 $tour = helper::get_tour($tourid);
447 $PAGE->navbar->add($tour->get_name(), $tour->get_view_link());
449 $this->header($tour->get_name());
450 echo \html_writer::span(get_string('viewtour_info', 'tool_usertours', [
451 'tourname' => $tour->get_name(),
452 'path' => $tour->get_pathmatch(),
454 echo \html_writer::div(get_string('viewtour_edit', 'tool_usertours', [
455 'editlink' => $tour->get_edit_link()->out(),
456 'resetlink' => $tour->get_reset_link()->out(),
459 $table = new table\step_list($tourid);
460 foreach ($tour->get_steps() as $step) {
461 $table->add_data_keyed($table->format_row($step));
464 $table->finish_output();
465 $this->print_edit_step_link($tourid);
467 // JS for Step management.
468 $PAGE->requires->js_call_amd('tool_usertours/managesteps', 'setup');
476 * @param int $tourid The ID of the tour to display.
478 protected function show_tour($tourid) {
479 $this->show_hide_tour($tourid, 1);
485 * @param int $tourid The ID of the tour to display.
487 protected function hide_tour($tourid) {
488 $this->show_hide_tour($tourid, 0);
492 * Show or Hide the tour.
494 * @param int $tourid The ID of the tour to display.
495 * @param int $visibility The intended visibility.
497 protected function show_hide_tour($tourid, $visibility) {
502 $tour = $DB->get_record('tool_usertours_tours', array('id' => $tourid));
503 $tour->enabled = $visibility;
504 $DB->update_record('tool_usertours_tours', $tour);
506 redirect(helper::get_list_tour_link());
512 * @param int $tourid The ID of the tour to remove.
514 protected function delete_tour($tourid) {
517 $tour = tour::instance($tourid);
520 redirect(helper::get_list_tour_link());
524 * Reset the tour state for all users.
526 * @param int $tourid The ID of the tour to remove.
528 protected function reset_tour_for_all($tourid) {
531 $tour = tour::instance($tourid);
532 $tour->mark_major_change();
534 redirect(helper::get_view_tour_link($tourid), get_string('tour_resetforall', 'tool_usertours'));
538 * Get the first tour matching the current page URL.
540 * @param bool $reset Forcibly update the current tour
543 public static function get_current_tour($reset = false) {
546 static $tour = false;
548 if ($tour === false || $reset) {
549 $tour = self::get_matching_tours($PAGE->url);
556 * Get the first tour matching the specified URL.
558 * @param moodle_url $pageurl The URL to match.
561 public static function get_matching_tours(\moodle_url $pageurl) {
565 SELECT * FROM {tool_usertours_tours}
568 ORDER BY sortorder ASC
571 $tours = $DB->get_records_sql($sql, array(
572 $pageurl->out_as_local_url(),
575 foreach ($tours as $record) {
576 $tour = tour::load_from_record($record);
577 if ($tour->is_enabled() && $tour->matches_all_filters($PAGE->context) && $tour->count_steps() > 0) {
586 * Import the provided tour JSON.
588 * @param string $json The tour configuration.
591 public static function import_tour_from_json($json) {
592 $tourconfig = json_decode($json);
594 // We do not use this yet - we may do in the future.
595 unset($tourconfig->version);
597 $steps = $tourconfig->steps;
598 unset($tourconfig->steps);
600 $tourconfig->id = null;
601 $tourconfig->sortorder = null;
602 $tour = tour::load_from_record($tourconfig, true);
603 $tour->persist(true);
605 // Ensure that steps are orderered by their sortorder.
606 \core_collator::asort_objects_by_property($steps, 'sortorder', \core_collator::SORT_NUMERIC);
608 foreach ($steps as $stepconfig) {
609 $stepconfig->id = null;
610 $stepconfig->tourid = $tour->get_id();
611 $step = step::load_from_record($stepconfig, true);
612 $step->persist(true);
619 * Helper to fetch the renderer.
623 protected function get_renderer() {
625 return $PAGE->get_renderer('tool_usertours');
629 * Print the edit step link.
631 * @param int $tourid The ID of the tour.
632 * @param int $stepid The ID of the step.
635 protected function print_edit_step_link($tourid, $stepid = null) {
636 $addlink = helper::get_edit_step_link($tourid, $stepid);
638 if (empty($stepid)) {
639 $attributes['class'] = 'createstep';
641 echo \html_writer::link($addlink, get_string('newstep', 'tool_usertours'), $attributes);
645 * Display the edit step form for the specified step.
647 * @param int $id The step to edit.
649 protected function edit_step($id) {
653 $step = step::instance($id);
656 $step->set_tourid(required_param('tourid', PARAM_INT));
659 $tour = $step->get_tour();
660 $PAGE->navbar->add($tour->get_name(), $tour->get_view_link());
662 $PAGE->navbar->add($step->get_title(), $step->get_edit_link());
664 $PAGE->navbar->add(get_string('newstep', 'tool_usertours'), $step->get_edit_link());
667 $form = new forms\editstep($step->get_edit_link(), $step);
668 if ($form->is_cancelled()) {
669 redirect($step->get_tour()->get_view_link());
670 } else if ($data = $form->get_data()) {
671 $step->handle_form_submission($form, $data);
672 $step->get_tour()->reset_step_sortorder();
673 redirect($step->get_tour()->get_view_link());
676 $this->header(get_string('newstep', 'tool_usertours'));
678 $this->header(get_string('editstep', 'tool_usertours', $step->get_title()));
680 $form->set_data($step->prepare_data_for_form());
688 * Move a tour up or down.
690 * @param int $id The tour to move.
692 protected function move_tour($id) {
695 $direction = required_param('direction', PARAM_INT);
697 $tour = tour::instance($id);
698 $currentsortorder = $tour->get_sortorder();
699 $targetsortorder = $currentsortorder + $direction;
701 $swapwith = helper::get_tour_from_sortorder($targetsortorder);
703 // Set the sort order to something out of the way.
704 $tour->set_sortorder(-1);
707 // Swap the two sort orders.
708 $swapwith->set_sortorder($currentsortorder);
709 $swapwith->persist();
711 $tour->set_sortorder($targetsortorder);
714 redirect(helper::get_list_tour_link());
718 * Move a step up or down.
720 * @param int $id The step to move.
722 protected function move_step($id) {
725 $direction = required_param('direction', PARAM_INT);
727 $step = step::instance($id);
728 $currentsortorder = $step->get_sortorder();
729 $targetsortorder = $currentsortorder + $direction;
731 $tour = $step->get_tour();
732 $swapwith = helper::get_step_from_sortorder($tour->get_id(), $targetsortorder);
734 // Set the sort order to something out of the way.
735 $step->set_sortorder(-1);
738 // Swap the two sort orders.
739 $swapwith->set_sortorder($currentsortorder);
740 $swapwith->persist();
742 $step->set_sortorder($targetsortorder);
745 // Reset the sort order.
746 $tour->reset_step_sortorder();
747 redirect($tour->get_view_link());
753 * @param int $stepid The ID of the step to remove.
755 protected function delete_step($stepid) {
758 $step = step::instance($stepid);
759 $tour = $step->get_tour();
762 redirect($tour->get_view_link());