mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 06:18:28 +01:00
MDL-69739 tool_usertours: Add tour-level CSS selector
This commit is contained in:
parent
11b094be4c
commit
51caf76655
2
admin/tool/usertours/amd/build/filter_cssselector.min.js
vendored
Normal file
2
admin/tool/usertours/amd/build/filter_cssselector.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
define ("tool_usertours/filter_cssselector",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.filterMatches=void 0;a.filterMatches=function filterMatches(a){var b=a.filtervalues.cssselector;if(b[0]){return!!document.querySelector(b[0])}return!0}});
|
||||||
|
//# sourceMappingURL=filter_cssselector.min.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../src/filter_cssselector.js"],"names":["filterMatches","tourConfig","filterValues","filtervalues","cssselector","document","querySelector"],"mappings":"yKA+B6B,QAAhBA,CAAAA,aAAgB,CAASC,CAAT,CAAqB,CAC9C,GAAIC,CAAAA,CAAY,CAAGD,CAAU,CAACE,YAAX,CAAwBC,WAA3C,CACA,GAAIF,CAAY,CAAC,CAAD,CAAhB,CAAqB,CACjB,MAAO,CAAC,CAACG,QAAQ,CAACC,aAAT,CAAuBJ,CAAY,CAAC,CAAD,CAAnC,CACZ,CAED,QACH,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * CSS selector client side filter.\n *\n * @module tool_usertours/filter_cssselector\n * @class filter_cssselector\n * @package tool_usertours\n * @copyright 2020 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Checks whether the configured CSS selector exists on this page.\n *\n * @param {array} tourConfig The tour configuration.\n * @returns {boolean}\n */\nexport const filterMatches = function(tourConfig) {\n let filterValues = tourConfig.filtervalues.cssselector;\n if (filterValues[0]) {\n return !!document.querySelector(filterValues[0]);\n }\n // If there is no CSS selector configured, this page matches.\n return true;\n};\n"],"file":"filter_cssselector.min.js"}
|
@ -1,2 +1,2 @@
|
|||||||
define ("tool_usertours/usertours",["core/ajax","tool_usertours/tour","jquery","core/templates","core/str","core/log","core/notification"],function(a,b,c,d,e,f,g){var h={tourId:null,currentTour:null,context:null,init:function init(a,b,d){h.tourId=a;h.context=d;if("undefined"==typeof b){b=!0}if(b){h.fetchTour(a)}h.addResetLink();c("body").on("click","[data-action=\"tool_usertours/resetpagetour\"]",function(a){a.preventDefault();h.resetTourState(h.tourId)})},fetchTour:function fetchTour(b){M.util.js_pending("admin_usertour_fetchTour"+b);c.when(a.call([{methodname:"tool_usertours_fetch_and_start_tour",args:{tourid:b,context:h.context,pageurl:window.location.href}}])[0],d.render("tool_usertours/tourstep",{})).then(function(a,c){if(!a.hasOwnProperty("tourconfig")){return}return h.startBootstrapTour(b,c[0],a.tourconfig)}).always(function(){M.util.js_complete("admin_usertour_fetchTour"+b)}).fail(g.exception)},addResetLink:function addResetLink(){var a;M.util.js_pending("admin_usertour_addResetLink");if(c(".tool_usertours-resettourcontainer").length){a=c(".tool_usertours-resettourcontainer")}else if(c(".logininfo").length){a=c(".logininfo")}else if(c("footer").length){a=c("footer")}else{a=c("body")}d.render("tool_usertours/resettour",{}).then(function(b,c){d.appendNodeContents(a,b,c)}).always(function(){M.util.js_complete("admin_usertour_addResetLink")}).fail()},startBootstrapTour:function startBootstrapTour(a,c,d){if(h.currentTour){d.onEnd=null;h.currentTour.endTour();delete h.currentTour}d.eventHandlers={afterEnd:[h.markTourComplete],afterRender:[h.markStepShown]};d.tourName=d.name;delete d.name;d.template=c;d.steps=d.steps.map(function(a){if("undefined"!=typeof a.element){a.target=a.element;delete a.element}if("undefined"!=typeof a.reflex){a.moveOnClick=!!a.reflex;delete a.reflex}if("undefined"!=typeof a.content){a.body=a.content;delete a.content}return a});h.currentTour=new b(d);return h.currentTour.startTour()},markStepShown:function markStepShown(){var b=this.getStepConfig(this.getCurrentStepNumber());c.when(a.call([{methodname:"tool_usertours_step_shown",args:{tourid:h.tourId,context:h.context,pageurl:window.location.href,stepid:b.stepid,stepindex:this.getCurrentStepNumber()}}])[0]).fail(f.error)},markTourComplete:function markTourComplete(){var b=this.getStepConfig(this.getCurrentStepNumber());c.when(a.call([{methodname:"tool_usertours_complete_tour",args:{tourid:h.tourId,context:h.context,pageurl:window.location.href,stepid:b.stepid,stepindex:this.getCurrentStepNumber()}}])[0]).fail(f.error)},resetTourState:function resetTourState(b){c.when(a.call([{methodname:"tool_usertours_reset_tour",args:{tourid:b,context:h.context,pageurl:window.location.href}}])[0]).then(function(a){if(a.startTour){h.fetchTour(a.startTour)}}).fail(g.exception)}};return{init:h.init,resetTourState:h.resetTourState}});
|
define ("tool_usertours/usertours",["core/ajax","tool_usertours/tour","jquery","core/templates","core/str","core/log","core/notification"],function(a,b,c,d,e,f,g){var h={tourId:null,currentTour:null,init:function init(a,b){for(var d=[],e=0;e<b.length;e++){d[e]="tool_usertours/filter_"+b[e]}require(d,function(){var d=null;for(var k in a){for(var e=a[k],f=0,g;f<b.length;f++){g=arguments[f];if(g.filterMatches(e)){d=e}else{d=null;break}}if(d){break}}if(null===d){return}h.tourId=d.tourId;var j=d.startTour;if("undefined"==typeof j){j=!0}if(j){h.fetchTour(h.tourId)}h.addResetLink();c("body").on("click","[data-action=\"tool_usertours/resetpagetour\"]",function(a){a.preventDefault();h.resetTourState(h.tourId)})})},fetchTour:function fetchTour(b){M.util.js_pending("admin_usertour_fetchTour"+b);c.when(a.call([{methodname:"tool_usertours_fetch_and_start_tour",args:{tourid:b,context:M.cfg.contextid,pageurl:window.location.href}}])[0],d.render("tool_usertours/tourstep",{})).then(function(a,c){if(!a.hasOwnProperty("tourconfig")){return}return h.startBootstrapTour(b,c[0],a.tourconfig)}).always(function(){M.util.js_complete("admin_usertour_fetchTour"+b)}).fail(g.exception)},addResetLink:function addResetLink(){var a;M.util.js_pending("admin_usertour_addResetLink");if(c(".tool_usertours-resettourcontainer").length){a=c(".tool_usertours-resettourcontainer")}else if(c(".logininfo").length){a=c(".logininfo")}else if(c("footer").length){a=c("footer")}else{a=c("body")}d.render("tool_usertours/resettour",{}).then(function(b,c){d.appendNodeContents(a,b,c)}).always(function(){M.util.js_complete("admin_usertour_addResetLink")}).fail()},startBootstrapTour:function startBootstrapTour(a,c,d){if(h.currentTour){d.onEnd=null;h.currentTour.endTour();delete h.currentTour}d.eventHandlers={afterEnd:[h.markTourComplete],afterRender:[h.markStepShown]};d.tourName=d.name;delete d.name;d.template=c;d.steps=d.steps.map(function(a){if("undefined"!=typeof a.element){a.target=a.element;delete a.element}if("undefined"!=typeof a.reflex){a.moveOnClick=!!a.reflex;delete a.reflex}if("undefined"!=typeof a.content){a.body=a.content;delete a.content}return a});h.currentTour=new b(d);return h.currentTour.startTour()},markStepShown:function markStepShown(){var b=this.getStepConfig(this.getCurrentStepNumber());c.when(a.call([{methodname:"tool_usertours_step_shown",args:{tourid:h.tourId,context:M.cfg.contextid,pageurl:window.location.href,stepid:b.stepid,stepindex:this.getCurrentStepNumber()}}])[0]).fail(f.error)},markTourComplete:function markTourComplete(){var b=this.getStepConfig(this.getCurrentStepNumber());c.when(a.call([{methodname:"tool_usertours_complete_tour",args:{tourid:h.tourId,context:M.cfg.contextid,pageurl:window.location.href,stepid:b.stepid,stepindex:this.getCurrentStepNumber()}}])[0]).fail(f.error)},resetTourState:function resetTourState(b){c.when(a.call([{methodname:"tool_usertours_reset_tour",args:{tourid:b,context:M.cfg.contextid,pageurl:window.location.href}}])[0]).then(function(a){if(a.startTour){h.fetchTour(a.startTour)}}).fail(g.exception)}};return{init:h.init,resetTourState:h.resetTourState}});
|
||||||
//# sourceMappingURL=usertours.min.js.map
|
//# sourceMappingURL=usertours.min.js.map
|
||||||
|
File diff suppressed because one or more lines are too long
39
admin/tool/usertours/amd/src/filter_cssselector.js
Normal file
39
admin/tool/usertours/amd/src/filter_cssselector.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSS selector client side filter.
|
||||||
|
*
|
||||||
|
* @module tool_usertours/filter_cssselector
|
||||||
|
* @class filter_cssselector
|
||||||
|
* @package tool_usertours
|
||||||
|
* @copyright 2020 The Open University
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the configured CSS selector exists on this page.
|
||||||
|
*
|
||||||
|
* @param {array} tourConfig The tour configuration.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const filterMatches = function(tourConfig) {
|
||||||
|
let filterValues = tourConfig.filtervalues.cssselector;
|
||||||
|
if (filterValues[0]) {
|
||||||
|
return !!document.querySelector(filterValues[0]);
|
||||||
|
}
|
||||||
|
// If there is no CSS selector configured, this page matches.
|
||||||
|
return true;
|
||||||
|
};
|
@ -14,36 +14,62 @@ function(ajax, BootstrapTour, $, templates, str, log, notification) {
|
|||||||
|
|
||||||
currentTour: null,
|
currentTour: null,
|
||||||
|
|
||||||
context: null,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise the user tour for the current page.
|
* Initialise the user tour for the current page.
|
||||||
*
|
*
|
||||||
* @method init
|
* @method init
|
||||||
* @param {Number} tourId The ID of the tour to start.
|
* @param {Array} tourDetails The matching tours for this page.
|
||||||
* @param {Bool} startTour Attempt to start the tour now.
|
* @param {Array} filters The names of all client side filters.
|
||||||
* @param {Number} context The context of the current page.
|
|
||||||
*/
|
*/
|
||||||
init: function(tourId, startTour, context) {
|
init: function(tourDetails, filters) {
|
||||||
// Only one tour per page is allowed.
|
let requirements = [];
|
||||||
usertours.tourId = tourId;
|
for (var req = 0; req < filters.length; req++) {
|
||||||
|
requirements[req] = 'tool_usertours/filter_' + filters[req];
|
||||||
usertours.context = context;
|
|
||||||
|
|
||||||
if (typeof startTour === 'undefined') {
|
|
||||||
startTour = true;
|
|
||||||
}
|
}
|
||||||
|
require(requirements, function() {
|
||||||
|
// Run the client side filters to find the first matching tour.
|
||||||
|
let matchingTour = null;
|
||||||
|
for (let key in tourDetails) {
|
||||||
|
let tour = tourDetails[key];
|
||||||
|
for (let i = 0; i < filters.length; i++) {
|
||||||
|
let filter = arguments[i];
|
||||||
|
if (filter.filterMatches(tour)) {
|
||||||
|
matchingTour = tour;
|
||||||
|
} else {
|
||||||
|
// If any filter doesn't match, move on to the next tour.
|
||||||
|
matchingTour = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If all filters matched then use this tour.
|
||||||
|
if (matchingTour) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (startTour) {
|
if (matchingTour === null) {
|
||||||
// Fetch the tour configuration.
|
return;
|
||||||
usertours.fetchTour(tourId);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
usertours.addResetLink();
|
// Only one tour per page is allowed.
|
||||||
// Watch for the reset link.
|
usertours.tourId = matchingTour.tourId;
|
||||||
$('body').on('click', '[data-action="tool_usertours/resetpagetour"]', function(e) {
|
|
||||||
e.preventDefault();
|
let startTour = matchingTour.startTour;
|
||||||
usertours.resetTourState(usertours.tourId);
|
if (typeof startTour === 'undefined') {
|
||||||
|
startTour = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startTour) {
|
||||||
|
// Fetch the tour configuration.
|
||||||
|
usertours.fetchTour(usertours.tourId);
|
||||||
|
}
|
||||||
|
|
||||||
|
usertours.addResetLink();
|
||||||
|
// Watch for the reset link.
|
||||||
|
$('body').on('click', '[data-action="tool_usertours/resetpagetour"]', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
usertours.resetTourState(usertours.tourId);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -61,7 +87,7 @@ function(ajax, BootstrapTour, $, templates, str, log, notification) {
|
|||||||
methodname: 'tool_usertours_fetch_and_start_tour',
|
methodname: 'tool_usertours_fetch_and_start_tour',
|
||||||
args: {
|
args: {
|
||||||
tourid: tourId,
|
tourid: tourId,
|
||||||
context: usertours.context,
|
context: M.cfg.contextid,
|
||||||
pageurl: window.location.href,
|
pageurl: window.location.href,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,7 +212,7 @@ function(ajax, BootstrapTour, $, templates, str, log, notification) {
|
|||||||
methodname: 'tool_usertours_step_shown',
|
methodname: 'tool_usertours_step_shown',
|
||||||
args: {
|
args: {
|
||||||
tourid: usertours.tourId,
|
tourid: usertours.tourId,
|
||||||
context: usertours.context,
|
context: M.cfg.contextid,
|
||||||
pageurl: window.location.href,
|
pageurl: window.location.href,
|
||||||
stepid: stepConfig.stepid,
|
stepid: stepConfig.stepid,
|
||||||
stepindex: this.getCurrentStepNumber(),
|
stepindex: this.getCurrentStepNumber(),
|
||||||
@ -209,7 +235,7 @@ function(ajax, BootstrapTour, $, templates, str, log, notification) {
|
|||||||
methodname: 'tool_usertours_complete_tour',
|
methodname: 'tool_usertours_complete_tour',
|
||||||
args: {
|
args: {
|
||||||
tourid: usertours.tourId,
|
tourid: usertours.tourId,
|
||||||
context: usertours.context,
|
context: M.cfg.contextid,
|
||||||
pageurl: window.location.href,
|
pageurl: window.location.href,
|
||||||
stepid: stepConfig.stepid,
|
stepid: stepConfig.stepid,
|
||||||
stepindex: this.getCurrentStepNumber(),
|
stepindex: this.getCurrentStepNumber(),
|
||||||
@ -232,7 +258,7 @@ function(ajax, BootstrapTour, $, templates, str, log, notification) {
|
|||||||
methodname: 'tool_usertours_reset_tour',
|
methodname: 'tool_usertours_reset_tour',
|
||||||
args: {
|
args: {
|
||||||
tourid: tourId,
|
tourid: tourId,
|
||||||
context: usertours.context,
|
context: M.cfg.contextid,
|
||||||
pageurl: window.location.href,
|
pageurl: window.location.href,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,8 +131,9 @@ class tour extends external_api {
|
|||||||
|
|
||||||
$result = [];
|
$result = [];
|
||||||
|
|
||||||
if ($tourinstance = \tool_usertours\manager::get_matching_tours(new \moodle_url($params['pageurl']))) {
|
$matchingtours = \tool_usertours\manager::get_matching_tours(new \moodle_url($params['pageurl']));
|
||||||
if ($tour->get_id() === $tourinstance->get_id()) {
|
foreach ($matchingtours as $match) {
|
||||||
|
if ($tour->get_id() === $match->get_id()) {
|
||||||
$result['startTour'] = $tour->get_id();
|
$result['startTour'] = $tour->get_id();
|
||||||
|
|
||||||
\tool_usertours\event\tour_reset::create([
|
\tool_usertours\event\tour_reset::create([
|
||||||
@ -142,7 +143,7 @@ class tour extends external_api {
|
|||||||
'pageurl' => $params['pageurl'],
|
'pageurl' => $params['pageurl'],
|
||||||
],
|
],
|
||||||
])->trigger();
|
])->trigger();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
namespace tool_usertours;
|
namespace tool_usertours;
|
||||||
|
|
||||||
|
use tool_usertours\local\clientside_filter\clientside_filter;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -523,23 +525,28 @@ class helper {
|
|||||||
}
|
}
|
||||||
self::$bootstrapped = true;
|
self::$bootstrapped = true;
|
||||||
|
|
||||||
if ($tour = manager::get_current_tour()) {
|
$tours = manager::get_current_tours();
|
||||||
$PAGE->requires->js_call_amd('tool_usertours/usertours', 'init', [
|
|
||||||
$tour->get_id(),
|
|
||||||
$tour->should_show_for_user(),
|
|
||||||
$PAGE->context->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if ($tours) {
|
||||||
* Add the reset link to the current page.
|
$filters = static::get_all_clientside_filters();
|
||||||
*/
|
|
||||||
public static function bootstrap_reset() {
|
$tourdetails = array_map(function($tour) use ($filters) {
|
||||||
if (manager::get_current_tour()) {
|
return [
|
||||||
echo \html_writer::link('', get_string('resettouronpage', 'tool_usertours'), [
|
'tourId' => $tour->get_id(),
|
||||||
'data-action' => 'tool_usertours/resetpagetour',
|
'startTour' => $tour->should_show_for_user(),
|
||||||
]);
|
'filtervalues' => $tour->get_client_filter_values($filters),
|
||||||
|
];
|
||||||
|
}, $tours);
|
||||||
|
|
||||||
|
$filternames = [];
|
||||||
|
foreach ($filters as $filter) {
|
||||||
|
$filternames[] = $filter::get_filter_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
$PAGE->requires->js_call_amd('tool_usertours/usertours', 'init', [
|
||||||
|
$tourdetails,
|
||||||
|
$filternames,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,6 +564,25 @@ class helper {
|
|||||||
return $rc->isInstantiable();
|
return $rc->isInstantiable();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$filters = array_merge($filters, static::get_all_clientside_filters());
|
||||||
|
|
||||||
|
return $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of all clientside filters.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function get_all_clientside_filters() {
|
||||||
|
$filters = \core_component::get_component_classes_in_namespace('tool_usertours', 'local\clientside_filter');
|
||||||
|
$filters = array_keys($filters);
|
||||||
|
|
||||||
|
$filters = array_filter($filters, function($filterclass) {
|
||||||
|
$rc = new \ReflectionClass($filterclass);
|
||||||
|
return $rc->isInstantiable();
|
||||||
|
});
|
||||||
|
|
||||||
return $filters;
|
return $filters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clientside filter base.
|
||||||
|
*
|
||||||
|
* @package tool_usertours
|
||||||
|
* @copyright 2020 The Open University
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace tool_usertours\local\clientside_filter;
|
||||||
|
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
use tool_usertours\local\filter\base;
|
||||||
|
use tool_usertours\tour;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clientside filter base.
|
||||||
|
*
|
||||||
|
* @copyright 2020 The Open University
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
abstract class clientside_filter extends base {
|
||||||
|
/**
|
||||||
|
* Returns the filter values needed for client side filtering.
|
||||||
|
*
|
||||||
|
* @param tour $tour The tour to find the filter values for
|
||||||
|
* @return stdClass
|
||||||
|
*/
|
||||||
|
public static function get_client_side_values(tour $tour): stdClass {
|
||||||
|
$data = (object) [];
|
||||||
|
|
||||||
|
if (is_a(static::class, clientside_filter::class, true)) {
|
||||||
|
$data->filterdata = $tour->get_filter_values(static::get_filter_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
<?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/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selector filter.
|
||||||
|
*
|
||||||
|
* @package tool_usertours
|
||||||
|
* @copyright 2020 The Open University
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
namespace tool_usertours\local\clientside_filter;
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
use tool_usertours\tour;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Course filter.
|
||||||
|
*
|
||||||
|
* @copyright 2020 The Open University
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
class cssselector extends clientside_filter {
|
||||||
|
/**
|
||||||
|
* The name of the filter.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function get_filter_name() {
|
||||||
|
return 'cssselector';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the base add form element with a selector text box.
|
||||||
|
*
|
||||||
|
* @param \MoodleQuickForm $mform
|
||||||
|
*/
|
||||||
|
public static function add_filter_to_form(\MoodleQuickForm &$mform) {
|
||||||
|
$filtername = self::get_filter_name();
|
||||||
|
$key = "filter_{$filtername}";
|
||||||
|
|
||||||
|
$mform->addElement('text', $key, get_string($key, 'tool_usertours'));
|
||||||
|
$mform->setType($key, PARAM_RAW);
|
||||||
|
$mform->addHelpButton($key, $key, 'tool_usertours');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the filter values for the form.
|
||||||
|
*
|
||||||
|
* @param tour $tour The tour to prepare values from
|
||||||
|
* @param stdClass $data The data value
|
||||||
|
* @return stdClass
|
||||||
|
*/
|
||||||
|
public static function prepare_filter_values_for_form(tour $tour, \stdClass $data) {
|
||||||
|
$filtername = static::get_filter_name();
|
||||||
|
|
||||||
|
$key = "filter_{$filtername}";
|
||||||
|
$values = $tour->get_filter_values($filtername);
|
||||||
|
if (empty($values)) {
|
||||||
|
$values = [""];
|
||||||
|
}
|
||||||
|
$data->$key = $values[0];
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the filter values from the form to the tour.
|
||||||
|
*
|
||||||
|
* @param tour $tour The tour to save values to
|
||||||
|
* @param stdClass $data The data submitted in the form
|
||||||
|
*/
|
||||||
|
public static function save_filter_values_from_form(tour $tour, \stdClass $data) {
|
||||||
|
$filtername = static::get_filter_name();
|
||||||
|
|
||||||
|
$key = "filter_{$filtername}";
|
||||||
|
|
||||||
|
$newvalue = [$data->$key];
|
||||||
|
if (empty($data->$key)) {
|
||||||
|
$newvalue = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$tour->set_filter_values($filtername, $newvalue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the filter values needed for client side filtering.
|
||||||
|
*
|
||||||
|
* @param tour $tour The tour to find the filter values for
|
||||||
|
* @return stdClass
|
||||||
|
*/
|
||||||
|
public static function get_client_side_values(tour $tour): stdClass {
|
||||||
|
$filtername = static::get_filter_name();
|
||||||
|
$filtervalues = $tour->get_filter_values($filtername);
|
||||||
|
|
||||||
|
// Filter values might not exist for tours that were created before this filter existed.
|
||||||
|
if (!$filtervalues) {
|
||||||
|
return new stdClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) $filtervalues;
|
||||||
|
}
|
||||||
|
}
|
@ -608,42 +608,44 @@ class manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first tour matching the current page URL.
|
* Get all tours for the current page URL.
|
||||||
*
|
*
|
||||||
* @param bool $reset Forcibly update the current tour
|
* @param bool $reset Forcibly update the current tours
|
||||||
* @return tour
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function get_current_tour($reset = false) {
|
public static function get_current_tours($reset = false): array {
|
||||||
global $PAGE;
|
global $PAGE;
|
||||||
|
|
||||||
static $tour = false;
|
static $tours = false;
|
||||||
|
|
||||||
if ($tour === false || $reset) {
|
if ($tours === false || $reset) {
|
||||||
$tour = self::get_matching_tours($PAGE->url);
|
$tours = self::get_matching_tours($PAGE->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tour;
|
return $tours;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first tour matching the specified URL.
|
* Get all tours matching the specified URL.
|
||||||
*
|
*
|
||||||
* @param moodle_url $pageurl The URL to match.
|
* @param moodle_url $pageurl The URL to match.
|
||||||
* @return tour
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function get_matching_tours(\moodle_url $pageurl) {
|
public static function get_matching_tours(\moodle_url $pageurl): array {
|
||||||
global $PAGE;
|
global $PAGE;
|
||||||
|
|
||||||
$tours = cache::get_matching_tourdata($pageurl);
|
$tours = cache::get_matching_tourdata($pageurl);
|
||||||
|
|
||||||
|
$matches = [];
|
||||||
|
$filters = helper::get_all_filters();
|
||||||
foreach ($tours as $record) {
|
foreach ($tours as $record) {
|
||||||
$tour = tour::load_from_record($record);
|
$tour = tour::load_from_record($record);
|
||||||
if ($tour->is_enabled() && $tour->matches_all_filters($PAGE->context)) {
|
if ($tour->is_enabled() && $tour->matches_all_filters($PAGE->context, $filters)) {
|
||||||
return $tour;
|
$matches[] = $tour;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return $matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
namespace tool_usertours;
|
namespace tool_usertours;
|
||||||
|
|
||||||
|
use tool_usertours\local\clientside_filter\clientside_filter;
|
||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -769,11 +771,14 @@ class tour {
|
|||||||
/**
|
/**
|
||||||
* Check whether this tour matches all filters.
|
* Check whether this tour matches all filters.
|
||||||
*
|
*
|
||||||
* @param context $context The context to check
|
* @param \context $context The context to check.
|
||||||
|
* @param array|null $filters Optional array of filters.
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function matches_all_filters(\context $context) {
|
public function matches_all_filters(\context $context, array $filters = null): bool {
|
||||||
$filters = helper::get_all_filters();
|
if (!$filters) {
|
||||||
|
$filters = helper::get_all_filters();
|
||||||
|
}
|
||||||
|
|
||||||
// All filters must match.
|
// All filters must match.
|
||||||
// If any one filter fails to match, we return false.
|
// If any one filter fails to match, we return false.
|
||||||
@ -785,4 +790,20 @@ class tour {
|
|||||||
|
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,8 @@ $string['filter_course'] = 'Courses';
|
|||||||
$string['filter_course_help'] = 'Show the tour on a page that is associated with the selected course.';
|
$string['filter_course_help'] = 'Show the tour on a page that is associated with the selected course.';
|
||||||
$string['filter_courseformat'] = 'Course format';
|
$string['filter_courseformat'] = 'Course format';
|
||||||
$string['filter_courseformat_help'] = 'Show the tour on a page that is associated with a course using the selected course format.';
|
$string['filter_courseformat_help'] = 'Show the tour on a page that is associated with a course using the selected course format.';
|
||||||
|
$string['filter_cssselector'] = 'CSS selector';
|
||||||
|
$string['filter_cssselector_help'] = 'Only show the tour when the specified CSS selector is found on the page.';
|
||||||
$string['filter_header'] = 'Tour filters';
|
$string['filter_header'] = 'Tour filters';
|
||||||
$string['filter_help'] = 'Select the conditions under which the tour will be shown. All of the filters must match for a tour to be shown to a user.';
|
$string['filter_help'] = 'Select the conditions under which the tour will be shown. All of the filters must match for a tour to be shown to a user.';
|
||||||
$string['filter_date_account_creation'] = 'User account creation date within';
|
$string['filter_date_account_creation'] = 'User account creation date within';
|
||||||
|
@ -142,3 +142,88 @@ Feature: Apply tour filters to a tour
|
|||||||
When I am on "Course 2" course homepage
|
When I am on "Course 2" course homepage
|
||||||
And I wait until the page is ready
|
And I wait until the page is ready
|
||||||
Then I should not see "Welcome to your course tour."
|
Then I should not see "Welcome to your course tour."
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: Add tours with CSS selectors
|
||||||
|
Given the following "users" exist:
|
||||||
|
| username | firstname | lastname | email |
|
||||||
|
| student1 | Student | 1 | student1@example.com |
|
||||||
|
Given the following "courses" exist:
|
||||||
|
| fullname | shortname | format | enablecompletion |
|
||||||
|
| Course 1 | C1 | topics | 1 |
|
||||||
|
| Course 2 | C2 | topics | 1 |
|
||||||
|
And I log in as "admin"
|
||||||
|
And I am on "Course 1" course homepage with editing mode on
|
||||||
|
And I add a "Wiki" to section "1" and I fill the form with:
|
||||||
|
| Wiki name | Test wiki name |
|
||||||
|
| Description | Test wiki description |
|
||||||
|
| First page name | First page |
|
||||||
|
| Wiki mode | Collaborative wiki |
|
||||||
|
And I am on "Course 2" course homepage
|
||||||
|
And I add a "Forum" to section "1" and I fill the form with:
|
||||||
|
| Forum name | Test forum name |
|
||||||
|
| Forum type | Standard forum for general use |
|
||||||
|
| Description | Test forum description |
|
||||||
|
And I add a new user tour with:
|
||||||
|
| Name | Wiki tour |
|
||||||
|
| Description | A tour with both matches |
|
||||||
|
| Apply to URL match | /course/view.php% |
|
||||||
|
| Tour is enabled | 1 |
|
||||||
|
| CSS selector | .modtype_wiki |
|
||||||
|
And I add steps to the "Wiki tour" tour:
|
||||||
|
| targettype | Title | Content |
|
||||||
|
| Display in middle of page | Welcome | Welcome to the Wiki tour |
|
||||||
|
And I add a new user tour with:
|
||||||
|
| Name | Forum tour |
|
||||||
|
| Description | A tour with both matches |
|
||||||
|
| Apply to URL match | /course/view.php% |
|
||||||
|
| Tour is enabled | 1 |
|
||||||
|
| CSS selector | .modtype_forum |
|
||||||
|
And I add steps to the "Forum tour" tour:
|
||||||
|
| targettype | Title | Content |
|
||||||
|
| Display in middle of page | Welcome | Welcome to the Forum tour |
|
||||||
|
And I am on "Course 1" course homepage
|
||||||
|
Then I should see "Welcome to the Wiki tour"
|
||||||
|
And I am on "Course 2" course homepage
|
||||||
|
Then I should see "Welcome to the Forum tour"
|
||||||
|
|
||||||
|
@javascript
|
||||||
|
Scenario: Check filtering respects the sort order
|
||||||
|
Given the following "users" exist:
|
||||||
|
| username | firstname | lastname | email |
|
||||||
|
| student1 | Student | 1 | student1@example.com |
|
||||||
|
And I log in as "admin"
|
||||||
|
And I add a new user tour with:
|
||||||
|
| Name | First tour |
|
||||||
|
| Description | The first tour |
|
||||||
|
| Apply to URL match | /my/% |
|
||||||
|
| Tour is enabled | 1 |
|
||||||
|
| CSS selector | #page-my-index |
|
||||||
|
And I add steps to the "First tour" tour:
|
||||||
|
| targettype | Title | Content |
|
||||||
|
| Display in middle of page | Welcome | Welcome to the First tour |
|
||||||
|
And I add a new user tour with:
|
||||||
|
| Name | Second tour |
|
||||||
|
| Description | The second tour |
|
||||||
|
| Apply to URL match | /my/% |
|
||||||
|
| Tour is enabled | 0 |
|
||||||
|
| CSS selector | #page-my-index |
|
||||||
|
And I add steps to the "Second tour" tour:
|
||||||
|
| targettype | Title | Content |
|
||||||
|
| Display in middle of page | Welcome | Welcome to the Second tour |
|
||||||
|
And I add a new user tour with:
|
||||||
|
| Name | Third tour |
|
||||||
|
| Description | The third tour |
|
||||||
|
| Apply to URL match | /my/% |
|
||||||
|
| Tour is enabled | 1 |
|
||||||
|
| CSS selector | #page-my-index |
|
||||||
|
And I add steps to the "Third tour" tour:
|
||||||
|
| targettype | Title | Content |
|
||||||
|
| Display in middle of page | Welcome | Welcome to the Third tour |
|
||||||
|
And I am on homepage
|
||||||
|
Then I should see "Welcome to the First tour"
|
||||||
|
And I open the User tour settings page
|
||||||
|
And I click on "Move tour down" "link" in the "The first tour" "table_row"
|
||||||
|
And I click on "Move tour down" "link" in the "The first tour" "table_row"
|
||||||
|
And I am on homepage
|
||||||
|
Then I should see "Welcome to the Third tour"
|
||||||
|
@ -222,6 +222,13 @@ class tool_usertours_manager_testcase extends advanced_testcase {
|
|||||||
'description' => '',
|
'description' => '',
|
||||||
'configdata' => '',
|
'configdata' => '',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'pathmatch' => '/my/%',
|
||||||
|
'enabled' => true,
|
||||||
|
'name' => 'My tour enabled 2',
|
||||||
|
'description' => '',
|
||||||
|
'configdata' => '',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'pathmatch' => '/my/%',
|
'pathmatch' => '/my/%',
|
||||||
'enabled' => false,
|
'enabled' => false,
|
||||||
@ -277,32 +284,32 @@ class tool_usertours_manager_testcase extends advanced_testcase {
|
|||||||
'No matches found' => [
|
'No matches found' => [
|
||||||
$alltours,
|
$alltours,
|
||||||
$CFG->wwwroot . '/some/invalid/value',
|
$CFG->wwwroot . '/some/invalid/value',
|
||||||
null,
|
[],
|
||||||
],
|
],
|
||||||
'Never return a disabled tour' => [
|
'Never return a disabled tour' => [
|
||||||
$alltours,
|
$alltours,
|
||||||
$CFG->wwwroot . '/my/index.php',
|
$CFG->wwwroot . '/my/index.php',
|
||||||
'My tour enabled',
|
['My tour enabled', 'My tour enabled 2'],
|
||||||
],
|
],
|
||||||
'My not course' => [
|
'My not course' => [
|
||||||
$alltours,
|
$alltours,
|
||||||
$CFG->wwwroot . '/my/index.php',
|
$CFG->wwwroot . '/my/index.php',
|
||||||
'My tour enabled',
|
['My tour enabled', 'My tour enabled 2'],
|
||||||
],
|
],
|
||||||
'My with params' => [
|
'My with params' => [
|
||||||
$alltours,
|
$alltours,
|
||||||
$CFG->wwwroot . '/my/index.php?id=42',
|
$CFG->wwwroot . '/my/index.php?id=42',
|
||||||
'My tour enabled',
|
['My tour enabled', 'My tour enabled 2'],
|
||||||
],
|
],
|
||||||
'Course with params' => [
|
'Course with params' => [
|
||||||
$alltours,
|
$alltours,
|
||||||
$CFG->wwwroot . '/course/?id=42',
|
$CFG->wwwroot . '/course/?id=42',
|
||||||
'course tour enabled',
|
['course tour enabled'],
|
||||||
],
|
],
|
||||||
'Course with params and trailing content' => [
|
'Course with params and trailing content' => [
|
||||||
$alltours,
|
$alltours,
|
||||||
$CFG->wwwroot . '/course/?id=42&foo=bar',
|
$CFG->wwwroot . '/course/?id=42&foo=bar',
|
||||||
'course tour with additional params enabled',
|
['course tour with additional params enabled', 'course tour enabled'],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -311,11 +318,11 @@ class tool_usertours_manager_testcase extends advanced_testcase {
|
|||||||
* Tests for the get_matching_tours function.
|
* Tests for the get_matching_tours function.
|
||||||
*
|
*
|
||||||
* @dataProvider get_matching_tours_provider
|
* @dataProvider get_matching_tours_provider
|
||||||
* @param array $alltours The list of tours to insert
|
* @param array $alltours The list of tours to insert.
|
||||||
* @param string $url The URL to test
|
* @param string $url The URL to test.
|
||||||
* @param string $expected The name of the expected matching tour
|
* @param array $expected List of names of the expected matching tours.
|
||||||
*/
|
*/
|
||||||
public function test_get_matching_tours($alltours, $url, $expected) {
|
public function test_get_matching_tours(array $alltours, string $url, array $expected) {
|
||||||
$this->resetAfterTest();
|
$this->resetAfterTest();
|
||||||
|
|
||||||
foreach ($alltours as $tourconfig) {
|
foreach ($alltours as $tourconfig) {
|
||||||
@ -323,12 +330,10 @@ class tool_usertours_manager_testcase extends advanced_testcase {
|
|||||||
$this->helper_create_step((object) ['tourid' => $tour->get_id()]);
|
$this->helper_create_step((object) ['tourid' => $tour->get_id()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$match = \tool_usertours\manager::get_matching_tours(new moodle_url($url));
|
$matches = \tool_usertours\manager::get_matching_tours(new moodle_url($url));
|
||||||
if ($expected === null) {
|
$this->assertEquals(count($expected), count($matches));
|
||||||
$this->assertNull($match);
|
for ($i = 0; $i < count($matches); $i++) {
|
||||||
} else {
|
$this->assertEquals($expected[$i], $matches[$i]->get_name());
|
||||||
$this->assertNotNull($match);
|
|
||||||
$this->assertEquals($expected, $match->get_name());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,6 @@
|
|||||||
|
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
$plugin->version = 2021052501; // The current module version (Date: YYYYMMDDXX).
|
$plugin->version = 2021052502; // The current module version (Date: YYYYMMDDXX).
|
||||||
$plugin->requires = 2021052500; // Requires this Moodle version.
|
$plugin->requires = 2021052500; // Requires this Moodle version.
|
||||||
$plugin->component = 'tool_usertours'; // Full name of the plugin (used for diagnostics).
|
$plugin->component = 'tool_usertours'; // Full name of the plugin (used for diagnostics).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user