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\helpers;
  20  
  21  use core_text;
  22  
  23  /**
  24   * This class handles the setting and retrieving of a users' filter values for given reports
  25   *
  26   * It is currently using the user preference API as a storage mechanism
  27   *
  28   * @package     core_reportbuilder
  29   * @copyright   2021 Paul Holden <paulh@moodle.com>
  30   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31   */
  32  class user_filter_manager {
  33  
  34      /** @var int The size of each chunk, matching the maximum length of a single user preference */
  35      private const PREFERENCE_CHUNK_SIZE = 1333;
  36  
  37      /** @var string The prefix used to name the stored user preferences */
  38      private const PREFERENCE_NAME_PREFIX = 'reportbuilder-report-';
  39  
  40      /**
  41       * Generate user preference name for given report
  42       *
  43       * @param int $reportid
  44       * @param int $index
  45       * @return string
  46       */
  47      private static function user_preference_name(int $reportid, int $index): string {
  48          return static::PREFERENCE_NAME_PREFIX . "{$reportid}-{$index}";
  49      }
  50  
  51      /**
  52       * Set user filters for given report
  53       *
  54       * @param int $reportid
  55       * @param array $values
  56       * @param int|null $userid
  57       * @return bool
  58       */
  59      public static function set(int $reportid, array $values, int $userid = null): bool {
  60          $jsonvalues = json_encode($values);
  61  
  62          $jsonchunks = str_split($jsonvalues, static::PREFERENCE_CHUNK_SIZE);
  63          foreach ($jsonchunks as $index => $jsonchunk) {
  64              $userpreference = static::user_preference_name($reportid, $index);
  65              set_user_preference($userpreference, $jsonchunk, $userid);
  66          }
  67  
  68          // Ensure any subsequent preferences are reset (to account for number of chunks decreasing).
  69          static::reset_all($reportid, $userid, $index + 1);
  70  
  71          return true;
  72      }
  73  
  74      /**
  75       * Get user filters for given report
  76       *
  77       * @param int $reportid
  78       * @param int|null $userid
  79       * @return array
  80       */
  81      public static function get(int $reportid, int $userid = null): array {
  82          $jsonvalues = '';
  83          $index = 0;
  84  
  85          // We'll repeatedly append chunks to our JSON string, until we hit one that is below the maximum length.
  86          do {
  87              $userpreference = static::user_preference_name($reportid, $index++);
  88              $jsonchunk = get_user_preferences($userpreference, '', $userid);
  89              $jsonvalues .= $jsonchunk;
  90          } while (core_text::strlen($jsonchunk) === static::PREFERENCE_CHUNK_SIZE);
  91  
  92          return (array) json_decode($jsonvalues);
  93      }
  94  
  95      /**
  96       * Merge individual user filter values for given report
  97       *
  98       * @param int $reportid
  99       * @param array $values
 100       * @param int|null $userid
 101       * @return bool
 102       */
 103      public static function merge(int $reportid, array $values, int $userid = null): bool {
 104          $existing = static::get($reportid, $userid);
 105  
 106          return static::set($reportid, array_merge($existing, $values), $userid);
 107      }
 108  
 109      /**
 110       * Reset all user filters for given report
 111       *
 112       * @param int $reportid
 113       * @param int|null $userid
 114       * @param int $index If specified, then preferences will be reset starting from this index
 115       * @return bool
 116       */
 117      public static function reset_all(int $reportid, int $userid = null, int $index = 0): bool {
 118          // We'll repeatedly retrieve and reset preferences, until we hit one that is below the maximum length.
 119          do {
 120              $userpreference = static::user_preference_name($reportid, $index++);
 121              $jsonchunk = get_user_preferences($userpreference, '', $userid);
 122              unset_user_preference($userpreference, $userid);
 123          } while (core_text::strlen($jsonchunk) === static::PREFERENCE_CHUNK_SIZE);
 124  
 125          return true;
 126      }
 127  
 128      /**
 129       * Reset single user filter for given report
 130       *
 131       * @param int $reportid
 132       * @param string $uniqueidentifier
 133       * @param int|null $userid
 134       * @return bool
 135       */
 136      public static function reset_single(int $reportid, string $uniqueidentifier, int $userid = null): bool {
 137          $originalvalues = static::get($reportid, $userid);
 138  
 139          // Remove any filters whose name is prefixed by given identifier.
 140          $values = array_filter($originalvalues, static function(string $filterkey) use ($uniqueidentifier): bool {
 141              return core_text::strpos($filterkey, $uniqueidentifier) !== 0;
 142          }, ARRAY_FILTER_USE_KEY);
 143  
 144          return static::set($reportid, $values, $userid);
 145      }
 146  
 147      /**
 148       * Get all report filters for given user
 149       *
 150       * This is primarily designed for the privacy provider, and allows us to preserve all the preference logic within this class.
 151       *
 152       * @param int $userid
 153       * @return array
 154       */
 155      public static function get_all_for_user(int $userid): array {
 156          global $DB;
 157          $prefs = [];
 158  
 159          // We need to locate the first preference chunk of all report filters.
 160          $select = 'userid = :userid AND ' . $DB->sql_like('name', ':namelike');
 161          $params = [
 162              'userid' => $userid,
 163              'namelike' => $DB->sql_like_escape(static::PREFERENCE_NAME_PREFIX) . '%-0',
 164          ];
 165          $preferences = $DB->get_fieldset_select('user_preferences', 'name', $select, $params);
 166  
 167          // Retrieve all found filters.
 168          foreach ($preferences as $preference) {
 169              preg_match('/^' . static::PREFERENCE_NAME_PREFIX . '(?<reportid>\d+)\-/', $preference, $matches);
 170              $prefs[static::PREFERENCE_NAME_PREFIX . $matches['reportid']] = static::get((int) $matches['reportid'], $userid);
 171          }
 172  
 173          return $prefs;
 174      }
 175  }