Merge branch 'MDL-68991-master' of git://github.com/rezaies/moodle
[moodle.git] / lib / classes / userfeedback.php
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/>.
17 /**
18  * This file contains the core_userfeedback class
19  *
20  * @package    core
21  * @copyright  2020 Shamim Rezaie <shamim@moodle.com>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 /**
28  * This Class contains helper functions for user feedback functionality.
29  *
30  * @copyright  2020 Shamim Rezaie <shamim@moodle.com>
31  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32  */
33 class core_userfeedback {
34     /**
35      * @var int Ask user to give feedback a few days after each major upgrade.
36      */
37     public const REMIND_AFTER_UPGRADE = 1;
39     /**
40      * @var int Ask user to give feedback periodically.
41      */
42     public const REMIND_PERIODICALLY = 2;
44     /**
45      * @var int Do not ask user to give feedback.
46      */
47     public const REMIND_NEVER = 3;
49     /**
50      * Displays the feedback reminder block.
51      */
52     public static function print_reminder_block(): void {
53         global $PAGE;
55         static $jscalled = false;
57         $actions = [
58             [
59                 'title' => get_string('calltofeedback_give'),
60                 'url' => static::make_link()->out(false),
61                 'data' => [
62                     'action' => 'give',
63                     'record' => 1,
64                     'hide' => 1,
65                 ],
66             ],
67             [
68                 'title' => get_string('calltofeedback_remind'),
69                 'url' => '#',
70                 'data' => [
71                     'action' => 'remind',
72                     'record' => 1,
73                     'hide' => 1,
74                 ],
75             ],
76         ];
77         $icon = [
78             'pix' => 'i/bullhorn',
79             'component' => 'core'
80         ];
82         \core\notification::add_call_to_action($icon, get_string('calltofeedback'), $actions, 'core/userfeedback');
84         if (!$jscalled) {
85             $jscalled = true;
86             // Calling the following more than once will register event listeners twice.
87             $PAGE->requires->js_call_amd('core/userfeedback', 'registerEventListeners');
88         }
89     }
91     /**
92      * Indicates whether the feedback reminder block should be shown or not.
93      *
94      * @return bool
95      */
96     public static function should_display_reminder(): bool {
97         global $CFG;
99         if (static::can_give_feedback()) {
100             $give = get_user_preferences('core_userfeedback_give');
101             $remind = get_user_preferences('core_userfeedback_remind');
103             $lastactiontime = max($give ?: 0, $remind ?: 0);
105             switch ($CFG->userfeedback_nextreminder) {
106                 case static::REMIND_AFTER_UPGRADE:
107                     $lastupgrade = static::last_major_upgrade_time();
108                     if ($lastupgrade >= $lastactiontime) {
109                         return $lastupgrade + ($CFG->userfeedback_remindafter * DAYSECS) < time();
110                     }
111                     break;
112                 case static::REMIND_PERIODICALLY:
113                     return $lastactiontime + ($CFG->userfeedback_remindafter * DAYSECS) < time();
114                     break;
115             }
116         }
117         return false;
118     }
120     /**
121      * Prepare and return the URL of the feedback site
122      *
123      * @return moodle_url
124      */
125     public static function make_link(): moodle_url {
126         global $CFG, $PAGE;
128         $baseurl = $CFG->userfeedback_url ?? 'https://feedback.moodle.org/lms';
129         $lang = clean_param(current_language(), PARAM_LANG); // Avoid breaking WS because of incorrect package langs.
130         $moodleurl = $CFG->wwwroot;
131         $moodleversion = $CFG->release;
132         $theme = $PAGE->theme->name;
133         $themeversion = get_config('theme_'.$theme, 'version');
135         $url = new moodle_url($baseurl, [
136             'lang' => $lang,
137             'moodle_url' => $moodleurl,
138             'moodle_version' => $moodleversion,
139             'theme' => $theme,
140             'theme_version' => $themeversion,
141             'newtest' => 'Y', // Respondents might be using the same device/browser to fill out the survey.
142                               // The newtest param resets the session.
143         ]);
145         return $url;
146     }
148     /**
149      * Whether the current can give feedback.
150      *
151      * @return bool
152      */
153     public static function can_give_feedback(): bool {
154         global $CFG;
156         return !empty($CFG->enableuserfeedback) && isloggedin() && !isguestuser();
157     }
159     /**
160      * Returns the last major upgrade time
161      *
162      * @return int
163      */
164     private static function last_major_upgrade_time(): int {
165         global $DB;
167         $targetversioncast = $DB->sql_cast_char2real('targetversion');
168         $versioncast = $DB->sql_cast_char2real('version');
170         // A time difference more than 3 months has to be a core upgrade.
171         // Also, passing IGNORE_MULTIPLE because we are only interested in the first field and LIMIT is not cross-DB.
172         $time = $DB->get_field_sql("SELECT timemodified
173                                      FROM {upgrade_log}
174                                     WHERE plugin = 'core' AND $targetversioncast - $versioncast > 30000
175                                  ORDER BY timemodified DESC", null, IGNORE_MULTIPLE);
177         return (int)$time;
178     }