AutorÃa | Ultima modificación | Ver Log |
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace mod_edusharing;
use coding_exception;
use dml_exception;
use EduSharingApiClient\CurlResult;
use EduSharingApiClient\CurlHandler as EdusharingCurlHandler;
use EduSharingApiClient\DisplayMode;
use EduSharingApiClient\EduSharingAuthHelper;
use EduSharingApiClient\EduSharingHelperBase;
use EduSharingApiClient\EduSharingNodeHelper;
use EduSharingApiClient\EduSharingNodeHelperConfig;
use EduSharingApiClient\MissingRightsException;
use EduSharingApiClient\NodeDeletedException;
use EduSharingApiClient\UrlHandling;
use EduSharingApiClient\Usage;
use EduSharingApiClient\UsageDeletedException;
use Exception;
use JsonException;
use moodle_exception;
use require_login_exception;
use stdClass;
/**
* class EduSharingService
*
* Wrapper service class for API utilities bundled in the auth plugin
*
* @author Marian Ziegler <ziegler@edu-sharing.net>
* @package mod_edusharing
*/
class EduSharingService {
/**
* @var EduSharingAuthHelper|null
*/
private ?EduSharingAuthHelper $authhelper;
/**
* @var EduSharingNodeHelper|null
*/
private ?EduSharingNodeHelper $nodehelper;
/**
* @var UtilityFunctions|null
*/
private ?UtilityFunctions $utils;
/**
* EduSharingService constructor
*
* constructor params are optional if you want to use DI.
* This possibility is needed for unit testing
*
* @param EduSharingAuthHelper|null $authhelper
* @param EduSharingNodeHelper|null $nodehelper
* @param UtilityFunctions|null $utils
* @throws dml_exception
*/
public function __construct(
?EduSharingAuthHelper $authhelper = null,
?EduSharingNodeHelper $nodehelper = null,
?UtilityFunctions $utils = null
) {
$this->authhelper = $authhelper;
$this->nodehelper = $nodehelper;
$this->utils = $utils;
global $CFG;
require_once($CFG->dirroot . '/mod/edusharing/eduSharingAutoloader.php');
$this->init();
}
/**
* Function init
*
* @throws dml_exception
* @throws Exception
*/
private function init(): void {
global $CFG;
$this->utils === null && $this->utils = new UtilityFunctions();
if ($this->authhelper === null || $this->nodehelper === null) {
$internalurl = $this->utils->get_internal_url();
$basehelper = new EduSharingHelperBase(
$internalurl,
$this->utils->get_config_entry('application_private_key'),
$this->utils->get_config_entry('application_appid')
);
$basehelper->registerCurlHandler(new MoodleCurlHandler());
$this->authhelper === null && $this->authhelper = new EduSharingAuthHelper($basehelper);
if ($this->nodehelper === null) {
$nodeconfig = new EduSharingNodeHelperConfig(
new UrlHandling(
true,
$CFG->wwwroot . "/filter/edusharing/inlineHelper.php?sesskey=" . sesskey()
)
);
$this->nodehelper = new EduSharingNodeHelper($basehelper, $nodeconfig);
}
}
}
/**
* Function create_usage
*
* @param stdClass $usagedata
* @return Usage
* @throws JsonException
* @throws MissingRightsException
* @throws Exception
*/
public function create_usage(stdClass $usagedata): Usage {
return $this->nodehelper->createUsage(
!empty($usagedata->ticket) ? $usagedata->ticket : $this->get_ticket(),
(string)$usagedata->containerId,
(string)$usagedata->resourceId,
(string)$usagedata->nodeId,
(string)$usagedata->nodeVersion
);
}
/**
* Function get_usage_id
*
* @param stdClass $usagedata
* @return string
* @throws Exception
*/
public function get_usage_id(stdClass $usagedata): string {
$usageid = $this->nodehelper->getUsageIdByParameters($usagedata->ticket,
$usagedata->nodeId,
$usagedata->containerId,
$usagedata->resourceId
);
$usageid === null && throw new Exception('No usage found: ' . json_encode($usagedata));
return $usageid;
}
/**
* Function delete_usage
*
* @param stdClass $usagedata
* @throws Exception
*/
public function delete_usage(stdClass $usagedata): void {
!isset($usagedata->usageId) && throw new Exception('No usage id provided, deletion cannot be performed');
try {
$this->nodehelper->deleteUsage($usagedata->nodeId, $usagedata->usageId);
} catch (UsageDeletedException $usagedeletedexception) {
debugging('noted, deleting locally: ' . $usagedeletedexception->getMessage());
}
}
/**
* Function get_node
*
* @param Usage $usage
* @param array|null $renderingparams
* @param string|null $userid
* @return array
* @throws JsonException
* @throws NodeDeletedException
* @throws UsageDeletedException
*/
public function get_node(Usage $usage, ?array $renderingparams = null, ?string $userid = null): array {
return $this->nodehelper->getNodeByUsage($usage, DisplayMode::INLINE, $renderingparams, $userid);
}
/**
* Function get_redirect_url
*
* @param Usage $usage
* @param string|null $userid
* @param string $mode
* @return string
* @throws JsonException
* @throws NodeDeletedException
* @throws UsageDeletedException
*/
public function get_redirect_url(Usage $usage, ?string $userid = null, string $mode = 'content'): string {
return $this->nodehelper->getRedirectUrl($mode, $usage, [], $userid);
}
/**
* Function get_ticket
*
* @throws Exception
*/
public function get_ticket(): string {
global $USER;
if (isset($USER->edusharing_userticket)) {
if (isset($USER->edusharing_userticketvalidationts) && time() - $USER->edusharing_userticketvalidationts < 10) {
return $USER->edusharing_userticket;
}
$ticketinfo = $this->authhelper->getTicketAuthenticationInfo($USER->edusharing_userticket);
if ($ticketinfo['statusCode'] === 'OK') {
$USER->edusharing_userticketvalidationts = time();
return $USER->edusharing_userticket;
}
}
$additionalfields = null;
if ($this->utils->get_config_entry('send_additional_auth') === '1') {
$additionalfields = [
'firstName' => $USER->firstname,
'lastName' => $USER->lastname,
'email' => $USER->email,
];
}
return $this->authhelper->getTicketForUser($this->utils->get_auth_key(), $additionalfields);
}
/**
* Function delete_instance
*
* Given an ID of an instance of this module,
* this function will permanently delete the instance
* and any data that depends on it.
*
* @param string $id
* @return void
* @throws dml_exception
* @throws Exception
*/
public function delete_instance(string $id): void {
global $DB;
$edusharing = $DB->get_record('edusharing', ['id' => $id], '*', MUST_EXIST);
$usagedata = new stdClass();
$usagedata->ticket = $this->get_ticket();
$usagedata->nodeId = $this->utils->get_object_id_from_url($edusharing->object_url);
$usagedata->containerId = $edusharing->course;
$usagedata->resourceId = $edusharing->id;
$usagedata->usageId = empty($edusharing->usage_id) ? $this->get_usage_id($usagedata) : $edusharing->usage_id;
$this->delete_usage($usagedata);
$DB->delete_records('edusharing', ['id' => $edusharing->id]);
}
/**
* Function add_instance
*
* @param stdClass $edusharing
* @param int|null $updatetime
* @return bool|int
*/
public function add_instance(stdClass $edusharing, ?int $updatetime = null): bool|int {
global $DB;
$edusharing->timecreated = $updatetime ?? time();
$edusharing->timemodified = $updatetime ?? time();
// You may have to add extra stuff in here.
$this->post_process_edusharing_object($edusharing, $updatetime);
if (isset($_POST['object_version']) && $_POST['object_version'] != '0') {
$edusharing->object_version = $_POST['object_version'];
}
// Use simple version handling for atto plugin or legacy code.
if (isset($edusharing->editor_atto)) {
// Avoid database error.
$edusharing->introformat = 0;
} else if (isset($edusharing->window_versionshow) && $edusharing->window_versionshow == 'current') {
$edusharing->object_version = $edusharing->window_version;
}
try {
$id = $DB->insert_record('edusharing', $edusharing);
} catch (Exception $exception) {
debugging($exception->getMessage());
return false;
}
$usagedata = new stdClass();
$usagedata->containerId = $edusharing->course;
$usagedata->resourceId = $id;
$usagedata->nodeId = $this->utils->get_object_id_from_url($edusharing->object_url);
$usagedata->nodeVersion = $edusharing->object_version;
try {
$usage = $this->create_usage($usagedata);
$edusharing->id = $id;
$edusharing->usage_id = $usage->usageId;
$DB->update_record('edusharing', $edusharing);
return $id;
} catch (Exception $exception) {
!empty($exception->getMessage()) && debugging($exception->getMessage());
try {
$DB->delete_records('edusharing', ['id' => $id]);
} catch (Exception $deleteexception) {
debugging($deleteexception->getMessage());
}
return false;
}
}
/**
* Function update_instance
*
* @param stdClass $edusharing
* @param int|null $updatetime
* @return bool
*/
public function update_instance(stdClass $edusharing, ?int $updatetime = null): bool {
global $DB;
// FIX: when editing a moodle-course-module the $edusharing->id will be named $edusharing->instance.
if (!empty($edusharing->instance)) {
$edusharing->id = $edusharing->instance;
}
$this->post_process_edusharing_object($edusharing, $updatetime);
$usagedata = new stdClass();
$usagedata->containerId = $edusharing->course;
$usagedata->resourceId = $edusharing->id;
$usagedata->nodeId = $this->utils->get_object_id_from_url($edusharing->object_url);
$usagedata->nodeVersion = $edusharing->object_version;
try {
$memento = $DB->get_record('edusharing', ['id' => $edusharing->id], '*', MUST_EXIST);
$usagedata->ticket = $this->get_ticket();
} catch (Exception $exception) {
unset($exception);
return false;
}
try {
$usage = $this->create_usage($usagedata);
$edusharing->usage_id = $usage->usageId;
$DB->update_record('edusharing', $edusharing);
} catch (Exception $exception) {
!empty($exception->getMessage()) && debugging($exception->getMessage());
try {
$DB->update_record('edusharing', $memento);
} catch (Exception $updateexception) {
!empty($exception->getMessage()) && debugging($updateexception->getMessage());
}
return false;
}
return true;
}
/**
* Function post_process_edusharing_object
*
* @param stdClass $edusharing
* @param int|null $updatetime
* @return void
*/
private function post_process_edusharing_object(stdClass $edusharing, ?int $updatetime = null): void {
if ($updatetime === null) {
$updatetime = time();
}
global $COURSE;
if (empty($edusharing->timecreated)) {
$edusharing->timecreated = $updatetime;
}
$edusharing->timeupdated = $updatetime;
if (!empty($edusharing->force_download)) {
$edusharing->force_download = 1;
$edusharing->popup_window = 0;
} else if (!empty($edusharing->popup_window)) {
$edusharing->force_download = 0;
$edusharing->options = '';
} else {
if (empty($edusharing->blockdisplay)) {
$edusharing->options = '';
}
$edusharing->popup_window = '';
}
$edusharing->tracking = empty($edusharing->tracking) ? 0 : $edusharing->tracking;
if (!$edusharing->course) {
$edusharing->course = $COURSE->id;
}
}
/**
* Function import_metadata
*
* @param string $url
* @return CurlResult
*/
public function import_metadata(string $url): CurlResult {
$curloptions = [
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_HEADER => 0,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'],
];
return $this->authhelper->base->handleCurlRequest($url, $curloptions);
}
/**
* Function validate_session
*
* @param string $url
* @param string $auth
* @return CurlResult
*/
public function validate_session(string $url, string $auth): CurlResult {
$headers = [
'Content-Type: application/json',
'Accept: application/json',
'Authorization: Basic ' . base64_encode($auth),
];
$url = rtrim($url, '/') . '/rest/authentication/v1/validateSession';
return $this->authhelper->base->handleCurlRequest($url, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HTTPHEADER => $headers,
]);
}
/**
* Function register_plugin
*
* @param string $url
* @param string $delimiter
* @param string $body
* @param string $auth
* @return CurlResult
*/
public function register_plugin(string $url, string $delimiter, string $body, string $auth): CurlResult {
$registrationurl = rtrim($url, '/') . '/rest/admin/v1/applications/xml';
$headers = [
'Content-Type: multipart/form-data; boundary=' . $delimiter,
'Content-Length: ' . strlen($body),
'Accept: application/json',
'Authorization: Basic ' . base64_encode($auth),
];
$this->authhelper->base->curlHandler->setMethod(EdusharingCurlHandler::METHOD_PUT);
return $this->authhelper->base->handleCurlRequest($registrationurl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $body,
]);
}
/**
* Function sign
*
* @param string $input
* @return string
*/
public function sign(string $input): string {
return $this->nodehelper->base->sign($input);
}
/**
* Function get_render_html
*
* @param string $url
* @return string
*/
public function get_render_html(string $url): string {
$curloptions = [
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_HEADER => 0,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'],
];
$result = $this->authhelper->base->handleCurlRequest($url, $curloptions);
if ($result->error !== 0) {
try {
return 'Unexpected Error';
} catch (Exception $exception) {
return $exception->getMessage();
}
}
return $result->content;
}
/**
* Function require_edu_login
*
* @param int|null $courseid
* @param bool $checkticket
* @param bool $checksessionkey
* @throws coding_exception
* @throws moodle_exception
* @throws require_login_exception
* @throws Exception
*/
public function require_edu_login(?int $courseid = null, bool $checkticket = true, bool $checksessionkey = true): void {
require_login($courseid);
$checksessionkey && require_sesskey();
$checkticket && $this->get_ticket();
}
/**
* Function get_preview_image
*
* @param Usage $usage
* @return CurlResult
*/
public function get_preview_image(Usage $usage): CurlResult {
return $this->nodehelper->getPreview($usage);
}
}