Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?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/>.namespace tool_usertours;/*** 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*/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 int Whether to show the tour only until it has been marked complete */const SHOW_TOUR_UNTIL_COMPLETE = 1;/** @var int Whether to show the tour every time a page matches */const SHOW_TOUR_ON_EACH_PAGE_VISIT = 2;/*** @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 $endtourlabel The end tour label.*/protected $endtourlabel;/*** @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 = [];/*** @var bool $displaystepnumbers Display the step numbers in this tour.*/protected $displaystepnumbers = true;/*** 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', ['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->endtourlabel = $record->endtourlabel ?? null;$this->config = json_decode($record->configdata);$this->dirty = false;$this->steps = [];$this->displaystepnumbers = !empty($record->displaystepnumbers);return $this;}/*** Fetch all steps in the tour.** @return step[]*/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 end tour label for the tour.** @return string*/public function get_endtourlabel(): string {if ($this->endtourlabel) {$label = helper::get_string_from_input($this->endtourlabel);} else if ($this->count_steps() == 1) {$label = get_string('endonesteptour', 'tool_usertours');} else {$label = get_string('endtour', 'tool_usertours');}return $label;}/*** Set the endtourlabel of the tour to the specified value.** @param string $value* @return $this*/public function set_endtourlabel(string $value): tour {$this->endtourlabel = $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 duplicate this tour.** @return moodle_url*/public function get_duplicate_link() {return helper::get_duplicate_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) ['id' => $this->id,'name' => $this->name,'description' => $this->description,'pathmatch' => $this->pathmatch,'enabled' => $this->enabled,'sortorder' => $this->sortorder,'endtourlabel' => $this->endtourlabel,'configdata' => json_encode($this->config),'displaystepnumbers' => $this->displaystepnumbers,];}/*** 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) [];}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) [];}$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', ['id' => $this->id]);helper::reset_tour_sortorder();$this->remove_user_preferences();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', ['tourid' => $this->id], 'sortorder ASC', 'id');$index = 0;foreach ($steps as $step) {$DB->set_field('tool_usertours_steps', 'sortorder', $index, ['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;}/*** Remove stored user preferences for the tour*/protected function remove_user_preferences(): void {global $DB;$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()]);}/*** 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 ($this->get_showtourwhen() === self::SHOW_TOUR_ON_EACH_PAGE_VISIT) {// The tour should be shown on every page visit.return true;}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() {// Clear old reset and completion notes.$this->remove_user_preferences();$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();$data->showtourwhen = $this->get_showtourwhen();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.* @param array|null $filters Optional array of filters.* @return bool*/public function matches_all_filters(\context $context, array $filters = null): bool {if (!$filters) {$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;}/*** Gets all filter values for use in client side filters.** @param array $filters Array of clientside filters.* @return array*/public function get_client_filter_values(array $filters): array {$results = [];foreach ($filters as $filter) {$results[$filter::get_filter_name()] = $filter::get_client_side_values($this);}return $results;}/*** Set the value for the display step numbers setting.** @param bool $value True for enable.* @return $this*/public function set_display_step_numbers(bool $value): tour {$this->displaystepnumbers = $value;$this->dirty = true;return $this;}/*** Get the value of the display step numbers setting.** @return bool*/public function get_display_step_numbers(): bool {return $this->displaystepnumbers;}/*** Set the value for the when to show the tour.** @see self::SHOW_TOUR_UNTIL_COMPLETE* @see self::SHOW_TOUR_ON_EACH_PAGE_VISIT** @param int $value* @return self*/public function set_showtourwhen(int $value): tour {return $this->set_config('showtourwhen', $value);}/*** When to show the tour.** @see self::SHOW_TOUR_UNTIL_COMPLETE* @see self::SHOW_TOUR_ON_EACH_PAGE_VISIT** @return int*/public function get_showtourwhen(): int {return $this->get_config('showtourwhen', self::SHOW_TOUR_UNTIL_COMPLETE);}}