Proyectos de Subversion Moodle

Rev

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);
    }
}