| 1 | efrain | 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 | }
 |