Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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 mod_edusharing;
20
 
21
use coding_exception;
22
use dml_exception;
23
use EduSharingApiClient\CurlResult;
24
use EduSharingApiClient\CurlHandler as EdusharingCurlHandler;
25
use EduSharingApiClient\DisplayMode;
26
use EduSharingApiClient\EduSharingAuthHelper;
27
use EduSharingApiClient\EduSharingHelperBase;
28
use EduSharingApiClient\EduSharingNodeHelper;
29
use EduSharingApiClient\EduSharingNodeHelperConfig;
30
use EduSharingApiClient\MissingRightsException;
31
use EduSharingApiClient\NodeDeletedException;
32
use EduSharingApiClient\UrlHandling;
33
use EduSharingApiClient\Usage;
34
use EduSharingApiClient\UsageDeletedException;
35
use Exception;
36
use JsonException;
37
use moodle_exception;
38
use require_login_exception;
39
use stdClass;
40
 
41
/**
42
 * class EduSharingService
43
 *
44
 * Wrapper service class for API utilities bundled in the auth plugin
45
 *
46
 * @author Marian Ziegler <ziegler@edu-sharing.net>
47
 * @package mod_edusharing
48
 */
49
class EduSharingService {
50
    /**
51
     * @var EduSharingAuthHelper|null
52
     */
53
    private ?EduSharingAuthHelper $authhelper;
54
    /**
55
     * @var EduSharingNodeHelper|null
56
     */
57
    private ?EduSharingNodeHelper $nodehelper;
58
    /**
59
     * @var UtilityFunctions|null
60
     */
61
    private ?UtilityFunctions     $utils;
62
 
63
    /**
64
     * EduSharingService constructor
65
     *
66
     * constructor params are optional if you want to use DI.
67
     * This possibility is needed for unit testing
68
     *
69
     * @param EduSharingAuthHelper|null $authhelper
70
     * @param EduSharingNodeHelper|null $nodehelper
71
     * @param UtilityFunctions|null $utils
72
     * @throws dml_exception
73
     */
74
    public function __construct(
75
        ?EduSharingAuthHelper $authhelper = null,
76
        ?EduSharingNodeHelper $nodehelper = null,
77
        ?UtilityFunctions $utils = null
78
    ) {
79
        $this->authhelper = $authhelper;
80
        $this->nodehelper = $nodehelper;
81
        $this->utils      = $utils;
82
        global $CFG;
83
        require_once($CFG->dirroot . '/mod/edusharing/eduSharingAutoloader.php');
84
        $this->init();
85
    }
86
 
87
    /**
88
     * Function init
89
     *
90
     * @throws dml_exception
91
     * @throws Exception
92
     */
93
    private function init(): void {
94
        global $CFG;
95
        $this->utils === null && $this->utils = new UtilityFunctions();
96
        if ($this->authhelper === null || $this->nodehelper === null) {
97
            $internalurl = $this->utils->get_internal_url();
98
            $basehelper  = new EduSharingHelperBase(
99
                $internalurl,
100
                $this->utils->get_config_entry('application_private_key'),
101
                $this->utils->get_config_entry('application_appid')
102
            );
103
            $basehelper->registerCurlHandler(new MoodleCurlHandler());
104
            $this->authhelper === null && $this->authhelper = new EduSharingAuthHelper($basehelper);
105
            if ($this->nodehelper === null) {
106
                $nodeconfig       = new EduSharingNodeHelperConfig(
107
                    new UrlHandling(
108
                        true,
109
                        $CFG->wwwroot . "/filter/edusharing/inlineHelper.php?sesskey=" . sesskey()
110
                    )
111
                );
112
                $this->nodehelper = new EduSharingNodeHelper($basehelper, $nodeconfig);
113
            }
114
        }
115
    }
116
 
117
    /**
118
     * Function create_usage
119
     *
120
     * @param stdClass $usagedata
121
     * @return Usage
122
     * @throws JsonException
123
     * @throws MissingRightsException
124
     * @throws Exception
125
     */
126
    public function create_usage(stdClass $usagedata): Usage {
127
        return $this->nodehelper->createUsage(
128
            !empty($usagedata->ticket) ? $usagedata->ticket : $this->get_ticket(),
129
            (string)$usagedata->containerId,
130
            (string)$usagedata->resourceId,
131
            (string)$usagedata->nodeId,
132
            (string)$usagedata->nodeVersion
133
        );
134
    }
135
 
136
    /**
137
     * Function get_usage_id
138
     *
139
     * @param stdClass $usagedata
140
     * @return string
141
     * @throws Exception
142
     */
143
    public function get_usage_id(stdClass $usagedata): string {
144
        $usageid = $this->nodehelper->getUsageIdByParameters($usagedata->ticket,
145
            $usagedata->nodeId,
146
            $usagedata->containerId,
147
            $usagedata->resourceId
148
        );
149
        $usageid === null && throw new Exception('No usage found: ' . json_encode($usagedata));
150
        return $usageid;
151
    }
152
 
153
    /**
154
     * Function delete_usage
155
     *
156
     * @param stdClass $usagedata
157
     * @throws Exception
158
     */
159
    public function delete_usage(stdClass $usagedata): void {
160
        !isset($usagedata->usageId) && throw new Exception('No usage id provided, deletion cannot be performed');
161
        try {
162
            $this->nodehelper->deleteUsage($usagedata->nodeId, $usagedata->usageId);
163
        } catch (UsageDeletedException $usagedeletedexception) {
164
            debugging('noted, deleting locally: ' . $usagedeletedexception->getMessage());
165
        }
166
    }
167
 
168
    /**
169
     * Function get_node
170
     *
171
     * @param Usage $usage
172
     * @param array|null $renderingparams
173
     * @param string|null $userid
174
     * @return array
175
     * @throws JsonException
176
     * @throws NodeDeletedException
177
     * @throws UsageDeletedException
178
     */
179
    public function get_node(Usage $usage, ?array $renderingparams = null, ?string $userid = null): array {
180
        return $this->nodehelper->getNodeByUsage($usage, DisplayMode::INLINE, $renderingparams, $userid);
181
    }
182
 
183
    /**
184
     * Function get_redirect_url
185
     *
186
     * @param Usage $usage
187
     * @param string|null $userid
188
     * @param string $mode
189
     * @return string
190
     * @throws JsonException
191
     * @throws NodeDeletedException
192
     * @throws UsageDeletedException
193
     */
194
    public function get_redirect_url(Usage $usage, ?string $userid = null, string $mode = 'content'): string {
195
        return $this->nodehelper->getRedirectUrl($mode, $usage, [], $userid);
196
    }
197
 
198
    /**
199
     * Function get_ticket
200
     *
201
     * @throws Exception
202
     */
203
    public function get_ticket(): string {
204
        global $USER;
205
        if (isset($USER->edusharing_userticket)) {
206
            if (isset($USER->edusharing_userticketvalidationts) && time() - $USER->edusharing_userticketvalidationts < 10) {
207
                return $USER->edusharing_userticket;
208
            }
209
            $ticketinfo = $this->authhelper->getTicketAuthenticationInfo($USER->edusharing_userticket);
210
            if ($ticketinfo['statusCode'] === 'OK') {
211
                $USER->edusharing_userticketvalidationts = time();
212
 
213
                return $USER->edusharing_userticket;
214
            }
215
        }
216
        $additionalfields = null;
217
        if ($this->utils->get_config_entry('send_additional_auth') === '1') {
218
            $additionalfields = [
219
                'firstName' => $USER->firstname,
220
                'lastName'  => $USER->lastname,
221
                'email'     => $USER->email,
222
            ];
223
        }
224
        return $this->authhelper->getTicketForUser($this->utils->get_auth_key(), $additionalfields);
225
    }
226
 
227
    /**
228
     * Function delete_instance
229
     *
230
     * Given an ID of an instance of this module,
231
     * this function will permanently delete the instance
232
     * and any data that depends on it.
233
     *
234
     * @param string $id
235
     * @return void
236
     * @throws dml_exception
237
     * @throws Exception
238
     */
239
    public function delete_instance(string $id): void {
240
        global $DB;
241
        $edusharing             = $DB->get_record('edusharing', ['id' => $id], '*', MUST_EXIST);
242
        $usagedata              = new stdClass();
243
        $usagedata->ticket      = $this->get_ticket();
244
        $usagedata->nodeId      = $this->utils->get_object_id_from_url($edusharing->object_url);
245
        $usagedata->containerId = $edusharing->course;
246
        $usagedata->resourceId  = $edusharing->id;
247
        $usagedata->usageId    = empty($edusharing->usage_id) ? $this->get_usage_id($usagedata) : $edusharing->usage_id;
248
        $this->delete_usage($usagedata);
249
        $DB->delete_records('edusharing', ['id' => $edusharing->id]);
250
    }
251
 
252
    /**
253
     * Function add_instance
254
     *
255
     * @param stdClass $edusharing
256
     * @param int|null $updatetime
257
     * @return bool|int
258
     */
259
    public function add_instance(stdClass $edusharing, ?int $updatetime = null): bool|int {
260
        global $DB;
261
 
262
        $edusharing->timecreated  = $updatetime ?? time();
263
        $edusharing->timemodified = $updatetime ?? time();
264
 
265
        // You may have to add extra stuff in here.
266
        $this->post_process_edusharing_object($edusharing, $updatetime);
267
 
268
        if (isset($_POST['object_version']) && $_POST['object_version'] != '0') {
269
            $edusharing->object_version = $_POST['object_version'];
270
        }
271
        // Use simple version handling for atto plugin or legacy code.
272
        if (isset($edusharing->editor_atto)) {
273
            // Avoid database error.
274
            $edusharing->introformat = 0;
275
        } else if (isset($edusharing->window_versionshow) && $edusharing->window_versionshow == 'current') {
276
            $edusharing->object_version = $edusharing->window_version;
277
        }
278
        try {
279
            $id = $DB->insert_record('edusharing', $edusharing);
280
        } catch (Exception $exception) {
281
            debugging($exception->getMessage());
282
            return false;
283
        }
284
        $usagedata              = new stdClass();
285
        $usagedata->containerId = $edusharing->course;
286
        $usagedata->resourceId  = $id;
287
        $usagedata->nodeId      = $this->utils->get_object_id_from_url($edusharing->object_url);
288
        $usagedata->nodeVersion = $edusharing->object_version;
289
        try {
290
            $usage                = $this->create_usage($usagedata);
291
            $edusharing->id       = $id;
292
            $edusharing->usage_id = $usage->usageId;
293
            $DB->update_record('edusharing', $edusharing);
294
            return $id;
295
        } catch (Exception $exception) {
296
            !empty($exception->getMessage()) && debugging($exception->getMessage());
297
            try {
298
                $DB->delete_records('edusharing', ['id' => $id]);
299
            } catch (Exception $deleteexception) {
300
                debugging($deleteexception->getMessage());
301
            }
302
            return false;
303
        }
304
    }
305
 
306
    /**
307
     * Function update_instance
308
     *
309
     * @param stdClass $edusharing
310
     * @param int|null $updatetime
311
     * @return bool
312
     */
313
    public function update_instance(stdClass $edusharing, ?int $updatetime = null): bool {
314
        global $DB;
315
        // FIX: when editing a moodle-course-module the $edusharing->id will be named $edusharing->instance.
316
        if (!empty($edusharing->instance)) {
317
            $edusharing->id = $edusharing->instance;
318
        }
319
        $this->post_process_edusharing_object($edusharing, $updatetime);
320
        $usagedata              = new stdClass();
321
        $usagedata->containerId = $edusharing->course;
322
        $usagedata->resourceId  = $edusharing->id;
323
        $usagedata->nodeId      = $this->utils->get_object_id_from_url($edusharing->object_url);
324
        $usagedata->nodeVersion = $edusharing->object_version;
325
        try {
326
            $memento           = $DB->get_record('edusharing', ['id' => $edusharing->id], '*', MUST_EXIST);
327
            $usagedata->ticket = $this->get_ticket();
328
        } catch (Exception $exception) {
329
            unset($exception);
330
            return false;
331
        }
332
        try {
333
            $usage                = $this->create_usage($usagedata);
334
            $edusharing->usage_id = $usage->usageId;
335
            $DB->update_record('edusharing', $edusharing);
336
        } catch (Exception $exception) {
337
            !empty($exception->getMessage()) && debugging($exception->getMessage());
338
            try {
339
                $DB->update_record('edusharing', $memento);
340
            } catch (Exception $updateexception) {
341
                !empty($exception->getMessage()) && debugging($updateexception->getMessage());
342
            }
343
            return false;
344
        }
345
        return true;
346
    }
347
 
348
    /**
349
     * Function post_process_edusharing_object
350
     *
351
     * @param stdClass $edusharing
352
     * @param int|null $updatetime
353
     * @return void
354
     */
355
    private function post_process_edusharing_object(stdClass $edusharing, ?int $updatetime = null): void {
356
        if ($updatetime === null) {
357
            $updatetime = time();
358
        }
359
        global $COURSE;
360
        if (empty($edusharing->timecreated)) {
361
            $edusharing->timecreated = $updatetime;
362
        }
363
        $edusharing->timeupdated = $updatetime;
364
        if (!empty($edusharing->force_download)) {
365
            $edusharing->force_download = 1;
366
            $edusharing->popup_window   = 0;
367
        } else if (!empty($edusharing->popup_window)) {
368
            $edusharing->force_download = 0;
369
            $edusharing->options        = '';
370
        } else {
371
            if (empty($edusharing->blockdisplay)) {
372
                $edusharing->options = '';
373
            }
374
            $edusharing->popup_window = '';
375
        }
376
        $edusharing->tracking = empty($edusharing->tracking) ? 0 : $edusharing->tracking;
377
        if (!$edusharing->course) {
378
            $edusharing->course = $COURSE->id;
379
        }
380
    }
381
 
382
    /**
383
     * Function import_metadata
384
     *
385
     * @param string $url
386
     * @return CurlResult
387
     */
388
    public function import_metadata(string $url): CurlResult {
389
        $curloptions = [
390
            CURLOPT_SSL_VERIFYPEER => false,
391
            CURLOPT_SSL_VERIFYHOST => false,
392
            CURLOPT_FOLLOWLOCATION => 1,
393
            CURLOPT_HEADER         => 0,
394
            CURLOPT_RETURNTRANSFER => 1,
395
            CURLOPT_USERAGENT      => $_SERVER['HTTP_USER_AGENT'],
396
        ];
397
        return $this->authhelper->base->handleCurlRequest($url, $curloptions);
398
    }
399
 
400
    /**
401
     * Function validate_session
402
     *
403
     * @param string $url
404
     * @param string $auth
405
     * @return CurlResult
406
     */
407
    public function validate_session(string $url, string $auth): CurlResult {
408
        $headers = [
409
            'Content-Type: application/json',
410
            'Accept: application/json',
411
            'Authorization: Basic ' . base64_encode($auth),
412
        ];
413
        $url     = rtrim($url, '/') . '/rest/authentication/v1/validateSession';
414
        return $this->authhelper->base->handleCurlRequest($url, [
415
            CURLOPT_RETURNTRANSFER => 1,
416
            CURLOPT_HTTPHEADER     => $headers,
417
        ]);
418
    }
419
 
420
    /**
421
     * Function register_plugin
422
     *
423
     * @param string $url
424
     * @param string $delimiter
425
     * @param string $body
426
     * @param string $auth
427
     * @return CurlResult
428
     */
429
    public function register_plugin(string $url, string $delimiter, string $body, string $auth): CurlResult {
430
        $registrationurl = rtrim($url, '/') . '/rest/admin/v1/applications/xml';
431
        $headers         = [
432
            'Content-Type: multipart/form-data; boundary=' . $delimiter,
433
            'Content-Length: ' . strlen($body),
434
            'Accept: application/json',
435
            'Authorization: Basic ' . base64_encode($auth),
436
        ];
437
        $this->authhelper->base->curlHandler->setMethod(EdusharingCurlHandler::METHOD_PUT);
438
        return $this->authhelper->base->handleCurlRequest($registrationurl, [
439
            CURLOPT_RETURNTRANSFER => 1,
440
            CURLOPT_HTTPHEADER     => $headers,
441
            CURLOPT_POSTFIELDS     => $body,
442
        ]);
443
    }
444
 
445
    /**
446
     * Function sign
447
     *
448
     * @param string $input
449
     * @return string
450
     */
451
    public function sign(string $input): string {
452
        return $this->nodehelper->base->sign($input);
453
    }
454
 
455
    /**
456
     * Function get_render_html
457
     *
458
     * @param string $url
459
     * @return string
460
     */
461
    public function get_render_html(string $url): string {
462
        $curloptions = [
463
            CURLOPT_SSL_VERIFYPEER => false,
464
            CURLOPT_SSL_VERIFYHOST => false,
465
            CURLOPT_FOLLOWLOCATION => 1,
466
            CURLOPT_HEADER         => 0,
467
            CURLOPT_RETURNTRANSFER => 1,
468
            CURLOPT_USERAGENT      => $_SERVER['HTTP_USER_AGENT'],
469
        ];
470
        $result      = $this->authhelper->base->handleCurlRequest($url, $curloptions);
471
        if ($result->error !== 0) {
472
            try {
473
                return 'Unexpected Error';
474
            } catch (Exception $exception) {
475
                return $exception->getMessage();
476
            }
477
        }
478
        return $result->content;
479
    }
480
 
481
    /**
482
     * Function require_edu_login
483
     *
484
     * @param int|null $courseid
485
     * @param bool $checkticket
486
     * @param bool $checksessionkey
487
     * @throws coding_exception
488
     * @throws moodle_exception
489
     * @throws require_login_exception
490
     * @throws Exception
491
     */
492
    public function require_edu_login(?int $courseid = null, bool $checkticket = true, bool $checksessionkey = true): void {
493
        require_login($courseid);
494
        $checksessionkey && require_sesskey();
495
        $checkticket && $this->get_ticket();
496
    }
497
 
498
    /**
499
     * Function get_preview_image
500
     *
501
     * @param Usage $usage
502
     * @return CurlResult
503
     */
504
    public function get_preview_image(Usage $usage): CurlResult {
505
        return $this->nodehelper->getPreview($usage);
506
    }
507
}