41d2fb93c8
Fetching user tours used to fail on external_api::validate_context() and require_login() calls if the user did not have the site policy agreed. The patch introduces a check to see if the user is fully set up and ready to use the site before attempting to load the tours.
954 lines
28 KiB
PHP
954 lines
28 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* Tour manager.
|
|
*
|
|
* @package tool_usertours
|
|
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
namespace tool_usertours;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
use tool_usertours\local\forms;
|
|
use tool_usertours\local\table;
|
|
use core\notification;
|
|
|
|
/**
|
|
* Tour manager.
|
|
*
|
|
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class manager {
|
|
|
|
/**
|
|
* @var ACTION_LISTTOURS The action to get the list of tours.
|
|
*/
|
|
const ACTION_LISTTOURS = 'listtours';
|
|
|
|
/**
|
|
* @var ACTION_NEWTOUR The action to create a new tour.
|
|
*/
|
|
const ACTION_NEWTOUR = 'newtour';
|
|
|
|
/**
|
|
* @var ACTION_EDITTOUR The action to edit the tour.
|
|
*/
|
|
const ACTION_EDITTOUR = 'edittour';
|
|
|
|
/**
|
|
* @var ACTION_MOVETOUR The action to move a tour up or down.
|
|
*/
|
|
const ACTION_MOVETOUR = 'movetour';
|
|
|
|
/**
|
|
* @var ACTION_EXPORTTOUR The action to export the tour.
|
|
*/
|
|
const ACTION_EXPORTTOUR = 'exporttour';
|
|
|
|
/**
|
|
* @var ACTION_IMPORTTOUR The action to import the tour.
|
|
*/
|
|
const ACTION_IMPORTTOUR = 'importtour';
|
|
|
|
/**
|
|
* @var ACTION_DELETETOUR The action to delete the tour.
|
|
*/
|
|
const ACTION_DELETETOUR = 'deletetour';
|
|
|
|
/**
|
|
* @var ACTION_VIEWTOUR The action to view the tour.
|
|
*/
|
|
const ACTION_VIEWTOUR = 'viewtour';
|
|
|
|
/**
|
|
* @var ACTION_DUPLICATETOUR The action to duplicate the tour.
|
|
*/
|
|
const ACTION_DUPLICATETOUR = 'duplicatetour';
|
|
|
|
/**
|
|
* @var ACTION_NEWSTEP The action to create a new step.
|
|
*/
|
|
const ACTION_NEWSTEP = 'newstep';
|
|
|
|
/**
|
|
* @var ACTION_EDITSTEP The action to edit step configuration.
|
|
*/
|
|
const ACTION_EDITSTEP = 'editstep';
|
|
|
|
/**
|
|
* @var ACTION_MOVESTEP The action to move a step up or down.
|
|
*/
|
|
const ACTION_MOVESTEP = 'movestep';
|
|
|
|
/**
|
|
* @var ACTION_DELETESTEP The action to delete a step.
|
|
*/
|
|
const ACTION_DELETESTEP = 'deletestep';
|
|
|
|
/**
|
|
* @var ACTION_VIEWSTEP The action to view a step.
|
|
*/
|
|
const ACTION_VIEWSTEP = 'viewstep';
|
|
|
|
/**
|
|
* @var ACTION_HIDETOUR The action to hide a tour.
|
|
*/
|
|
const ACTION_HIDETOUR = 'hidetour';
|
|
|
|
/**
|
|
* @var ACTION_SHOWTOUR The action to show a tour.
|
|
*/
|
|
const ACTION_SHOWTOUR = 'showtour';
|
|
|
|
/**
|
|
* @var ACTION_RESETFORALL
|
|
*/
|
|
const ACTION_RESETFORALL = 'resetforall';
|
|
|
|
/**
|
|
* @var CONFIG_SHIPPED_TOUR
|
|
*/
|
|
const CONFIG_SHIPPED_TOUR = 'shipped_tour';
|
|
|
|
/**
|
|
* @var CONFIG_SHIPPED_FILENAME
|
|
*/
|
|
const CONFIG_SHIPPED_FILENAME = 'shipped_filename';
|
|
|
|
/**
|
|
* @var CONFIG_SHIPPED_VERSION
|
|
*/
|
|
const CONFIG_SHIPPED_VERSION = 'shipped_version';
|
|
|
|
/**
|
|
* Helper method to initialize admin page, setting appropriate extra URL parameters
|
|
*
|
|
* @param string $action
|
|
*/
|
|
protected function setup_admin_externalpage(string $action): void {
|
|
admin_externalpage_setup('tool_usertours/tours', '', array_filter([
|
|
'action' => $action,
|
|
'id' => optional_param('id', 0, PARAM_INT),
|
|
'tourid' => optional_param('tourid', 0, PARAM_INT),
|
|
'direction' => optional_param('direction', 0, PARAM_INT),
|
|
]));
|
|
}
|
|
|
|
/**
|
|
* This is the entry point for this controller class.
|
|
*
|
|
* @param string $action The action to perform.
|
|
*/
|
|
public function execute($action) {
|
|
$this->setup_admin_externalpage($action);
|
|
|
|
// Add the main content.
|
|
switch($action) {
|
|
case self::ACTION_NEWTOUR:
|
|
case self::ACTION_EDITTOUR:
|
|
$this->edit_tour(optional_param('id', null, PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_MOVETOUR:
|
|
$this->move_tour(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_EXPORTTOUR:
|
|
$this->export_tour(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_IMPORTTOUR:
|
|
$this->import_tour();
|
|
break;
|
|
|
|
case self::ACTION_VIEWTOUR:
|
|
$this->view_tour(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_DUPLICATETOUR:
|
|
$this->duplicate_tour(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_HIDETOUR:
|
|
$this->hide_tour(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_SHOWTOUR:
|
|
$this->show_tour(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_DELETETOUR:
|
|
$this->delete_tour(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_RESETFORALL:
|
|
$this->reset_tour_for_all(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_NEWSTEP:
|
|
case self::ACTION_EDITSTEP:
|
|
$this->edit_step(optional_param('id', null, PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_MOVESTEP:
|
|
$this->move_step(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_DELETESTEP:
|
|
$this->delete_step(required_param('id', PARAM_INT));
|
|
break;
|
|
|
|
case self::ACTION_LISTTOURS:
|
|
default:
|
|
$this->print_tour_list();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print out the page header.
|
|
*
|
|
* @param string $title The title to display.
|
|
*/
|
|
protected function header($title = null) {
|
|
global $OUTPUT;
|
|
|
|
// Print the page heading.
|
|
echo $OUTPUT->header();
|
|
|
|
if ($title === null) {
|
|
$title = get_string('tours', 'tool_usertours');
|
|
}
|
|
|
|
echo $OUTPUT->heading($title);
|
|
}
|
|
|
|
/**
|
|
* Print out the page footer.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function footer() {
|
|
global $OUTPUT;
|
|
|
|
echo $OUTPUT->footer();
|
|
}
|
|
|
|
/**
|
|
* Print the the list of tours.
|
|
*/
|
|
protected function print_tour_list() {
|
|
global $PAGE, $OUTPUT;
|
|
|
|
$this->header();
|
|
echo \html_writer::span(get_string('tourlist_explanation', 'tool_usertours'));
|
|
$table = new table\tour_list();
|
|
$tours = helper::get_tours();
|
|
foreach ($tours as $tour) {
|
|
$table->add_data_keyed($table->format_row($tour));
|
|
}
|
|
|
|
$table->finish_output();
|
|
$actions = [
|
|
(object) [
|
|
'link' => helper::get_edit_tour_link(),
|
|
'linkproperties' => [],
|
|
'img' => 'b/tour-new',
|
|
'title' => get_string('newtour', 'tool_usertours'),
|
|
],
|
|
(object) [
|
|
'link' => helper::get_import_tour_link(),
|
|
'linkproperties' => [],
|
|
'img' => 'b/tour-import',
|
|
'title' => get_string('importtour', 'tool_usertours'),
|
|
],
|
|
(object) [
|
|
'link' => new \moodle_url('https://archive.moodle.net/tours'),
|
|
'linkproperties' => [
|
|
'target' => '_blank',
|
|
],
|
|
'img' => 'b/tour-shared',
|
|
'title' => get_string('sharedtourslink', 'tool_usertours'),
|
|
],
|
|
];
|
|
|
|
echo \html_writer::start_tag('div', [
|
|
'class' => 'tour-actions',
|
|
]);
|
|
|
|
echo \html_writer::start_tag('ul');
|
|
foreach ($actions as $config) {
|
|
$action = \html_writer::start_tag('li');
|
|
$linkproperties = $config->linkproperties;
|
|
$linkproperties['href'] = $config->link;
|
|
$action .= \html_writer::start_tag('a', $linkproperties);
|
|
$action .= $OUTPUT->pix_icon($config->img, $config->title, 'tool_usertours');
|
|
$action .= \html_writer::div($config->title);
|
|
$action .= \html_writer::end_tag('a');
|
|
$action .= \html_writer::end_tag('li');
|
|
echo $action;
|
|
}
|
|
echo \html_writer::end_tag('ul');
|
|
echo \html_writer::end_tag('div');
|
|
|
|
// JS for Tour management.
|
|
$PAGE->requires->js_call_amd('tool_usertours/managetours', 'setup');
|
|
$this->footer();
|
|
}
|
|
|
|
/**
|
|
* Return the edit tour link.
|
|
*
|
|
* @param int $id The ID of the tour
|
|
* @return string
|
|
*/
|
|
protected function get_edit_tour_link($id = null) {
|
|
$addlink = helper::get_edit_tour_link($id);
|
|
return \html_writer::link($addlink, get_string('newtour', 'tool_usertours'));
|
|
}
|
|
|
|
/**
|
|
* Print the edit tour link.
|
|
*
|
|
* @param int $id The ID of the tour
|
|
*/
|
|
protected function print_edit_tour_link($id = null) {
|
|
echo $this->get_edit_tour_link($id);
|
|
}
|
|
|
|
/**
|
|
* Get the import tour link.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function get_import_tour_link() {
|
|
$importlink = helper::get_import_tour_link();
|
|
return \html_writer::link($importlink, get_string('importtour', 'tool_usertours'));
|
|
}
|
|
|
|
/**
|
|
* Print the edit tour page.
|
|
*
|
|
* @param int $id The ID of the tour
|
|
*/
|
|
protected function edit_tour($id = null) {
|
|
global $PAGE;
|
|
if ($id) {
|
|
$tour = tour::instance($id);
|
|
$PAGE->navbar->add($tour->get_name(), $tour->get_edit_link());
|
|
|
|
} else {
|
|
$tour = new tour();
|
|
$PAGE->navbar->add(get_string('newtour', 'tool_usertours'), $tour->get_edit_link());
|
|
}
|
|
|
|
$form = new forms\edittour($tour);
|
|
|
|
if ($form->is_cancelled()) {
|
|
redirect(helper::get_list_tour_link());
|
|
} else if ($data = $form->get_data()) {
|
|
// Creating a new tour.
|
|
$tour->set_name($data->name);
|
|
$tour->set_description($data->description);
|
|
$tour->set_pathmatch($data->pathmatch);
|
|
$tour->set_enabled(!empty($data->enabled));
|
|
|
|
foreach (configuration::get_defaultable_keys() as $key) {
|
|
$tour->set_config($key, $data->$key);
|
|
}
|
|
|
|
// Save filter values.
|
|
foreach (helper::get_all_filters() as $filterclass) {
|
|
$filterclass::save_filter_values_from_form($tour, $data);
|
|
}
|
|
|
|
$tour->persist();
|
|
|
|
redirect(helper::get_list_tour_link());
|
|
} else {
|
|
if (empty($tour)) {
|
|
$this->header('newtour');
|
|
} else {
|
|
if (!empty($tour->get_config(self::CONFIG_SHIPPED_TOUR))) {
|
|
notification::add(get_string('modifyshippedtourwarning', 'tool_usertours'), notification::WARNING);
|
|
}
|
|
|
|
$this->header($tour->get_name());
|
|
$data = $tour->prepare_data_for_form();
|
|
|
|
// Prepare filter values for the form.
|
|
foreach (helper::get_all_filters() as $filterclass) {
|
|
$filterclass::prepare_filter_values_for_form($tour, $data);
|
|
}
|
|
$form->set_data($data);
|
|
}
|
|
|
|
$form->display();
|
|
$this->footer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print the export tour page.
|
|
*
|
|
* @param int $id The ID of the tour
|
|
*/
|
|
protected function export_tour($id) {
|
|
$tour = tour::instance($id);
|
|
|
|
// Grab the full data record.
|
|
$export = $tour->to_record();
|
|
|
|
// Remove the id.
|
|
unset($export->id);
|
|
|
|
// Set the version.
|
|
$export->version = get_config('tool_usertours', 'version');
|
|
|
|
// Step export.
|
|
$export->steps = [];
|
|
foreach ($tour->get_steps() as $step) {
|
|
$record = $step->to_record();
|
|
unset($record->id);
|
|
unset($record->tourid);
|
|
|
|
$export->steps[] = $record;
|
|
}
|
|
|
|
$exportstring = json_encode($export);
|
|
|
|
$filename = 'tour_export_' . $tour->get_id() . '_' . time() . '.json';
|
|
|
|
// Force download.
|
|
send_file($exportstring, $filename, 0, 0, true, true);
|
|
}
|
|
|
|
/**
|
|
* Handle tour import.
|
|
*/
|
|
protected function import_tour() {
|
|
global $PAGE;
|
|
$PAGE->navbar->add(get_string('importtour', 'tool_usertours'), helper::get_import_tour_link());
|
|
|
|
$form = new forms\importtour();
|
|
|
|
if ($form->is_cancelled()) {
|
|
redirect(helper::get_list_tour_link());
|
|
} else if ($form->get_data()) {
|
|
// Importing a tour.
|
|
$tourconfigraw = $form->get_file_content('tourconfig');
|
|
$tour = self::import_tour_from_json($tourconfigraw);
|
|
|
|
redirect($tour->get_view_link());
|
|
} else {
|
|
$this->header();
|
|
$form->display();
|
|
$this->footer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print the view tour page.
|
|
*
|
|
* @param int $tourid The ID of the tour to display.
|
|
*/
|
|
protected function view_tour($tourid) {
|
|
global $PAGE;
|
|
$tour = helper::get_tour($tourid);
|
|
|
|
$PAGE->navbar->add($tour->get_name(), $tour->get_view_link());
|
|
|
|
$this->header($tour->get_name());
|
|
echo \html_writer::span(get_string('viewtour_info', 'tool_usertours', [
|
|
'tourname' => $tour->get_name(),
|
|
'path' => $tour->get_pathmatch(),
|
|
]));
|
|
echo \html_writer::div(get_string('viewtour_edit', 'tool_usertours', [
|
|
'editlink' => $tour->get_edit_link()->out(),
|
|
'resetlink' => $tour->get_reset_link()->out(),
|
|
]));
|
|
|
|
$table = new table\step_list($tourid);
|
|
foreach ($tour->get_steps() as $step) {
|
|
$table->add_data_keyed($table->format_row($step));
|
|
}
|
|
|
|
$table->finish_output();
|
|
$this->print_edit_step_link($tourid);
|
|
|
|
// JS for Step management.
|
|
$PAGE->requires->js_call_amd('tool_usertours/managesteps', 'setup');
|
|
|
|
$this->footer();
|
|
}
|
|
|
|
/**
|
|
* Duplicate an existing tour.
|
|
*
|
|
* @param int $tourid The ID of the tour to duplicate.
|
|
*/
|
|
protected function duplicate_tour($tourid) {
|
|
$tour = helper::get_tour($tourid);
|
|
|
|
$export = $tour->to_record();
|
|
// Remove the id.
|
|
unset($export->id);
|
|
|
|
// Set the version.
|
|
$export->version = get_config('tool_usertours', 'version');
|
|
|
|
$export->name = get_string('duplicatetour_name', 'tool_usertours', $export->name);
|
|
|
|
// Step export.
|
|
$export->steps = [];
|
|
foreach ($tour->get_steps() as $step) {
|
|
$record = $step->to_record();
|
|
unset($record->id);
|
|
unset($record->tourid);
|
|
|
|
$export->steps[] = $record;
|
|
}
|
|
|
|
$exportstring = json_encode($export);
|
|
$newtour = self::import_tour_from_json($exportstring);
|
|
|
|
redirect($newtour->get_view_link());
|
|
}
|
|
|
|
/**
|
|
* Show the tour.
|
|
*
|
|
* @param int $tourid The ID of the tour to display.
|
|
*/
|
|
protected function show_tour($tourid) {
|
|
$this->show_hide_tour($tourid, 1);
|
|
}
|
|
|
|
/**
|
|
* Hide the tour.
|
|
*
|
|
* @param int $tourid The ID of the tour to display.
|
|
*/
|
|
protected function hide_tour($tourid) {
|
|
$this->show_hide_tour($tourid, 0);
|
|
}
|
|
|
|
/**
|
|
* Show or Hide the tour.
|
|
*
|
|
* @param int $tourid The ID of the tour to display.
|
|
* @param int $visibility The intended visibility.
|
|
*/
|
|
protected function show_hide_tour($tourid, $visibility) {
|
|
global $DB;
|
|
|
|
require_sesskey();
|
|
|
|
$tour = $DB->get_record('tool_usertours_tours', array('id' => $tourid));
|
|
$tour->enabled = $visibility;
|
|
$DB->update_record('tool_usertours_tours', $tour);
|
|
|
|
redirect(helper::get_list_tour_link());
|
|
}
|
|
|
|
/**
|
|
* Delete the tour.
|
|
*
|
|
* @param int $tourid The ID of the tour to remove.
|
|
*/
|
|
protected function delete_tour($tourid) {
|
|
require_sesskey();
|
|
|
|
$tour = tour::instance($tourid);
|
|
$tour->remove();
|
|
|
|
redirect(helper::get_list_tour_link());
|
|
}
|
|
|
|
/**
|
|
* Reset the tour state for all users.
|
|
*
|
|
* @param int $tourid The ID of the tour to remove.
|
|
*/
|
|
protected function reset_tour_for_all($tourid) {
|
|
require_sesskey();
|
|
|
|
$tour = tour::instance($tourid);
|
|
$tour->mark_major_change();
|
|
|
|
redirect(helper::get_view_tour_link($tourid), get_string('tour_resetforall', 'tool_usertours'));
|
|
}
|
|
|
|
/**
|
|
* Get all tours for the current page URL.
|
|
*
|
|
* @param bool $reset Forcibly update the current tours
|
|
* @return array
|
|
*/
|
|
public static function get_current_tours($reset = false): array {
|
|
global $PAGE;
|
|
|
|
static $tours = false;
|
|
|
|
if ($tours === false || $reset) {
|
|
$tours = self::get_matching_tours($PAGE->url);
|
|
}
|
|
|
|
return $tours;
|
|
}
|
|
|
|
/**
|
|
* Get all tours matching the specified URL.
|
|
*
|
|
* @param moodle_url $pageurl The URL to match.
|
|
* @return array
|
|
*/
|
|
public static function get_matching_tours(\moodle_url $pageurl): array {
|
|
global $PAGE, $USER;
|
|
|
|
// The following three checks make sure that the user is fully ready to use the site. If not, we do not show any tours.
|
|
// We need the user to get properly set up so that all require_login() and other bits work as expected.
|
|
|
|
if (user_not_fully_set_up($USER)) {
|
|
return [];
|
|
}
|
|
|
|
if (get_user_preferences('auth_forcepasswordchange', false)) {
|
|
return [];
|
|
}
|
|
|
|
if (empty($USER->policyagreed) && !is_siteadmin()) {
|
|
$manager = new \core_privacy\local\sitepolicy\manager();
|
|
|
|
if ($manager->is_defined(isguestuser())) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
$tours = cache::get_matching_tourdata($pageurl);
|
|
|
|
$matches = [];
|
|
if ($tours) {
|
|
$filters = helper::get_all_filters();
|
|
foreach ($tours as $record) {
|
|
$tour = tour::load_from_record($record);
|
|
if ($tour->is_enabled() && $tour->matches_all_filters($PAGE->context, $filters)) {
|
|
$matches[] = $tour;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $matches;
|
|
}
|
|
|
|
/**
|
|
* Import the provided tour JSON.
|
|
*
|
|
* @param string $json The tour configuration.
|
|
* @return tour
|
|
*/
|
|
public static function import_tour_from_json($json) {
|
|
$tourconfig = json_decode($json);
|
|
|
|
// We do not use this yet - we may do in the future.
|
|
unset($tourconfig->version);
|
|
|
|
$steps = $tourconfig->steps;
|
|
unset($tourconfig->steps);
|
|
|
|
$tourconfig->id = null;
|
|
$tourconfig->sortorder = null;
|
|
$tour = tour::load_from_record($tourconfig, true);
|
|
$tour->persist(true);
|
|
|
|
// Ensure that steps are orderered by their sortorder.
|
|
\core_collator::asort_objects_by_property($steps, 'sortorder', \core_collator::SORT_NUMERIC);
|
|
|
|
foreach ($steps as $stepconfig) {
|
|
$stepconfig->id = null;
|
|
$stepconfig->tourid = $tour->get_id();
|
|
$step = step::load_from_record($stepconfig, true);
|
|
$step->persist(true);
|
|
}
|
|
|
|
return $tour;
|
|
}
|
|
|
|
/**
|
|
* Helper to fetch the renderer.
|
|
*
|
|
* @return renderer
|
|
*/
|
|
protected function get_renderer() {
|
|
global $PAGE;
|
|
return $PAGE->get_renderer('tool_usertours');
|
|
}
|
|
|
|
/**
|
|
* Print the edit step link.
|
|
*
|
|
* @param int $tourid The ID of the tour.
|
|
* @param int $stepid The ID of the step.
|
|
* @return string
|
|
*/
|
|
protected function print_edit_step_link($tourid, $stepid = null) {
|
|
$addlink = helper::get_edit_step_link($tourid, $stepid);
|
|
$attributes = [];
|
|
if (empty($stepid)) {
|
|
$attributes['class'] = 'createstep';
|
|
}
|
|
echo \html_writer::link($addlink, get_string('newstep', 'tool_usertours'), $attributes);
|
|
}
|
|
|
|
/**
|
|
* Display the edit step form for the specified step.
|
|
*
|
|
* @param int $id The step to edit.
|
|
*/
|
|
protected function edit_step($id) {
|
|
global $PAGE;
|
|
|
|
if (isset($id)) {
|
|
$step = step::instance($id);
|
|
} else {
|
|
$step = new step();
|
|
$step->set_tourid(required_param('tourid', PARAM_INT));
|
|
}
|
|
|
|
$tour = $step->get_tour();
|
|
|
|
if (!empty($tour->get_config(self::CONFIG_SHIPPED_TOUR))) {
|
|
notification::add(get_string('modifyshippedtourwarning', 'tool_usertours'), notification::WARNING);
|
|
}
|
|
|
|
$PAGE->navbar->add($tour->get_name(), $tour->get_view_link());
|
|
if (isset($id)) {
|
|
$PAGE->navbar->add($step->get_title(), $step->get_edit_link());
|
|
} else {
|
|
$PAGE->navbar->add(get_string('newstep', 'tool_usertours'), $step->get_edit_link());
|
|
}
|
|
|
|
$form = new forms\editstep($step->get_edit_link(), $step);
|
|
if ($form->is_cancelled()) {
|
|
redirect($step->get_tour()->get_view_link());
|
|
} else if ($data = $form->get_data()) {
|
|
$step->handle_form_submission($form, $data);
|
|
$step->get_tour()->reset_step_sortorder();
|
|
redirect($step->get_tour()->get_view_link());
|
|
} else {
|
|
if (empty($id)) {
|
|
$this->header(get_string('newstep', 'tool_usertours'));
|
|
} else {
|
|
$this->header(get_string('editstep', 'tool_usertours', $step->get_title()));
|
|
}
|
|
$form->set_data($step->prepare_data_for_form());
|
|
|
|
$form->display();
|
|
$this->footer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move a tour up or down and redirect once complete.
|
|
*
|
|
* @param int $id The tour to move.
|
|
*/
|
|
protected function move_tour($id) {
|
|
require_sesskey();
|
|
|
|
$direction = required_param('direction', PARAM_INT);
|
|
|
|
$tour = tour::instance($id);
|
|
self::_move_tour($tour, $direction);
|
|
|
|
redirect(helper::get_list_tour_link());
|
|
}
|
|
|
|
/**
|
|
* Move a tour up or down.
|
|
*
|
|
* @param tour $tour The tour to move.
|
|
*
|
|
* @param int $direction
|
|
*/
|
|
protected static function _move_tour(tour $tour, $direction) {
|
|
// We can't move the first tour higher, nor the last tour any lower.
|
|
if (($tour->is_first_tour() && $direction == helper::MOVE_UP) ||
|
|
($tour->is_last_tour() && $direction == helper::MOVE_DOWN)) {
|
|
|
|
return;
|
|
}
|
|
|
|
$currentsortorder = $tour->get_sortorder();
|
|
$targetsortorder = $currentsortorder + $direction;
|
|
|
|
$swapwith = helper::get_tour_from_sortorder($targetsortorder);
|
|
|
|
// Set the sort order to something out of the way.
|
|
$tour->set_sortorder(-1);
|
|
$tour->persist();
|
|
|
|
// Swap the two sort orders.
|
|
$swapwith->set_sortorder($currentsortorder);
|
|
$swapwith->persist();
|
|
|
|
$tour->set_sortorder($targetsortorder);
|
|
$tour->persist();
|
|
}
|
|
|
|
/**
|
|
* Move a step up or down.
|
|
*
|
|
* @param int $id The step to move.
|
|
*/
|
|
protected function move_step($id) {
|
|
require_sesskey();
|
|
|
|
$direction = required_param('direction', PARAM_INT);
|
|
|
|
$step = step::instance($id);
|
|
$currentsortorder = $step->get_sortorder();
|
|
$targetsortorder = $currentsortorder + $direction;
|
|
|
|
$tour = $step->get_tour();
|
|
$swapwith = helper::get_step_from_sortorder($tour->get_id(), $targetsortorder);
|
|
|
|
// Set the sort order to something out of the way.
|
|
$step->set_sortorder(-1);
|
|
$step->persist();
|
|
|
|
// Swap the two sort orders.
|
|
$swapwith->set_sortorder($currentsortorder);
|
|
$swapwith->persist();
|
|
|
|
$step->set_sortorder($targetsortorder);
|
|
$step->persist();
|
|
|
|
// Reset the sort order.
|
|
$tour->reset_step_sortorder();
|
|
redirect($tour->get_view_link());
|
|
}
|
|
|
|
/**
|
|
* Delete the step.
|
|
*
|
|
* @param int $stepid The ID of the step to remove.
|
|
*/
|
|
protected function delete_step($stepid) {
|
|
require_sesskey();
|
|
|
|
$step = step::instance($stepid);
|
|
$tour = $step->get_tour();
|
|
|
|
$step->remove();
|
|
redirect($tour->get_view_link());
|
|
}
|
|
|
|
/**
|
|
* Make sure all of the default tours that are shipped with Moodle are created
|
|
* and up to date with the latest version.
|
|
*/
|
|
public static function update_shipped_tours() {
|
|
global $DB, $CFG;
|
|
|
|
// A list of tours that are shipped with Moodle. They are in
|
|
// the format filename => version. The version value needs to
|
|
// be increased if the tour has been updated.
|
|
$shippedtours = [
|
|
];
|
|
|
|
// These are tours that we used to ship but don't ship any longer.
|
|
// We do not remove them, but we do disable them.
|
|
$unshippedtours = [
|
|
// Formerly included in Moodle 3.2.0.
|
|
'boost_administrator.json' => 1,
|
|
'boost_course_view.json' => 1,
|
|
|
|
// Formerly included in Moodle 3.6.0.
|
|
'36_dashboard.json' => 3,
|
|
'36_messaging.json' => 3,
|
|
];
|
|
|
|
$existingtourrecords = $DB->get_recordset('tool_usertours_tours');
|
|
|
|
// Get all of the existing shipped tours and check if they need to be
|
|
// updated.
|
|
foreach ($existingtourrecords as $tourrecord) {
|
|
$tour = tour::load_from_record($tourrecord);
|
|
|
|
if (!empty($tour->get_config(self::CONFIG_SHIPPED_TOUR))) {
|
|
$filename = $tour->get_config(self::CONFIG_SHIPPED_FILENAME);
|
|
$version = $tour->get_config(self::CONFIG_SHIPPED_VERSION);
|
|
|
|
// If we know about this tour (otherwise leave it as is).
|
|
if (isset($shippedtours[$filename])) {
|
|
// And the version in the DB is an older version.
|
|
if ($version < $shippedtours[$filename]) {
|
|
// Remove the old version because it's been updated
|
|
// and needs to be recreated.
|
|
$tour->remove();
|
|
} else {
|
|
// The tour has not been updated so we don't need to
|
|
// do anything with it.
|
|
unset($shippedtours[$filename]);
|
|
}
|
|
}
|
|
|
|
if (isset($unshippedtours[$filename])) {
|
|
if ($version <= $unshippedtours[$filename]) {
|
|
$tour = tour::instance($tour->get_id());
|
|
$tour->set_enabled(tour::DISABLED);
|
|
$tour->persist();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$existingtourrecords->close();
|
|
|
|
// Ensure we correct the sortorder in any existing tours, prior to adding latest shipped tours.
|
|
helper::reset_tour_sortorder();
|
|
|
|
foreach (array_reverse($shippedtours) as $filename => $version) {
|
|
$filepath = $CFG->dirroot . "/{$CFG->admin}/tool/usertours/tours/" . $filename;
|
|
$tourjson = file_get_contents($filepath);
|
|
$tour = self::import_tour_from_json($tourjson);
|
|
|
|
// Set some additional config data to record that this tour was
|
|
// added as a shipped tour.
|
|
$tour->set_config(self::CONFIG_SHIPPED_TOUR, true);
|
|
$tour->set_config(self::CONFIG_SHIPPED_FILENAME, $filename);
|
|
$tour->set_config(self::CONFIG_SHIPPED_VERSION, $version);
|
|
|
|
// Bump new tours to the top of the list.
|
|
while ($tour->get_sortorder() > 0) {
|
|
self::_move_tour($tour, helper::MOVE_UP);
|
|
}
|
|
|
|
if (defined('BEHAT_SITE_RUNNING') || (defined('PHPUNIT_TEST') && PHPUNIT_TEST)) {
|
|
// Disable this tour if this is behat or phpunit.
|
|
$tour->set_enabled(false);
|
|
}
|
|
|
|
$tour->persist();
|
|
}
|
|
}
|
|
}
|