GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( b38aca...ed3c2e )
by Leonardo
06:08 queued 57s
created

AbstractContentParser::convertMarkdownLinks()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 51
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 30
nc 1
nop 1
dl 0
loc 51
rs 9.44
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQLAPI\GraphQLAPI\ContentProcessors;
6
7
use InvalidArgumentException;
8
use PoP\ComponentModel\Misc\RequestUtils;
9
use GraphQLAPI\GraphQLAPI\General\LocaleUtils;
10
use GraphQLAPI\GraphQLAPI\General\RequestParams;
11
12
abstract class AbstractContentParser implements ContentParserInterface
13
{
14
    /**
15
     * Parse the file's Markdown into HTML Content
16
     *
17
     * @param string $relativePathDir Dir relative to the /docs/${lang}/ folder
18
     * @throws InvalidArgumentException When the file is not found
19
     */
20
    public function getContent(
21
        string $filename,
22
        string $relativePathDir = '',
23
        array $options = []
24
    ): string {
25
        // Make sure the relative path ends with "/"
26
        if ($relativePathDir) {
27
            $relativePathDir = \trailingslashit($relativePathDir);
28
        }
29
        $localizeFile = \trailingslashit($this->getLocalizedFileDir()) . $relativePathDir . $filename;
30
        if (file_exists($localizeFile)) {
31
            // First check if the localized version exists
32
            $file = $localizeFile;
33
        } else {
34
            // Otherwise, use the default language version
35
            $file = \trailingslashit($this->getDefaultFileDir()) . $relativePathDir . $filename;
36
            // Make sure this file exists
37
            if (!file_exists($file)) {
38
                throw new InvalidArgumentException(sprintf(
39
                    \__('File \'%s\' does not exist', 'graphql-api'),
40
                    $file
41
                ));
42
            }
43
        }
44
        $fileContent = file_get_contents($file);
45
        if ($fileContent === false) {
46
            throw new InvalidArgumentException(sprintf(
47
                \__('File \'%s\' is corrupted', 'graphql-api'),
48
                $file
49
            ));
50
        }
51
        $htmlContent = $this->getHTMLContent($fileContent);
52
        $pathURL = \trailingslashit($this->getDefaultFileURL()). $relativePathDir;
53
        return $this->processHTMLContent($htmlContent, $pathURL, $options);
54
    }
55
56
    /**
57
     * Where the markdown file localized to the user's language is stored
58
     */
59
    public function getLocalizedFileDir(): string
60
    {
61
        return $this->getFileDir(LocaleUtils::getLocaleLanguage());
62
    }
63
64
    /**
65
     * Where the default markdown file (for if the localized language is not available) is stored
66
     * Default language for documentation: English
67
     */
68
    public function getDefaultFileDir(): string
69
    {
70
        return $this->getFileDir($this->getDefaultDocsLanguage());
71
    }
72
73
    /**
74
     * Default language for documentation: English
75
     */
76
    public function getDefaultDocsLanguage(): string
77
    {
78
        return 'en';
79
    }
80
81
    /**
82
     * Path where to find the local images
83
     */
84
    protected function getFileDir(string $lang): string
85
    {
86
        return constant('GRAPHQL_API_DIR') . "/docs/${lang}";
87
    }
88
89
    /**
90
     * Path URL to append to the local images referenced in the markdown file
91
     */
92
    protected function getDefaultFileURL(): string
93
    {
94
        $lang = $this->getDefaultDocsLanguage();
95
        return constant('GRAPHQL_API_URL') . "docs/${lang}";
96
    }
97
98
    /**
99
     * Process the HTML content:
100
     *
101
     * - Add the path to the images and anchors
102
     * - Add classes to HTML elements
103
     * - Append video embeds
104
     */
105
    abstract protected function getHTMLContent(string $fileContent): string;
106
107
    /**
108
     * Process the HTML content:
109
     *
110
     * - Add the path to the images and anchors
111
     * - Add classes to HTML elements
112
     * - Append video embeds
113
     *
114
     * @param array<string, mixed> $options
115
     */
116
    protected function processHTMLContent(string $htmlContent, string $pathURL, array $options = []): string
117
    {
118
        // Add default values for the options
119
        $options = array_merge(
120
            [
121
                ContentParserOptions::APPEND_PATH_URL_TO_IMAGES => true,
122
                ContentParserOptions::APPEND_PATH_URL_TO_ANCHORS => true,
123
                ContentParserOptions::SUPPORT_MARKDOWN_LINKS => true,
124
                ContentParserOptions::ADD_CLASSES => true,
125
                ContentParserOptions::EMBED_VIDEOS => true,
126
                ContentParserOptions::TAB_CONTENT => false,
127
            ],
128
            $options
129
        );
130
        // Add the path to the images
131
        if ($options[ContentParserOptions::APPEND_PATH_URL_TO_IMAGES]) {
132
            $htmlContent = $this->appendPathURLToImages($pathURL, $htmlContent);
133
        }
134
        // Convert Markdown links: execute before appending path to anchors
135
        if ($options[ContentParserOptions::SUPPORT_MARKDOWN_LINKS]) {
136
            $htmlContent = $this->convertMarkdownLinks($htmlContent);
137
        }
138
        // Add the path to the anchors
139
        if ($options[ContentParserOptions::APPEND_PATH_URL_TO_ANCHORS]) {
140
            $htmlContent = $this->appendPathURLToAnchors($pathURL, $htmlContent);
141
        }
142
        // Add classes to HTML elements
143
        if ($options[ContentParserOptions::ADD_CLASSES]) {
144
            $htmlContent = $this->addClasses($htmlContent);
145
        }
146
        // Append video embeds
147
        if ($options[ContentParserOptions::EMBED_VIDEOS]) {
148
            $htmlContent = $this->embedVideos($htmlContent);
149
        }
150
        // Convert the <h2> into tabs
151
        if ($options[ContentParserOptions::TAB_CONTENT]) {
152
            $htmlContent = $this->tabContent($htmlContent);
153
        }
154
        return $htmlContent;
155
    }
156
157
    /**
158
     * Add tabs to the content wherever there is an <h2>
159
     */
160
    protected function tabContent(string $htmlContent): string
161
    {
162
        $tag = 'h2';
163
        $firstTagPos = strpos($htmlContent, '<' . $tag . '>');
164
        // Check if there is any <h2>
165
        if ($firstTagPos !== false) {
166
            // Content before the first <h2> does not go within any tab
167
            $contentStarter = substr(
168
                $htmlContent,
169
                0,
170
                $firstTagPos
171
            );
172
            // Add the markup for the tabs around every <h2>
173
            $regex = sprintf(
174
                '/<%1$s>(.*?)<\/%1$s>/',
175
                $tag
176
            );
177
            $headers = [];
178
            $panelContent = preg_replace_callback(
179
                $regex,
180
                function ($matches) use (&$headers) {
181
                    $isFirstTab = empty($headers);
182
                    if (!$isFirstTab) {
183
                        $tabbedPanel = '</div>';
184
                    } else {
185
                        $tabbedPanel = '';
186
                    }
187
                    $headers[] = $matches[1];
188
                    return $tabbedPanel . sprintf(
189
                        '<div id="doc-panel-%s" class="tab-content" style="display: %s;">',
190
                        count($headers),
191
                        $isFirstTab ? 'block' : 'none'
192
                    );// . $matches[0];
193
                },
194
                substr(
195
                    $htmlContent,
196
                    $firstTagPos
197
                )
198
            ) . '</div>';
199
200
            // Create the tabs
201
            $panelTabs = '<h2 class="nav-tab-wrapper">';
202
            $headersCount = count($headers);
203
            for ($i = 0; $i < $headersCount; $i++) {
204
                $isFirstTab = $i == 0;
205
                $panelTabs .= sprintf(
206
                    '<a href="#doc-panel-%s" class="nav-tab %s">%s</a>',
207
                    $i + 1,
208
                    $isFirstTab ? 'nav-tab-active' : '',
209
                    $headers[$i]
210
                );
211
            }
212
            $panelTabs .= '</h2>';
213
214
            return
215
                $contentStarter
216
                . '<div class="graphql-api-tabpanel">'
217
                . $panelTabs
218
                . $panelContent
219
                . '</div>';
220
        }
221
        return $htmlContent;
222
    }
223
224
    /**
225
     * Is the anchor pointing to an URL?
226
     */
227
    protected function isAbsoluteURL(string $href): bool
228
    {
229
        return \str_starts_with($href, 'http://') || \str_starts_with($href, 'https://');
230
    }
231
232
    /**
233
     * Whenever a link points to a .md file, convert it
234
     * so it works also within the plugin
235
     */
236
    protected function convertMarkdownLinks(string $htmlContent): string
237
    {
238
        return (string)preg_replace_callback(
239
            '/<a.*href="(.*?)\.md".*?>/',
240
            function (array $matches): string {
241
                // If the element has an absolute route, then no need
242
                if ($this->isAbsoluteURL($matches[1])) {
243
                    return $matches[0];
244
                }
245
                // The URL is the current one, plus attr to open the .md file
246
                // in a modal window
247
                $elementURL = \add_query_arg(
248
                    [
249
                        RequestParams::TAB => RequestParams::TAB_DOCS,
250
                        RequestParams::DOC => $matches[1] . '.md',
251
                        'TB_iframe' => 'true',
252
                    ],
253
                    RequestUtils::getRequestedFullURL()
254
                );
255
                /** @var string */
256
                $link = str_replace(
257
                    "href=\"{$matches[1]}.md\"",
258
                    "href=\"{$elementURL}\"",
259
                    $matches[0]
260
                );
261
                // Must also add some classnames
262
                $classnames = 'thickbox open-plugin-details-modal';
263
                // 1. If there are classes already
264
                /** @var string */
265
                $replacedLink = preg_replace_callback(
266
                    '/ class="(.*?)"/',
267
                    function (array $matches) use ($classnames): string {
268
                        return str_replace(
269
                            " class=\"{$matches[1]}\"",
270
                            " class=\"{$matches[1]} {$classnames}\"",
271
                            $matches[0]
272
                        );
273
                    },
274
                    $link
275
                );
276
                // 2. If there were no classes
277
                if ($replacedLink == $link) {
278
                    $replacedLink = str_replace(
279
                        "<a ",
280
                        "<a class=\"{$classnames}\" ",
281
                        $link
282
                    );
283
                }
284
                return $replacedLink;
285
            },
286
            $htmlContent
287
        );
288
    }
289
290
    /**
291
     * Append video embeds. These are not already in the markdown file
292
     * because GitHub can't add `<iframe>`. Then, the source only contains
293
     * a link to the video. This must be identified, and transformed into
294
     * the embed.
295
     *
296
     * The match is produced when a link is pointing to a video in
297
     * Vimeo or Youtube by the end of the paragraph, with/out a final dot.
298
     */
299
    protected function embedVideos(string $htmlContent): string
300
    {
301
        // Identify videos from Vimeo/Youtube
302
        return (string)preg_replace_callback(
303
            '/<p>(.*?)<a href="https:\/\/(vimeo.com|youtube.com|youtu.be)\/(.*?)">(.*?)<\/a>\.?<\/p>/',
304
            function ($matches) {
305
                global $wp_embed;
306
                // Keep the link, and append the embed immediately after
307
                return
308
                    $matches[0]
309
                    . '<div class="video-responsive-container">' .
310
                        $wp_embed->autoembed(sprintf(
311
                            'https://%s/%s',
312
                            $matches[2],
313
                            $matches[3]
314
                        ))
315
                    . '</div>';
316
            },
317
            $htmlContent
318
        );
319
    }
320
321
    /**
322
     * Add classes to the HTML elements
323
     */
324
    protected function addClasses(string $htmlContent): string
325
    {
326
        /**
327
         * Add class "wp-list-table widefat" to all tables
328
         */
329
        return str_replace(
330
            '<table>',
331
            '<table class="wp-list-table widefat striped">',
332
            $htmlContent
333
        );
334
    }
335
336
    /**
337
     * Convert relative paths to absolute paths for image URLs
338
     *
339
     * @param string $pathURL
340
     * @param string $htmlContent
341
     * @return string
342
     */
343
    protected function appendPathURLToImages(string $pathURL, string $htmlContent): string
344
    {
345
        return $this->appendPathURLToElement('img', 'src', $pathURL, $htmlContent);
346
    }
347
348
    /**
349
     * Convert relative paths to absolute paths for image URLs
350
     *
351
     * @param string $pathURL
352
     * @param string $htmlContent
353
     * @return string
354
     */
355
    protected function appendPathURLToAnchors(string $pathURL, string $htmlContent): string
356
    {
357
        return $this->appendPathURLToElement('a', 'href', $pathURL, $htmlContent);
358
    }
359
360
    /**
361
     * Convert relative paths to absolute paths for elements
362
     *
363
     * @param string $tag
364
     * @param string $attr
365
     * @param string $pathURL
366
     * @param string $htmlContent
367
     * @return string
368
     */
369
    protected function appendPathURLToElement(string $tag, string $attr, string $pathURL, string $htmlContent): string
370
    {
371
        /**
372
         * $regex will become:
373
         * - /<img.*src="(.*?)".*?>/
374
         * - /<a.*href="(.*?)".*?>/
375
         */
376
        $regex = sprintf(
377
            '/<%s.*%s="(.*?)".*?>/',
378
            $tag,
379
            $attr
380
        );
381
        return (string)preg_replace_callback(
382
            $regex,
383
            function ($matches) use ($pathURL, $attr) {
384
                // If the element has an absolute route, then no need
385
                if ($this->isAbsoluteURL($matches[1])) {
386
                    return $matches[0];
387
                }
388
                $elementURL = \trailingslashit($pathURL) . $matches[1];
389
                return str_replace(
390
                    "{$attr}=\"{$matches[1]}\"",
391
                    "{$attr}=\"{$elementURL}\"",
392
                    $matches[0]
393
                );
394
            },
395
            $htmlContent
396
        );
397
    }
398
}
399