You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
770 lines
20 KiB
770 lines
20 KiB
<?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 class.
|
|
*
|
|
* @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();
|
|
|
|
/**
|
|
* Tour class.
|
|
*
|
|
* @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class tour {
|
|
|
|
/**
|
|
* The tour is currently disabled
|
|
*
|
|
* @var DISABLED
|
|
*/
|
|
const DISABLED = 0;
|
|
|
|
/**
|
|
* The tour is currently disabled
|
|
*
|
|
* @var DISABLED
|
|
*/
|
|
const ENABLED = 1;
|
|
|
|
/**
|
|
* The user preference value to indicate the time of completion of the tour for a user.
|
|
*
|
|
* @var TOUR_LAST_COMPLETED_BY_USER
|
|
*/
|
|
const TOUR_LAST_COMPLETED_BY_USER = 'tool_usertours_tour_completion_time_';
|
|
|
|
/**
|
|
* The user preference value to indicate the time that a user last requested to see the tour.
|
|
*
|
|
* @var TOUR_REQUESTED_BY_USER
|
|
*/
|
|
const TOUR_REQUESTED_BY_USER = 'tool_usertours_tour_reset_time_';
|
|
|
|
/**
|
|
* @var $id The tour ID.
|
|
*/
|
|
protected $id;
|
|
|
|
/**
|
|
* @var $name The tour name.
|
|
*/
|
|
protected $name;
|
|
|
|
/**
|
|
* @var $description The tour description.
|
|
*/
|
|
protected $description;
|
|
|
|
/**
|
|
* @var $pathmatch The tour pathmatch.
|
|
*/
|
|
protected $pathmatch;
|
|
|
|
/**
|
|
* @var $enabled The tour enabled state.
|
|
*/
|
|
protected $enabled;
|
|
|
|
/**
|
|
* @var $sortorder The sort order.
|
|
*/
|
|
protected $sortorder;
|
|
|
|
/**
|
|
* @var $dirty Whether the current view of the tour has been modified.
|
|
*/
|
|
protected $dirty = false;
|
|
|
|
/**
|
|
* @var $config The configuration object for the tour.
|
|
*/
|
|
protected $config;
|
|
|
|
/**
|
|
* @var $filtervalues The filter configuration object for the tour.
|
|
*/
|
|
protected $filtervalues;
|
|
|
|
/**
|
|
* @var $steps The steps in this tour.
|
|
*/
|
|
protected $steps = [];
|
|
|
|
/**
|
|
* Create an instance of the specified tour.
|
|
*
|
|
* @param int $id The ID of the tour to load.
|
|
* @return tour
|
|
*/
|
|
public static function instance($id) {
|
|
$tour = new self();
|
|
return $tour->fetch($id);
|
|
}
|
|
|
|
/**
|
|
* Create an instance of tour from its provided DB record.
|
|
*
|
|
* @param stdClass $record The record of the tour to load.
|
|
* @param boolean $clean Clean the values.
|
|
* @return tour
|
|
*/
|
|
public static function load_from_record($record, $clean = false) {
|
|
$tour = new self();
|
|
return $tour->reload_from_record($record, $clean);
|
|
}
|
|
|
|
/**
|
|
* Fetch the specified tour into the current object.
|
|
*
|
|
* @param int $id The ID of the tour to fetch.
|
|
* @return tour
|
|
*/
|
|
protected function fetch($id) {
|
|
global $DB;
|
|
|
|
return $this->reload_from_record(
|
|
$DB->get_record('tool_usertours_tours', array('id' => $id), '*', MUST_EXIST)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Reload the current tour from database.
|
|
*
|
|
* @return tour
|
|
*/
|
|
protected function reload() {
|
|
return $this->fetch($this->id);
|
|
}
|
|
|
|
/**
|
|
* Reload the tour into the current object.
|
|
*
|
|
* @param stdClass $record The record to reload.
|
|
* @param boolean $clean Clean the values.
|
|
* @return tour
|
|
*/
|
|
protected function reload_from_record($record, $clean = false) {
|
|
$this->id = $record->id;
|
|
if (!property_exists($record, 'description')) {
|
|
if (property_exists($record, 'comment')) {
|
|
$record->description = $record->comment;
|
|
unset($record->comment);
|
|
}
|
|
}
|
|
if ($clean) {
|
|
$this->name = clean_param($record->name, PARAM_TEXT);
|
|
$this->description = clean_text($record->description);
|
|
} else {
|
|
$this->name = $record->name;
|
|
$this->description = $record->description;
|
|
}
|
|
$this->pathmatch = $record->pathmatch;
|
|
$this->enabled = $record->enabled;
|
|
if (isset($record->sortorder)) {
|
|
$this->sortorder = $record->sortorder;
|
|
}
|
|
$this->config = json_decode($record->configdata);
|
|
$this->dirty = false;
|
|
$this->steps = [];
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Fetch all steps in the tour.
|
|
*
|
|
* @return stdClass[]
|
|
*/
|
|
public function get_steps() {
|
|
if (empty($this->steps)) {
|
|
$this->steps = helper::get_steps($this->id);
|
|
}
|
|
|
|
return $this->steps;
|
|
}
|
|
|
|
/**
|
|
* Count the number of steps in the tour.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function count_steps() {
|
|
return count($this->get_steps());
|
|
}
|
|
|
|
/**
|
|
* The ID of the tour.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_id() {
|
|
return $this->id;
|
|
}
|
|
|
|
/**
|
|
* The name of the tour.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_name() {
|
|
return $this->name;
|
|
}
|
|
|
|
/**
|
|
* Set the name of the tour to the specified value.
|
|
*
|
|
* @param string $value The new name.
|
|
* @return $this
|
|
*/
|
|
public function set_name($value) {
|
|
$this->name = clean_param($value, PARAM_TEXT);
|
|
$this->dirty = true;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* The description associated with the tour.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_description() {
|
|
return $this->description;
|
|
}
|
|
|
|
/**
|
|
* Set the description of the tour to the specified value.
|
|
*
|
|
* @param string $value The new description.
|
|
* @return $this
|
|
*/
|
|
public function set_description($value) {
|
|
$this->description = clean_text($value);
|
|
$this->dirty = true;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* The path match for the tour.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_pathmatch() {
|
|
return $this->pathmatch;
|
|
}
|
|
|
|
/**
|
|
* Set the patchmatch of the tour to the specified value.
|
|
*
|
|
* @param string $value The new patchmatch.
|
|
* @return $this
|
|
*/
|
|
public function set_pathmatch($value) {
|
|
$this->pathmatch = $value;
|
|
$this->dirty = true;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* The enabled state of the tour.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_enabled() {
|
|
return $this->enabled;
|
|
}
|
|
|
|
/**
|
|
* Whether the tour is currently enabled.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function is_enabled() {
|
|
return ($this->enabled == self::ENABLED);
|
|
}
|
|
|
|
/**
|
|
* Set the enabled state of the tour to the specified value.
|
|
*
|
|
* @param boolean $value The new state.
|
|
* @return $this
|
|
*/
|
|
public function set_enabled($value) {
|
|
$this->enabled = $value;
|
|
$this->dirty = true;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* The link to view this tour.
|
|
*
|
|
* @return moodle_url
|
|
*/
|
|
public function get_view_link() {
|
|
return helper::get_view_tour_link($this->id);
|
|
}
|
|
|
|
/**
|
|
* The link to edit this tour.
|
|
*
|
|
* @return moodle_url
|
|
*/
|
|
public function get_edit_link() {
|
|
return helper::get_edit_tour_link($this->id);
|
|
}
|
|
|
|
/**
|
|
* The link to reset the state of this tour for all users.
|
|
*
|
|
* @return moodle_url
|
|
*/
|
|
public function get_reset_link() {
|
|
return helper::get_reset_tour_for_all_link($this->id);
|
|
}
|
|
|
|
/**
|
|
* The link to export this tour.
|
|
*
|
|
* @return moodle_url
|
|
*/
|
|
public function get_export_link() {
|
|
return helper::get_export_tour_link($this->id);
|
|
}
|
|
|
|
/**
|
|
* The link to remove this tour.
|
|
*
|
|
* @return moodle_url
|
|
*/
|
|
public function get_delete_link() {
|
|
return helper::get_delete_tour_link($this->id);
|
|
}
|
|
|
|
/**
|
|
* Prepare this tour for saving to the database.
|
|
*
|
|
* @return object
|
|
*/
|
|
public function to_record() {
|
|
return (object) array(
|
|
'id' => $this->id,
|
|
'name' => $this->name,
|
|
'description' => $this->description,
|
|
'pathmatch' => $this->pathmatch,
|
|
'enabled' => $this->enabled,
|
|
'sortorder' => $this->sortorder,
|
|
'configdata' => json_encode($this->config),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the current sortorder for this tour.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_sortorder() {
|
|
return (int) $this->sortorder;
|
|
}
|
|
|
|
/**
|
|
* Whether this tour is the first tour.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function is_first_tour() {
|
|
return ($this->get_sortorder() === 0);
|
|
}
|
|
|
|
/**
|
|
* Whether this tour is the last tour.
|
|
*
|
|
* @param int $tourcount The pre-fetched count of tours
|
|
* @return boolean
|
|
*/
|
|
public function is_last_tour($tourcount = null) {
|
|
if ($tourcount === null) {
|
|
$tourcount = helper::count_tours();
|
|
}
|
|
return ($this->get_sortorder() === ($tourcount - 1));
|
|
}
|
|
|
|
/**
|
|
* Set the sortorder for this tour.
|
|
*
|
|
* @param int $value The new sortorder to use.
|
|
* @return $this
|
|
*/
|
|
public function set_sortorder($value) {
|
|
$this->sortorder = $value;
|
|
$this->dirty = true;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Calculate the next sort-order value.
|
|
*
|
|
* @return int
|
|
*/
|
|
protected function calculate_sortorder() {
|
|
$this->sortorder = helper::count_tours();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the link to move this tour up in the sortorder.
|
|
*
|
|
* @return moodle_url
|
|
*/
|
|
public function get_moveup_link() {
|
|
return helper::get_move_tour_link($this->get_id(), helper::MOVE_UP);
|
|
}
|
|
|
|
/**
|
|
* Get the link to move this tour down in the sortorder.
|
|
*
|
|
* @return moodle_url
|
|
*/
|
|
public function get_movedown_link() {
|
|
return helper::get_move_tour_link($this->get_id(), helper::MOVE_DOWN);
|
|
}
|
|
|
|
/**
|
|
* Get the value of the specified configuration item.
|
|
*
|
|
* @param string $key The configuration key to set.
|
|
* @param mixed $default The default value to use if a value was not found.
|
|
* @return mixed
|
|
*/
|
|
public function get_config($key = null, $default = null) {
|
|
if ($this->config === null) {
|
|
$this->config = (object) array();
|
|
}
|
|
if ($key === null) {
|
|
return $this->config;
|
|
}
|
|
|
|
if (property_exists($this->config, $key)) {
|
|
return $this->config->$key;
|
|
}
|
|
|
|
if ($default !== null) {
|
|
return $default;
|
|
}
|
|
|
|
return configuration::get_default_value($key);
|
|
}
|
|
|
|
/**
|
|
* Set the configuration item as specified.
|
|
*
|
|
* @param string $key The configuration key to set.
|
|
* @param mixed $value The new value for the configuration item.
|
|
* @return $this
|
|
*/
|
|
public function set_config($key, $value) {
|
|
if ($this->config === null) {
|
|
$this->config = (object) array();
|
|
}
|
|
$this->config->$key = $value;
|
|
$this->dirty = true;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Save the tour and it's configuration to the database.
|
|
*
|
|
* @param boolean $force Whether to force writing to the database.
|
|
* @return $this
|
|
*/
|
|
public function persist($force = false) {
|
|
global $DB;
|
|
|
|
if (!$this->dirty && !$force) {
|
|
return $this;
|
|
}
|
|
|
|
if ($this->id) {
|
|
$record = $this->to_record();
|
|
$DB->update_record('tool_usertours_tours', $record);
|
|
} else {
|
|
$this->calculate_sortorder();
|
|
$record = $this->to_record();
|
|
unset($record->id);
|
|
$this->id = $DB->insert_record('tool_usertours_tours', $record);
|
|
}
|
|
|
|
$this->reload();
|
|
|
|
// Notify the cache that a tour has changed.
|
|
cache::notify_tour_change();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Remove this step.
|
|
*/
|
|
public function remove() {
|
|
global $DB;
|
|
|
|
if ($this->id === null) {
|
|
// Nothing to delete - this tour has not been persisted.
|
|
return null;
|
|
}
|
|
|
|
// Delete all steps associated with this tour.
|
|
// Note, although they are currently just DB records, there may be other components in the future.
|
|
foreach ($this->get_steps() as $step) {
|
|
$step->remove();
|
|
}
|
|
|
|
// Remove the configuration for the tour.
|
|
$DB->delete_records('tool_usertours_tours', array('id' => $this->id));
|
|
|
|
helper::reset_tour_sortorder();
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Reset the sortorder for all steps in the tour.
|
|
*
|
|
* @return $this
|
|
*/
|
|
public function reset_step_sortorder() {
|
|
global $DB;
|
|
$steps = $DB->get_records('tool_usertours_steps', array('tourid' => $this->id), 'sortorder ASC', 'id');
|
|
|
|
$index = 0;
|
|
foreach ($steps as $step) {
|
|
$DB->set_field('tool_usertours_steps', 'sortorder', $index, array('id' => $step->id));
|
|
$index++;
|
|
}
|
|
|
|
// Notify of a change to the step configuration.
|
|
// Note: Do not notify of a tour change here. This is only a step change for a tour.
|
|
cache::notify_step_change($this->get_id());
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Whether this tour should be displayed to the user.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function should_show_for_user() {
|
|
if (!$this->is_enabled()) {
|
|
// The tour is disabled - it should not be shown.
|
|
return false;
|
|
}
|
|
|
|
if ($tourcompletiondate = get_user_preferences(self::TOUR_LAST_COMPLETED_BY_USER . $this->get_id(), null)) {
|
|
if ($tourresetdate = get_user_preferences(self::TOUR_REQUESTED_BY_USER . $this->get_id(), null)) {
|
|
if ($tourresetdate >= $tourcompletiondate) {
|
|
return true;
|
|
}
|
|
}
|
|
$lastmajorupdate = $this->get_config('majorupdatetime', time());
|
|
if ($tourcompletiondate > $lastmajorupdate) {
|
|
// The user has completed the tour since the last major update.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get the key for this tour.
|
|
* This is used in the session cookie to determine whether the user has seen this tour before.
|
|
*/
|
|
public function get_tour_key() {
|
|
global $USER;
|
|
|
|
$tourtime = $this->get_config('majorupdatetime', null);
|
|
|
|
if ($tourtime === null) {
|
|
// This tour has no majorupdate time.
|
|
// Set one now to prevent repeated displays to the user.
|
|
$this->set_config('majorupdatetime', time());
|
|
$this->persist();
|
|
$tourtime = $this->get_config('majorupdatetime', null);
|
|
}
|
|
|
|
if ($userresetdate = get_user_preferences(self::TOUR_REQUESTED_BY_USER . $this->get_id(), null)) {
|
|
$tourtime = max($tourtime, $userresetdate);
|
|
}
|
|
|
|
return sprintf('tool_usertours_%d_%d_%s', $USER->id, $this->get_id(), $tourtime);
|
|
}
|
|
|
|
/**
|
|
* Reset the requested by user date.
|
|
*
|
|
* @return $this
|
|
*/
|
|
public function request_user_reset() {
|
|
set_user_preference(self::TOUR_REQUESTED_BY_USER . $this->get_id(), time());
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Mark this tour as completed for this user.
|
|
*
|
|
* @return $this
|
|
*/
|
|
public function mark_user_completed() {
|
|
set_user_preference(self::TOUR_LAST_COMPLETED_BY_USER . $this->get_id(), time());
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Update a tour giving it a new major update time.
|
|
* This will ensure that it is displayed to all users, even those who have already seen it.
|
|
*
|
|
* @return $this
|
|
*/
|
|
public function mark_major_change() {
|
|
global $DB;
|
|
|
|
// Clear old reset and completion notes.
|
|
$DB->delete_records('user_preferences', ['name' => self::TOUR_LAST_COMPLETED_BY_USER . $this->get_id()]);
|
|
$DB->delete_records('user_preferences', ['name' => self::TOUR_REQUESTED_BY_USER . $this->get_id()]);
|
|
$this->set_config('majorupdatetime', time());
|
|
$this->persist();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add the step configuration to the form.
|
|
*
|
|
* @param MoodleQuickForm $mform The form to add configuration to.
|
|
* @return $this
|
|
*/
|
|
public function add_config_to_form(\MoodleQuickForm &$mform) {
|
|
$options = configuration::get_placement_options();
|
|
$mform->addElement('select', 'placement', get_string('placement', 'tool_usertours'), $options);
|
|
$mform->addHelpButton('placement', 'placement', 'tool_usertours');
|
|
|
|
$this->add_config_field_to_form($mform, 'orphan');
|
|
$this->add_config_field_to_form($mform, 'backdrop');
|
|
$this->add_config_field_to_form($mform, 'reflex');
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add the specified step field configuration to the form.
|
|
*
|
|
* @param MoodleQuickForm $mform The form to add configuration to.
|
|
* @param string $key The key to add.
|
|
* @return $this
|
|
*/
|
|
protected function add_config_field_to_form(\MoodleQuickForm &$mform, $key) {
|
|
$options = [
|
|
true => get_string('yes'),
|
|
false => get_string('no'),
|
|
];
|
|
$mform->addElement('select', $key, get_string($key, 'tool_usertours'), $options);
|
|
$mform->setDefault($key, configuration::get_default_value($key));
|
|
$mform->addHelpButton($key, $key, 'tool_usertours');
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Prepare the configuration data for the moodle form.
|
|
*
|
|
* @return object
|
|
*/
|
|
public function prepare_data_for_form() {
|
|
$data = $this->to_record();
|
|
foreach (configuration::get_defaultable_keys() as $key) {
|
|
$data->$key = $this->get_config($key, configuration::get_default_value($key));
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Get the configured filter values.
|
|
*
|
|
* @param string $filter The filter to retrieve values for.
|
|
* @return array
|
|
*/
|
|
public function get_filter_values($filter) {
|
|
if ($allvalues = (array) $this->get_config('filtervalues')) {
|
|
if (isset($allvalues[$filter])) {
|
|
return $allvalues[$filter];
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Set the values for the specified filter.
|
|
*
|
|
* @param string $filter The filter to set.
|
|
* @param array $values The values to set.
|
|
* @return $this
|
|
*/
|
|
public function set_filter_values($filter, array $values = []) {
|
|
$allvalues = (array) $this->get_config('filtervalues', []);
|
|
$allvalues[$filter] = $values;
|
|
|
|
return $this->set_config('filtervalues', $allvalues);
|
|
}
|
|
|
|
/**
|
|
* Check whether this tour matches all filters.
|
|
*
|
|
* @param context $context The context to check
|
|
* @return bool
|
|
*/
|
|
public function matches_all_filters(\context $context) {
|
|
$filters = helper::get_all_filters();
|
|
|
|
// All filters must match.
|
|
// If any one filter fails to match, we return false.
|
|
foreach ($filters as $filterclass) {
|
|
if (!$filterclass::filter_matches($this, $context)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|