Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 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
namespace core_user\route\api;
18
 
19
use core\exception\coding_exception;
20
use core\exception\invalid_parameter_exception;
21
use core\param;
22
use core\router\route;
23
use core\router\schema\objects\scalar_type;
24
use core\router\schema\response\payload_response;
25
use core\router\schema\response\content\payload_response_type;
26
use core\router\schema\response\response_type;
27
use core\user;
28
use core_user\route\responses\user_preferences_response;
29
use stdClass;
30
use Psr\Http\Message\ResponseInterface;
31
use Psr\Http\Message\ServerRequestInterface;
32
 
33
/**
34
 * User preference API handler.
35
 *
36
 * @package    core_user
37
 * @copyright  Andrew Lyons <andrew@nicols.co.uk>
38
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39
 */
40
#[route(
41
    path: '/{user}/preferences',
42
    pathtypes: [
43
        new \core\router\parameters\path_user(),
44
    ],
45
)]
46
class preferences {
47
    /**
48
     * Fetch all user preferences, or a specific user preference.
49
     *
50
     * @param ResponseInterface $response
51
     * @param ServerRequestInterface $request
52
     * @param stdClass $user
53
     * @param null|string $preference
54
     * @return payload_response
55
     */
56
    #[route(
57
        path: '[/{preference}]',
58
        title: 'Fetch user preferences',
59
        description: 'Fetch one user preference, or all user preferences',
60
        pathtypes: [
61
            new \core\router\schema\parameters\path_parameter(
62
                name: 'preference',
63
                type: param::RAW,
64
            ),
65
        ],
66
        responses: [
67
            new user_preferences_response(),
68
        ],
69
    )]
70
    public function get_preferences(
71
        ResponseInterface $response,
72
        ServerRequestInterface $request,
73
        stdClass $user,
74
        ?string  $preference,
75
    ): payload_response {
76
        $this->check_user($user);
77
 
78
        $result = get_user_preferences(
79
            name: $preference,
80
            user: $user,
81
        );
82
 
83
        if (!is_array($result)) {
84
            // Check if we received just one preference.
85
            $result = [$preference => $result];
86
        }
87
 
88
        return new payload_response($result, $request, $response);
89
    }
90
 
91
    /**
92
     * Set a set of user preferences.
93
     *
94
     * @param ResponseInterface $response
95
     * @param stdClass $user
96
     * @return payload_response
97
     */
98
    #[route(
99
        method: ['POST'],
100
        title: 'Set or update multiple user preferences',
101
        requestbody: new \core\router\schema\request_body(
102
            content: new payload_response_type(
103
                schema: new \core\router\schema\objects\schema_object(
104
                    content: [
105
                        'preferences' => new \core\router\schema\objects\array_of_strings(
106
                            keyparamtype: param::TEXT,
107
                            valueparamtype: param::RAW,
108
                        ),
109
                    ],
110
                ),
111
            ),
112
        ),
113
        responses: [
114
            new user_preferences_response(),
115
        ],
116
    )]
117
    public function set_preferences(
118
        ResponseInterface $response,
119
        ServerRequestInterface $request,
120
        stdClass $user,
121
    ): payload_response {
122
        $this->check_user($user);
123
 
124
        $values = $request->getParsedBody();
125
        $preferences = $values['preferences'] ?? [];
126
 
127
        foreach ($preferences as $preference => $value) {
128
            $this->set_single_preference($user, $preference, $value);
129
        }
130
 
131
        $result = array_filter(
132
            get_user_preferences(
133
                user: $user,
134
            ),
135
            fn ($preference) => array_key_exists($preference, $preferences),
136
            ARRAY_FILTER_USE_KEY,
137
        );
138
 
139
        return new payload_response($result, $request, $response);
140
    }
141
 
142
    /**
143
     * Set a single user preference.
144
     *
145
     * @param ResponseInterface $response
146
     * @param string $themename
147
     * @param string $component
148
     * @param null|string $identifier
149
     * @return response_type
150
     */
151
    #[route(
152
        path: '/{preference}',
153
        method: ['POST'],
154
        title: 'Set a single user preference',
155
        description: 'Set a single user preference',
156
        pathtypes: [
157
            new \core\router\schema\parameters\path_parameter(
158
                name: 'preference',
159
                type: param::RAW,
160
            ),
161
        ],
162
        requestbody: new \core\router\schema\request_body(
163
            content: new payload_response_type(
164
                schema: new \core\router\schema\objects\schema_object(
165
                    content: [
166
                        'value' => new scalar_type(param::RAW),
167
                    ],
168
                ),
169
            ),
170
        ),
171
        responses: [
172
            new \core\router\schema\response\response(
173
                statuscode: 200,
174
                description: 'OK',
175
                content: [
176
                    new \core\router\schema\response\content\json_media_type(
177
                        schema: new \core\router\schema\objects\array_of_strings(
178
                            keyparamtype: param::TEXT,
179
                            valueparamtype: param::RAW,
180
                        ),
181
                        examples: [
182
                            new \core\router\schema\example(
183
                                name: 'A single preference value',
184
                                summary: 'A json response containing a single preference',
185
                                value: [
186
                                    "drawers-open-index" => "1",
187
                                ],
188
                            ),
189
                        ]
190
                    ),
191
                ],
192
            ),
193
        ],
194
    )]
195
    public function set_preference(
196
        ResponseInterface $response,
197
        ServerRequestInterface $request,
198
        stdClass $user,
199
        ?string $preference,
200
    ): response_type {
201
        $this->check_user($user);
202
 
203
        $values = $request->getParsedBody();
204
        $value = $values['value'] ?? null;
205
        $this->set_single_preference($user, $preference, $value);
206
 
207
        return $this->get_preferences($response, $request, $user, $preference);
208
    }
209
 
210
    /**
211
     * Set a single user preference.
212
     *
213
     * @param \stdClass $user
214
     * @param string $preference
215
     * @param mixed $value
216
     * @throws \core\exception\access_denied_exception
217
     * @throws \invalid_parameter_exception
218
     */
219
    protected function set_single_preference(
220
        stdClass $user,
221
        string $preference,
222
        mixed $value,
223
    ): void {
224
        try {
225
            $definition = user::get_preference_definition($preference);
226
        } catch (coding_exception $e) {
227
            throw new invalid_parameter_exception("Invalid preference '$preference'");
228
        }
229
 
230
        if (!user::can_edit_preference($preference, $user)) {
231
            throw new \core\exception\access_denied_exception('You do not have permission to edit this preference.');
232
        }
233
 
234
        if (isset($definition['type'])) {
235
            $type = param::from_type($definition['type']);
236
            $value = $this->standardise_value($type, $value);
237
        }
238
 
239
        $cleanvalue = user::clean_preference($value, $preference);
240
        if ($cleanvalue !== $value) {
241
            throw new \invalid_parameter_exception("Invalid value for preference '$preference': '{$value}'");
242
        }
243
        $value = $cleanvalue;
244
        set_user_preference($preference, $value, $user->id);
245
    }
246
 
247
    /**
248
     * Ensure that the requested user meets the requirements.
249
     *
250
     * @param stdClass $user
251
     * @throws invalid_parameter_exception
252
     */
253
    protected function check_user(stdClass $user): void {
254
        global $USER;
255
 
256
        if ($user->id !== $USER->id) {
257
            throw new \core\exception\access_denied_exception(
258
                'You do not have permission to view or edit preferences for other users.',
259
            );
260
        }
261
    }
262
 
263
    /**
264
     * Standardise value based on type.
265
     *
266
     * Note: We cannot use \core\param here because we only want to cast some types.
267
     * Requests do not have an inherent understanding of anything but strings. We need to be strict on typing of integers and bools.
268
     *
269
     * @param string param $type
270
     * @param mixed $value
271
     * @return mixed
272
     */
273
    protected function standardise_value(param $type, mixed $value): mixed {
274
        if (is_numeric($value) || is_bool($value)) {
275
            switch ($type) {
276
                case param::INT:
277
                case param::BOOL:
278
                    $value = (int) $value;
279
            }
280
        }
281
 
282
        return $value;
283
    }
284
}