Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Tour helper.
  19   *
  20   * @package    tool_usertours
  21   * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace tool_usertours;
  26  
  27  use core\output\inplace_editable;
  28  use tool_usertours\local\clientside_filter\clientside_filter;
  29  
  30  defined('MOODLE_INTERNAL') || die();
  31  
  32  /**
  33   * Tour helper.
  34   *
  35   * @copyright  2016 Andrew Nicols <andrew@nicols.co.uk>
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class helper {
  39  
  40      /**
  41       * @var MOVE_UP
  42       */
  43      const MOVE_UP = -1;
  44  
  45      /**
  46       * @var MOVE_DOWN
  47       */
  48      const MOVE_DOWN = 1;
  49  
  50      /**
  51       * @var boolean Has it been bootstrapped?
  52       */
  53      private static $bootstrapped = false;
  54  
  55      /**
  56       * @var string Regex to check any matching lang string.
  57       */
  58      protected const LANG_STRING_REGEX = '|^([a-zA-Z][a-zA-Z0-9\.:/_-]*),([a-zA-Z][a-zA-Z0-9\.:/_-]*)$|';
  59  
  60      /**
  61       * Get the link to edit the step.
  62       *
  63       * If no stepid is specified, then a link to create a new step is provided. The $targettype must be specified in this case.
  64       *
  65       * @param   int     $tourid     The tour that the step belongs to.
  66       * @param   int     $stepid     The step ID.
  67       * @param   int     $targettype The type of step.
  68       *
  69       * @return moodle_url
  70       */
  71      public static function get_edit_step_link($tourid, $stepid = null, $targettype = null) {
  72          $link = new \moodle_url('/admin/tool/usertours/configure.php');
  73  
  74          if ($stepid) {
  75              $link->param('action', manager::ACTION_EDITSTEP);
  76              $link->param('id', $stepid);
  77          } else {
  78              $link->param('action', manager::ACTION_NEWSTEP);
  79              $link->param('tourid', $tourid);
  80          }
  81  
  82          return $link;
  83      }
  84  
  85      /**
  86       * Get the link to move the tour.
  87       *
  88       * @param   int     $tourid     The tour ID.
  89       * @param   int     $direction  The direction to move in
  90       *
  91       * @return moodle_url
  92       */
  93      public static function get_move_tour_link($tourid, $direction = self::MOVE_DOWN) {
  94          $link = new \moodle_url('/admin/tool/usertours/configure.php');
  95  
  96          $link->param('action', manager::ACTION_MOVETOUR);
  97          $link->param('id', $tourid);
  98          $link->param('direction', $direction);
  99          $link->param('sesskey', sesskey());
 100  
 101          return $link;
 102      }
 103  
 104      /**
 105       * Get the link to move the step.
 106       *
 107       * @param   int     $stepid     The step ID.
 108       * @param   int     $direction  The direction to move in
 109       *
 110       * @return moodle_url
 111       */
 112      public static function get_move_step_link($stepid, $direction = self::MOVE_DOWN) {
 113          $link = new \moodle_url('/admin/tool/usertours/configure.php');
 114  
 115          $link->param('action', manager::ACTION_MOVESTEP);
 116          $link->param('id', $stepid);
 117          $link->param('direction', $direction);
 118          $link->param('sesskey', sesskey());
 119  
 120          return $link;
 121      }
 122  
 123      /**
 124       * Get the link ot create a new step.
 125       *
 126       * @param   int         $tourid     The ID of the tour to attach this step to.
 127       * @param   int         $targettype The type of target.
 128       *
 129       * @return  moodle_url              The required URL.
 130       */
 131      public static function get_new_step_link($tourid, $targettype = null) {
 132          $link = new \moodle_url('/admin/tool/usertours/configure.php');
 133          $link->param('action', manager::ACTION_NEWSTEP);
 134          $link->param('tourid', $tourid);
 135          $link->param('targettype', $targettype);
 136  
 137          return $link;
 138      }
 139  
 140      /**
 141       * Get the link used to view the tour.
 142       *
 143       * @param   int         $tourid     The ID of the tour to display.
 144       * @return  moodle_url              The URL.
 145       */
 146      public static function get_view_tour_link($tourid) {
 147          return new \moodle_url('/admin/tool/usertours/configure.php', [
 148                  'id'        => $tourid,
 149                  'action'    => manager::ACTION_VIEWTOUR,
 150              ]);
 151      }
 152  
 153      /**
 154       * Get the link used to reset the tour state for all users.
 155       *
 156       * @param   int         $tourid     The ID of the tour to display.
 157       * @return  moodle_url              The URL.
 158       */
 159      public static function get_reset_tour_for_all_link($tourid) {
 160          return new \moodle_url('/admin/tool/usertours/configure.php', [
 161                  'id'        => $tourid,
 162                  'action'    => manager::ACTION_RESETFORALL,
 163                  'sesskey'   => sesskey(),
 164              ]);
 165      }
 166  
 167      /**
 168       * Get the link used to edit the tour.
 169       *
 170       * @param   int         $tourid     The ID of the tour to edit.
 171       * @return  moodle_url              The URL.
 172       */
 173      public static function get_edit_tour_link($tourid = null) {
 174          $link = new \moodle_url('/admin/tool/usertours/configure.php');
 175  
 176          if ($tourid) {
 177              $link->param('action', manager::ACTION_EDITTOUR);
 178              $link->param('id', $tourid);
 179          } else {
 180              $link->param('action', manager::ACTION_NEWTOUR);
 181          }
 182  
 183          return $link;
 184      }
 185  
 186      /**
 187       * Get the link used to import the tour.
 188       *
 189       * @return  moodle_url              The URL.
 190       */
 191      public static function get_import_tour_link() {
 192          $link = new \moodle_url('/admin/tool/usertours/configure.php', [
 193                  'action'    => manager::ACTION_IMPORTTOUR,
 194              ]);
 195  
 196          return $link;
 197      }
 198  
 199      /**
 200       * Get the link used to export the tour.
 201       *
 202       * @param   int         $tourid     The ID of the tour to export.
 203       * @return  moodle_url              The URL.
 204       */
 205      public static function get_export_tour_link($tourid) {
 206          $link = new \moodle_url('/admin/tool/usertours/configure.php', [
 207                  'action'    => manager::ACTION_EXPORTTOUR,
 208                  'id'        => $tourid,
 209              ]);
 210  
 211          return $link;
 212      }
 213  
 214      /**
 215       * Get the link used to duplicate the tour.
 216       *
 217       * @param   int         $tourid     The ID of the tour to duplicate.
 218       * @return  moodle_url              The URL.
 219       */
 220      public static function get_duplicate_tour_link($tourid) {
 221          $link = new \moodle_url('/admin/tool/usertours/configure.php', [
 222                  'action'    => manager::ACTION_DUPLICATETOUR,
 223                  'id'        => $tourid,
 224          ]);
 225  
 226          return $link;
 227      }
 228  
 229      /**
 230       * Get the link used to delete the tour.
 231       *
 232       * @param   int         $tourid     The ID of the tour to delete.
 233       * @return  moodle_url              The URL.
 234       */
 235      public static function get_delete_tour_link($tourid) {
 236          return new \moodle_url('/admin/tool/usertours/configure.php', [
 237                  'id'        => $tourid,
 238                  'action'    => manager::ACTION_DELETETOUR,
 239                  'sesskey'   => sesskey(),
 240              ]);
 241      }
 242  
 243      /**
 244       * Get the link for listing tours.
 245       *
 246       * @return  moodle_url              The URL.
 247       */
 248      public static function get_list_tour_link() {
 249          $link = new \moodle_url('/admin/tool/usertours/configure.php');
 250          $link->param('action', manager::ACTION_LISTTOURS);
 251  
 252          return $link;
 253      }
 254  
 255      /**
 256       * Get a filler icon for display in the actions column of a table.
 257       *
 258       * @param   string      $url            The URL for the icon.
 259       * @param   string      $icon           The icon identifier.
 260       * @param   string      $alt            The alt text for the icon.
 261       * @param   string      $iconcomponent  The icon component.
 262       * @param   array       $options        Display options.
 263       * @return  string
 264       */
 265      public static function format_icon_link($url, $icon, $alt, $iconcomponent = 'moodle', $options = array()) {
 266          global $OUTPUT;
 267  
 268          return $OUTPUT->action_icon(
 269                  $url,
 270                  new \pix_icon($icon, $alt, $iconcomponent, [
 271                          'title' => $alt,
 272                      ]),
 273                  null,
 274                  $options
 275                  );
 276  
 277      }
 278  
 279      /**
 280       * Get a filler icon for display in the actions column of a table.
 281       *
 282       * @param   array       $options        Display options.
 283       * @return  string
 284       */
 285      public static function get_filler_icon($options = array()) {
 286          global $OUTPUT;
 287  
 288          return \html_writer::span(
 289              $OUTPUT->pix_icon('t/filler', '', 'tool_usertours', $options),
 290              'action-icon'
 291          );
 292      }
 293  
 294      /**
 295       * Get the link for deleting steps.
 296       *
 297       * @param   int         $stepid     The ID of the step to display.
 298       * @return  moodle_url              The URL.
 299       */
 300      public static function get_delete_step_link($stepid) {
 301          return new \moodle_url('/admin/tool/usertours/configure.php', [
 302                  'action'    => manager::ACTION_DELETESTEP,
 303                  'id'        => $stepid,
 304                  'sesskey'   => sesskey(),
 305              ]);
 306      }
 307  
 308      /**
 309       * Render the inplace editable used to edit the tour name.
 310       *
 311       * @param tour $tour The tour to edit.
 312       * @return inplace_editable
 313       */
 314      public static function render_tourname_inplace_editable(tour $tour): inplace_editable {
 315          $name = format_text(static::get_string_from_input($tour->get_name()), FORMAT_HTML);
 316          return new inplace_editable(
 317                  'tool_usertours',
 318                  'tourname',
 319                  $tour->get_id(),
 320                  true,
 321                  \html_writer::link(
 322                      $tour->get_view_link(),
 323                      $name
 324                  ),
 325                  $tour->get_name()
 326              );
 327      }
 328  
 329      /**
 330       * Render the inplace editable used to edit the tour description.
 331       *
 332       * @param tour $tour The tour to edit.
 333       * @return inplace_editable
 334       */
 335      public static function render_tourdescription_inplace_editable(tour $tour): inplace_editable {
 336          $description = format_text(static::get_string_from_input($tour->get_description()), FORMAT_HTML);
 337          return new inplace_editable(
 338                  'tool_usertours',
 339                  'tourdescription',
 340                  $tour->get_id(),
 341                  true,
 342                  $description,
 343                  $tour->get_description()
 344              );
 345      }
 346  
 347      /**
 348       * Render the inplace editable used to edit the tour enable state.
 349       *
 350       * @param tour $tour The tour to edit.
 351       * @return inplace_editable
 352       */
 353      public static function render_tourenabled_inplace_editable(tour $tour): inplace_editable {
 354          global $OUTPUT;
 355  
 356          if ($tour->is_enabled()) {
 357              $icon = 't/hide';
 358              $alt = get_string('disable');
 359              $value = 1;
 360          } else {
 361              $icon = 't/show';
 362              $alt = get_string('enable');
 363              $value = 0;
 364          }
 365  
 366          $editable = new inplace_editable(
 367                  'tool_usertours',
 368                  'tourenabled',
 369                  $tour->get_id(),
 370                  true,
 371                  $OUTPUT->pix_icon($icon, $alt, 'moodle', [
 372                          'title' => $alt,
 373                      ]),
 374                  $value
 375              );
 376  
 377          $editable->set_type_toggle();
 378          return $editable;
 379      }
 380  
 381      /**
 382       * Render the inplace editable used to edit the step name.
 383       *
 384       * @param step $step The step to edit.
 385       * @return inplace_editable
 386       */
 387      public static function render_stepname_inplace_editable(step $step): inplace_editable {
 388          $title = format_text(static::get_string_from_input($step->get_title()), FORMAT_HTML);
 389  
 390          return new inplace_editable(
 391                  'tool_usertours',
 392                  'stepname',
 393                  $step->get_id(),
 394                  true,
 395                  \html_writer::link(
 396                      $step->get_edit_link(),
 397                      $title
 398                  ),
 399                  $step->get_title()
 400              );
 401      }
 402  
 403      /**
 404       * Get all of the tours.
 405       *
 406       * @return  stdClass[]
 407       */
 408      public static function get_tours() {
 409          global $DB;
 410  
 411          $tours = $DB->get_records('tool_usertours_tours', array(), 'sortorder ASC');
 412          $return = [];
 413          foreach ($tours as $tour) {
 414              $return[$tour->id] = tour::load_from_record($tour);
 415          }
 416          return $return;
 417      }
 418  
 419      /**
 420       * Get the specified tour.
 421       *
 422       * @param   int         $tourid     The tour that the step belongs to.
 423       * @return  tour
 424       */
 425      public static function get_tour($tourid) {
 426          return tour::instance($tourid);
 427      }
 428  
 429      /**
 430       * Fetch the tour with the specified sortorder.
 431       *
 432       * @param   int         $sortorder  The sortorder of the tour.
 433       * @return  tour
 434       */
 435      public static function get_tour_from_sortorder($sortorder) {
 436          global $DB;
 437  
 438          $tour = $DB->get_record('tool_usertours_tours', array('sortorder' => $sortorder));
 439          return tour::load_from_record($tour);
 440      }
 441  
 442      /**
 443       * Return the count of all tours.
 444       *
 445       * @return  int
 446       */
 447      public static function count_tours() {
 448          global $DB;
 449  
 450          return $DB->count_records('tool_usertours_tours');
 451      }
 452  
 453      /**
 454       * Reset the sortorder for all tours.
 455       */
 456      public static function reset_tour_sortorder() {
 457          global $DB;
 458          $tours = $DB->get_records('tool_usertours_tours', null, 'sortorder ASC, pathmatch DESC', 'id, sortorder');
 459  
 460          $index = 0;
 461          foreach ($tours as $tour) {
 462              if ($tour->sortorder != $index) {
 463                  $DB->set_field('tool_usertours_tours', 'sortorder', $index, array('id' => $tour->id));
 464              }
 465              $index++;
 466          }
 467  
 468          // Notify the cache that a tour has changed.
 469          // Tours are only stored in the cache if there are steps.
 470          // If there step count has changed for some reason, this will change the potential cache results.
 471          cache::notify_tour_change();
 472      }
 473  
 474  
 475      /**
 476       * Get all of the steps in the tour.
 477       *
 478       * @param   int         $tourid     The tour that the step belongs to.
 479       * @return  step[]
 480       */
 481      public static function get_steps($tourid) {
 482          $steps = cache::get_stepdata($tourid);
 483  
 484          $return = [];
 485          foreach ($steps as $step) {
 486              $return[$step->id] = step::load_from_record($step);
 487          }
 488          return $return;
 489      }
 490  
 491      /**
 492       * Fetch the specified step.
 493       *
 494       * @param   int         $stepid     The id of the step to fetch.
 495       * @return  step
 496       */
 497      public static function get_step($stepid) {
 498          return step::instance($stepid);
 499      }
 500  
 501      /**
 502       * Fetch the step with the specified sortorder.
 503       *
 504       * @param   int         $tourid     The tour that the step belongs to.
 505       * @param   int         $sortorder  The sortorder of the step.
 506       * @return  step
 507       */
 508      public static function get_step_from_sortorder($tourid, $sortorder) {
 509          global $DB;
 510  
 511          $step = $DB->get_record('tool_usertours_steps', array('tourid' => $tourid, 'sortorder' => $sortorder));
 512          return step::load_from_record($step);
 513      }
 514  
 515      /**
 516       * Handle addition of the tour into the current page.
 517       */
 518      public static function bootstrap() {
 519          global $PAGE;
 520  
 521          if (!isloggedin() || isguestuser()) {
 522              return;
 523          }
 524  
 525          if (in_array($PAGE->pagelayout, ['maintenance', 'print', 'redirect'])) {
 526              // Do not try to show user tours inside iframe, in maintenance mode,
 527              // when printing, or during redirects.
 528              return;
 529          }
 530  
 531          if (self::$bootstrapped) {
 532              return;
 533          }
 534          self::$bootstrapped = true;
 535  
 536          $tours = manager::get_current_tours();
 537  
 538          if ($tours) {
 539              $filters = static::get_all_clientside_filters();
 540  
 541              $tourdetails = array_map(function($tour) use ($filters) {
 542                  return [
 543                          'tourId' => $tour->get_id(),
 544                          'startTour' => $tour->should_show_for_user(),
 545                          'filtervalues' => $tour->get_client_filter_values($filters),
 546                  ];
 547              }, $tours);
 548  
 549              $filternames = [];
 550              foreach ($filters as $filter) {
 551                      $filternames[] = $filter::get_filter_name();
 552              }
 553  
 554              $PAGE->requires->js_call_amd('tool_usertours/usertours', 'init', [
 555                      $tourdetails,
 556                      $filternames,
 557              ]);
 558          }
 559      }
 560  
 561      /**
 562       * Get a list of all possible filters.
 563       *
 564       * @return  array
 565       */
 566      public static function get_all_filters() {
 567          $filters = \core_component::get_component_classes_in_namespace('tool_usertours', 'local\filter');
 568          $filters = array_keys($filters);
 569  
 570          $filters = array_filter($filters, function($filterclass) {
 571              $rc = new \ReflectionClass($filterclass);
 572              return $rc->isInstantiable();
 573          });
 574  
 575          $filters = array_merge($filters, static::get_all_clientside_filters());
 576  
 577          return $filters;
 578      }
 579  
 580      /**
 581       * Get a list of all clientside filters.
 582       *
 583       * @return  array
 584       */
 585      public static function get_all_clientside_filters() {
 586          $filters = \core_component::get_component_classes_in_namespace('tool_usertours', 'local\clientside_filter');
 587          $filters = array_keys($filters);
 588  
 589          $filters = array_filter($filters, function($filterclass) {
 590              $rc = new \ReflectionClass($filterclass);
 591              return $rc->isInstantiable();
 592          });
 593  
 594          return $filters;
 595      }
 596  
 597      /**
 598       * Attempt to fetch any matching langstring if the content is in the
 599       * format identifier,component.
 600       *
 601       * @param string $content Step's content or Tour's name or Tour's description
 602       * @return string Processed content, any langstring will be converted to translated text
 603       */
 604      public static function get_string_from_input(string $content): string {
 605          $content = trim($content);
 606  
 607          if (preg_match(static::LANG_STRING_REGEX, $content, $matches)) {
 608              if ($matches[2] === 'moodle') {
 609                  $matches[2] = 'core';
 610              }
 611  
 612              if (get_string_manager()->string_exists($matches[1], $matches[2])) {
 613                  $content = get_string($matches[1], $matches[2]);
 614              }
 615          }
 616  
 617          return $content;
 618      }
 619  
 620      /**
 621       * Check if the given string contains any matching langstring.
 622       *
 623       * @param string $string
 624       * @return bool
 625       */
 626      public static function is_language_string_from_input(string $string): bool {
 627          return preg_match(static::LANG_STRING_REGEX, $string) == true;
 628      }
 629  }