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.
   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  declare(strict_types=1);
  18  
  19  namespace core_reportbuilder\local\report;
  20  
  21  use action_menu_link;
  22  use lang_string;
  23  use moodle_url;
  24  use pix_icon;
  25  use popup_action;
  26  use stdClass;
  27  
  28  /**
  29   * Class to represent a report action
  30   *
  31   * @package     core_reportbuilder
  32   * @copyright   2021 Paul Holden <paulh@moodle.com>
  33   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  final class action {
  36  
  37      /** @var moodle_url $url */
  38      protected $url;
  39  
  40      /** @var pix_icon $icon */
  41      protected $icon;
  42  
  43      /** @var array $attributes */
  44      protected $attributes;
  45  
  46      /** @var bool $popup */
  47      protected $popup;
  48  
  49      /** @var callable[] $callbacks */
  50      protected $callbacks = [];
  51  
  52      /** @var lang_string|string $title */
  53      protected $title;
  54  
  55      /**
  56       * Create an instance of an action to be added to a report. Both the parameters of the URL, and the attributes parameter
  57       * support placeholders which will be replaced with appropriate row values, e.g.:
  58       *
  59       * new action(new moodle_url('/', ['id' => ':id']), new pix_icon(...), ['data-id' => ':id'])
  60       *
  61       * Note that all expected placeholders should be added as base fields to the report
  62       *
  63       * @param moodle_url $url
  64       * @param pix_icon $icon
  65       * @param string[] $attributes Array of attributes to include in action, each will be cast to string prior to use
  66       * @param bool $popup
  67       * @param ?lang_string $title
  68       */
  69      public function __construct(
  70          moodle_url $url,
  71          pix_icon $icon,
  72          array $attributes = [],
  73          bool $popup = false,
  74          ?lang_string $title = null
  75      ) {
  76          $this->url = $url;
  77          $this->icon = $icon;
  78          $this->attributes = $attributes;
  79          $this->popup = $popup;
  80          // If title is not passed, check the title attribute from the icon.
  81          $this->title = $title ?? $icon->attributes['title'] ?? '';
  82      }
  83  
  84      /**
  85       * Adds callback to the action. Used to verify action is available to current user, or preprocess values used in placeholders
  86       *
  87       * Multiple callbacks can be added. If at least one returns false then the action will not be displayed
  88       *
  89       * @param callable $callback
  90       * @return self
  91       */
  92      public function add_callback(callable $callback): self {
  93          $this->callbacks[] = $callback;
  94          return $this;
  95      }
  96  
  97      /**
  98       * Return action menu link suitable for output, or null if the action cannot be displayed (because one of its callbacks
  99       * returned false, {@see add_callback})
 100       *
 101       * @param stdClass $row
 102       * @return action_menu_link|null
 103       */
 104      public function get_action_link(stdClass $row): ?action_menu_link {
 105  
 106          foreach ($this->callbacks as $callback) {
 107              $row = clone $row; // Clone so we don't modify the shared row inside a callback.
 108              if (!$callback($row)) {
 109                  return null;
 110              }
 111          }
 112  
 113          // Create a new moodle_url instance with our filled in placeholders for this row.
 114          $url = new moodle_url(
 115              $this->url->out_omit_querystring(true),
 116              self::replace_placeholders($this->url->params(), $row)
 117          );
 118  
 119          // Ensure we have a title attribute set, if one wasn't already provided.
 120          if (!array_key_exists('title', $this->attributes)) {
 121              $this->attributes['title'] = (string) $this->title;
 122          }
 123          $this->attributes['aria-label'] = $this->attributes['title'];
 124  
 125          if ($this->popup) {
 126              $this->attributes['data-action'] = 'report-action-popup';
 127              $this->attributes['data-popup-action'] = json_encode(new popup_action('click', $url));
 128          }
 129  
 130          // Interpolate any placeholders with correct values.
 131          $attributes = self::replace_placeholders($this->attributes, $row);
 132  
 133          // Ensure title attribute isn't duplicated.
 134          $title = $attributes['title'];
 135          unset($attributes['title']);
 136  
 137          return new action_menu_link($url, $this->icon, $title, null, $attributes);
 138      }
 139  
 140      /**
 141       * Given an array of values, replace all placeholders with corresponding property of the given row
 142       *
 143       * @param string[] $values
 144       * @param stdClass $row
 145       * @return array
 146       */
 147      private static function replace_placeholders(array $values, stdClass $row): array {
 148          return array_map(static function($value) use ($row) {
 149              return preg_replace_callback('/^:(?<property>.*)$/', static function(array $matches) use ($row): string {
 150                  return (string) ($row->{$matches['property']} ?? '');
 151              }, (string) $value);
 152          }, $values);
 153      }
 154  }