<commit_before><?php

namespace Podlove\Modules\Shownotes;

use Podlove\Http\Curl;
use Podlove\Model\Episode;
use Podlove\Modules\Shownotes\Model\Entry;

class REST_API
{
    const api_namespace = 'podlove/v1';
    const api_base = 'shownotes';

    public function register_routes()
    {
        register_rest_route(self::api_namespace, self::api_base, [
            [
                'methods' => \WP_REST_Server::READABLE,
                'callback' => [$this, 'get_items'],
                'permission_callback' => [$this, 'permission_check'],
                'args' => [
                    'episode_id' => [
                        'description' => 'Limit result set by episode.',
                        'type' => 'integer',
                    ],
                ],
            ],
            [
                'methods' => \WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
        register_rest_route(self::api_namespace, self::api_base.'/(?P<id>[\d]+)', [
            'args' => [
                'id' => [
                    'description' => __('Unique identifier for the object.'),
                    'type' => 'integer',
                ],
            ],
            [
                'methods' => \WP_REST_Server::READABLE,
                'callback' => [$this, 'get_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
            [
                'methods' => \WP_REST_Server::DELETABLE,
                'callback' => [$this, 'delete_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
            [
                'methods' => \WP_REST_Server::EDITABLE,
                'callback' => [$this, 'update_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
        register_rest_route(self::api_namespace, self::api_base.'/(?P<id>[\d]+)/unfurl', [
            'args' => [
                'id' => [
                    'description' => __('Unique identifier for the object.'),
                    'type' => 'integer',
                ],
            ],
            [
                'methods' => \WP_REST_Server::EDITABLE,
                'callback' => [$this, 'unfurl_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
        register_rest_route(self::api_namespace, self::api_base.'/osf', [
            [
                'methods' => \WP_REST_Server::CREATABLE,
                'callback' => [$this, 'import_osf'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
        register_rest_route(self::api_namespace, self::api_base.'/html', [
            [
                'methods' => \WP_REST_Server::CREATABLE,
                'callback' => [$this, 'import_html'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
    }

    public function import_html($request)
    {
        $post_id = $request['post_id'];

        if (!$episode = \Podlove\Model\Episode::find_or_create_by_post_id($post_id)) {
            return new \WP_Error(
                'podlove_rest_html_no_episode',
                'episode cannot be found',
                ['status' => 400]
            );
        }

        $html = $request['html'] ?? get_the_content(null, false, $post_id);

        $dom = new \DOMDocument('1.0');
        $dom->preserveWhiteSpace = false;
        $valid = $dom->loadHTML($html);

        if (!$valid) {
            return new \WP_Error(
                'podlove_rest_html_unreadable',
                'html could not be parsed',
                ['status' => 400]
            );
        }

        $xpath = new \DOMXPath($dom);

        foreach ($xpath->query('//a | //h1 | //h2 | //h3 | //h4 | //h5 | //h6') as $element) {
            if ($element->tagName == 'a') {
                $request = new \WP_REST_Request('POST', '/podlove/v1/shownotes');
                $request->set_query_params([
                    'episode_id' => $episode->id,
                    'original_url' => $element->getAttribute('href'),
                    'data' => [
                        'title' => $element->textContent,
                    ],
                    'type' => 'link',
                ]);
                rest_do_request($request);
            } else {
                $request = new \WP_REST_Request('POST', '/podlove/v1/shownotes');<commit_msg>fix(shownotes): encoding issue when importing from HTML<commit_after><?php

namespace Podlove\Modules\Shownotes;

use Podlove\Http\Curl;
use Podlove\Model\Episode;
use Podlove\Modules\Shownotes\Model\Entry;

class REST_API
{
    const api_namespace = 'podlove/v1';
    const api_base = 'shownotes';

    public function register_routes()
    {
        register_rest_route(self::api_namespace, self::api_base, [
            [
                'methods' => \WP_REST_Server::READABLE,
                'callback' => [$this, 'get_items'],
                'permission_callback' => [$this, 'permission_check'],
                'args' => [
                    'episode_id' => [
                        'description' => 'Limit result set by episode.',
                        'type' => 'integer',
                    ],
                ],
            ],
            [
                'methods' => \WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
        register_rest_route(self::api_namespace, self::api_base.'/(?P<id>[\d]+)', [
            'args' => [
                'id' => [
                    'description' => __('Unique identifier for the object.'),
                    'type' => 'integer',
                ],
            ],
            [
                'methods' => \WP_REST_Server::READABLE,
                'callback' => [$this, 'get_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
            [
                'methods' => \WP_REST_Server::DELETABLE,
                'callback' => [$this, 'delete_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
            [
                'methods' => \WP_REST_Server::EDITABLE,
                'callback' => [$this, 'update_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
        register_rest_route(self::api_namespace, self::api_base.'/(?P<id>[\d]+)/unfurl', [
            'args' => [
                'id' => [
                    'description' => __('Unique identifier for the object.'),
                    'type' => 'integer',
                ],
            ],
            [
                'methods' => \WP_REST_Server::EDITABLE,
                'callback' => [$this, 'unfurl_item'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
        register_rest_route(self::api_namespace, self::api_base.'/osf', [
            [
                'methods' => \WP_REST_Server::CREATABLE,
                'callback' => [$this, 'import_osf'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
        register_rest_route(self::api_namespace, self::api_base.'/html', [
            [
                'methods' => \WP_REST_Server::CREATABLE,
                'callback' => [$this, 'import_html'],
                'permission_callback' => [$this, 'permission_check'],
            ],
        ]);
    }

    public function import_html($request)
    {
        $post_id = $request['post_id'];

        if (!$episode = \Podlove\Model\Episode::find_or_create_by_post_id($post_id)) {
            return new \WP_Error(
                'podlove_rest_html_no_episode',
                'episode cannot be found',
                ['status' => 400]
            );
        }

        $html = $request['html'] ?? get_the_content(null, false, $post_id);

        $dom = new \DOMDocument('1.0');
        $dom->preserveWhiteSpace = false;

        // load html and ensure utf-8
        // @see php DOMDocument::loadHTML doc comments
        $valid = $dom->loadHTML('<?xml encoding="UTF-8">'.$html);

        foreach ($dom->childNodes as $item) {
            if ($item->nodeType == XML_PI_NODE) {
                $dom->removeChild($item);
            }
        }

        $dom->encoding = 'UTF-8';

        if (!$valid) {
            return new \WP_Error(
                'podlove_rest_html_unreadable',
                'html could not be parsed',
                ['status' => 400]
            );
        }

        $xpath = new \DOMXPath($dom);

        foreach ($xpath->query('//a | //h1 | //h2 | //h3 | //h4 | //h5 | //h6') as $element) {
            if ($element->tagName == 'a') {
                $request = new \WP_REST_Request('POST', '/podlove/v1/shownotes');
                $request->set_query_params([
                    'episode_id' => $episode->id,
                    'original_url' => $element->getAttribute('href'),
                    'data' => [
                        'title' => $element->textContent,
                    ],
                    'type' => 'link',
                ]);
                rest_do_request($request);
            } else {
                $request = new \WP_REST_Request('POST', '/podlove/v1/shownotes');
{'fraction_non_alphanumeric': 0.15558594571849357, 'fraction_numerical': 0.0038922785609088995, 'mean_word_length': 1.4725617685305592, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 114, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 1}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '953924', 'n_tokens_mistral': 2803, 'n_tokens_neox': 2620, 'n_words': 434}