Passed
Push — master ( 480456...25de13 )
by Caen
05:04 queued 02:16
created

addDynamicHeaderContent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Hyde\Framework\Services;
4
5
use Hyde\Framework\Helpers\Features;
6
use Hyde\Framework\Models\Pages\DocumentationPage;
7
use Illuminate\Support\Facades\View;
8
use Illuminate\Support\Str;
9
10
/**
11
 * Class to make Hyde documentation pages smarter,
12
 * by dynamically enriching them with semantic HTML.
13
 *
14
 * @experimental 🧪 Subject to change without notice.
15
 *
16
 * @todo #445 Rename to HydeSemanticDocs
17
 *
18
 * @see \Hyde\Framework\Testing\Feature\Services\HydeSmartDocsTest
19
 */
20
class SemanticDocumentationArticle
21
{
22
    protected DocumentationPage $page;
23
    protected string $html;
24
25
    protected string $header;
26
    protected string $body;
27
    protected string $footer;
28
29
    public function __construct(DocumentationPage $page, string $html)
30
    {
31
        $this->page = $page;
32
        $this->html = $html;
33
    }
34
35
    public function renderHeader(): string
36
    {
37
        return $this->header;
38
    }
39
40
    public function renderBody(): string
41
    {
42
        return $this->body;
43
    }
44
45
    public function renderFooter(): string
46
    {
47
        return $this->footer;
48
    }
49
50
    /** @internal */
51
    public function process(): self
52
    {
53
        $this->tokenize();
54
55
        $this->addDynamicHeaderContent();
56
        $this->addDynamicFooterContent();
57
58
        return $this;
59
    }
60
61
    protected function tokenize(): static
62
    {
63
        // The HTML content is expected to be two parts. To create semantic HTML,
64
        // we need to split the content into header and body. We do this by
65
        // extracting the first <h1> tag and everything before it.
66
67
        // Split the HTML content by the first newline
68
        $parts = explode("\n", $this->html, 2);
69
70
        $this->header = $parts[0];
71
        $this->body = $parts[1] ?? '';
72
        $this->footer = '';
73
74
        return $this;
75
    }
76
77
    protected function addDynamicHeaderContent(): static
78
    {
79
        // Hook to add dynamic content to the header.
80
        // This is where we can add TOC, breadcrumbs, etc.
81
82
        if ($this->canRenderSourceLink('header')) {
83
            $this->header .= $this->renderSourceLink();
84
        }
85
86
        return $this;
87
    }
88
89
    protected function addDynamicFooterContent(): static
90
    {
91
        // Hook to add dynamic content to the footer.
92
        // This is where we can add copyright, attributions, info, etc.
93
94
        if (config('torchlight.attribution.enabled', true) && $this->hasTorchlight()) {
95
            $this->footer .= Str::markdown(config(
96
                'torchlight.attribution.markdown',
97
                'Syntax highlighted by torchlight.dev'
98
            ));
99
        }
100
101
        if ($this->canRenderSourceLink('footer')) {
102
            $this->footer .= $this->renderSourceLink();
103
        }
104
105
        return $this;
106
    }
107
108
    protected function renderSourceLink(): string
109
    {
110
        return View::make('hyde::components.docs.edit-source-button', [
0 ignored issues
show
Bug Best Practice introduced by
The expression return Illuminate\Suppor...getOnlineSourcePath())) returns the type Illuminate\Contracts\View\View which is incompatible with the type-hinted return string.
Loading history...
111
            'href' => $this->page->getOnlineSourcePath(),
112
        ]);
113
    }
114
115
    /**
116
     * Create a new SemanticDocumentationArticle instance, process, and return it.
117
     *
118
     * @param  \Hyde\Framework\Models\Pages\DocumentationPage  $page  The source page object
119
     * @param  string  $html  compiled HTML content
120
     * @return static new processed instance
121
     */
122
    public static function create(DocumentationPage $page, string $html): static
123
    {
124
        return (new self($page, $html))->process();
125
    }
126
127
    /**
128
     * Does the current document use Torchlight?
129
     *
130
     * @return bool
131
     */
132
    public function hasTorchlight(): bool
133
    {
134
        return Features::hasTorchlight() && str_contains($this->html, 'Syntax highlighted by torchlight.dev');
135
    }
136
137
    /**
138
     * Do we satisfy the requirements to render an edit source button in the supplied position?
139
     *
140
     * @param  string  $inPosition
141
     * @return bool
142
     */
143
    protected function canRenderSourceLink(string $inPosition): bool
144
    {
145
        $config = config('docs.edit_source_link_position', 'both');
146
        $positions = $config === 'both' ? ['header', 'footer'] : [$config];
147
148
        return ($this->page->getOnlineSourcePath() !== false) && in_array($inPosition, $positions);
149
    }
150
}
151