Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
14 / 14
CRAP
100.00% covered (success)
100.00%
1 / 1
Features
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
14 / 14
33
100.00% covered (success)
100.00%
1 / 1
 enabled
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 hasHtmlPages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasBladePages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasMarkdownPages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasMarkdownPosts
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasDocumentationPages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasDocumentationSearch
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 hasDarkmode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasThemeToggleButtons
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 hasTorchlight
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 htmlPages
n/a
0 / 0
n/a
0 / 0
1
 bladePages
n/a
0 / 0
n/a
0 / 0
1
 markdownPages
n/a
0 / 0
n/a
0 / 0
1
 markdownPosts
n/a
0 / 0
n/a
0 / 0
1
 documentationPages
n/a
0 / 0
n/a
0 / 0
1
 documentationSearch
n/a
0 / 0
n/a
0 / 0
1
 darkmode
n/a
0 / 0
n/a
0 / 0
1
 torchlight
n/a
0 / 0
n/a
0 / 0
1
 sitemap
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 rss
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 toArray
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultOptions
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Hyde\Facades;
6
7use Hyde\Hyde;
8use Hyde\Enums\Feature;
9use Hyde\Pages\MarkdownPost;
10use Hyde\Pages\DocumentationPage;
11use JetBrains\PhpStorm\Deprecated;
12use Hyde\Support\Concerns\Serializable;
13use Hyde\Support\Contracts\SerializableContract;
14use Hyde\Framework\Concerns\Internal\MockableFeatures;
15use Illuminate\Support\Str;
16
17use function get_class_methods;
18use function extension_loaded;
19use function str_starts_with;
20use function in_array;
21use function collect;
22use function substr;
23use function count;
24use function app;
25
26/**
27 * Allows features to be enabled and disabled in a simple object-oriented manner.
28 *
29 * @internal Until this class is split into a service/manager class, it should not be used outside of Hyde as the API is subject to change.
30 *
31 * @todo Split facade logic to service/manager class. (Initial and mock data could be set with boot/set methods)
32 * Based entirely on Laravel Jetstream (License MIT)
33 *
34 * @see https://jetstream.laravel.com/
35 */
36class Features implements SerializableContract
37{
38    use Serializable;
39    use MockableFeatures;
40
41    /**
42     * Determine if the given specified is enabled.
43     */
44    public static function enabled(Feature $feature): bool
45    {
46        return static::resolveMockedInstance($feature->name) ?? in_array(
47            $feature, Config::getArray('hyde.features', static::getDefaultOptions())
48        );
49    }
50
51    // ================================================
52    // Determine if a given feature is enabled.
53    // ================================================
54
55    public static function hasHtmlPages(): bool
56    {
57        return static::enabled(Feature::HtmlPages);
58    }
59
60    public static function hasBladePages(): bool
61    {
62        return static::enabled(Feature::BladePages);
63    }
64
65    public static function hasMarkdownPages(): bool
66    {
67        return static::enabled(Feature::MarkdownPages);
68    }
69
70    public static function hasMarkdownPosts(): bool
71    {
72        return static::enabled(Feature::MarkdownPosts);
73    }
74
75    public static function hasDocumentationPages(): bool
76    {
77        return static::enabled(Feature::DocumentationPages);
78    }
79
80    public static function hasDocumentationSearch(): bool
81    {
82        return static::enabled(Feature::DocumentationSearch)
83            && static::hasDocumentationPages()
84            && count(DocumentationPage::files()) > 0;
85    }
86
87    public static function hasDarkmode(): bool
88    {
89        return static::enabled(Feature::Darkmode);
90    }
91
92    public static function hasThemeToggleButtons(): bool
93    {
94        return static::hasDarkmode() && Config::getBool('hyde.theme_toggle_buttons', true);
95    }
96
97    /**
98     * Torchlight is by default enabled automatically when an API token
99     * is set in the .env file but is disabled when running tests.
100     */
101    public static function hasTorchlight(): bool
102    {
103        return static::enabled(Feature::Torchlight)
104            && (Config::getNullableString('torchlight.token') !== null)
105            && (app('env') !== 'testing');
106    }
107
108    // =================================================
109    // Configure features to be used in the config file.
110    // =================================================
111
112    /**
113     * @codeCoverageIgnore Deprecated method.
114     *
115     * @deprecated This method will be removed in v2.0. Please use `Feature::HtmlPages` instead.
116     */
117    #[Deprecated(reason: 'Replaced by the \Hyde\Enums\Feature::HtmlPages Enum case', replacement: 'Feature::HtmlPages', since: '1.6.0')]
118    public static function htmlPages(): Feature
119    {
120        return Feature::HtmlPages;
121    }
122
123    /**
124     * @codeCoverageIgnore Deprecated method.
125     *
126     * @deprecated This method will be removed in v2.0. Please use `Feature::BladePages` instead.
127     */
128    #[Deprecated(reason: 'Replaced by the \Hyde\Enums\Feature::BladePages Enum case', replacement: 'Feature::BladePages', since: '1.6.0')]
129    public static function bladePages(): Feature
130    {
131        return Feature::BladePages;
132    }
133
134    /**
135     * @codeCoverageIgnore Deprecated method.
136     *
137     * @deprecated This method will be removed in v2.0. Please use `Feature::MarkdownPages` instead.
138     */
139    #[Deprecated(reason: 'Replaced by the \Hyde\Enums\Feature::MarkdownPages Enum case', replacement: 'Feature::MarkdownPages', since: '1.6.0')]
140    public static function markdownPages(): Feature
141    {
142        return Feature::MarkdownPages;
143    }
144
145    /**
146     * @codeCoverageIgnore Deprecated method.
147     *
148     * @deprecated This method will be removed in v2.0. Please use `Feature::MarkdownPosts` instead.
149     */
150    #[Deprecated(reason: 'Replaced by the \Hyde\Enums\Feature::MarkdownPosts Enum case', replacement: 'Feature::MarkdownPosts', since: '1.6.0')]
151    public static function markdownPosts(): Feature
152    {
153        return Feature::MarkdownPosts;
154    }
155
156    /**
157     * @codeCoverageIgnore Deprecated method.
158     *
159     * @deprecated This method will be removed in v2.0. Please use `Feature::DocumentationPages` instead.
160     */
161    #[Deprecated(reason: 'Replaced by the \Hyde\Enums\Feature::DocumentationPages Enum case', replacement: 'Feature::DocumentationPages', since: '1.6.0')]
162    public static function documentationPages(): Feature
163    {
164        return Feature::DocumentationPages;
165    }
166
167    /**
168     * @codeCoverageIgnore Deprecated method.
169     *
170     * @deprecated This method will be removed in v2.0. Please use `Feature::DocumentationSearch` instead.
171     */
172    #[Deprecated(reason: 'Replaced by the \Hyde\Enums\Feature::DocumentationSearch Enum case', replacement: 'Feature::DocumentationSearch', since: '1.6.0')]
173    public static function documentationSearch(): Feature
174    {
175        return Feature::DocumentationSearch;
176    }
177
178    /**
179     * @codeCoverageIgnore Deprecated method.
180     *
181     * @deprecated This method will be removed in v2.0. Please use `Feature::Darkmode` instead.
182     */
183    #[Deprecated(reason: 'Replaced by the \Hyde\Enums\Feature::Darkmode Enum case', replacement: 'Feature::Darkmode', since: '1.6.0')]
184    public static function darkmode(): Feature
185    {
186        return Feature::Darkmode;
187    }
188
189    /**
190     * @codeCoverageIgnore Deprecated method.
191     *
192     * @deprecated This method will be removed in v2.0. Please use `Feature::Torchlight` instead.
193     */
194    #[Deprecated(reason: 'Replaced by the \Hyde\Enums\Feature::Torchlight Enum case', replacement: 'Feature::Torchlight', since: '1.6.0')]
195    public static function torchlight(): Feature
196    {
197        return Feature::Torchlight;
198    }
199
200    // ====================================================
201    // Dynamic features that in addition to being enabled
202    // in the config file, require preconditions to be met.
203    // ====================================================
204
205    /** Can a sitemap be generated? */
206    public static function sitemap(): bool
207    {
208        return static::resolveMockedInstance('sitemap') ?? Hyde::hasSiteUrl()
209            && Config::getBool('hyde.generate_sitemap', true)
210            && extension_loaded('simplexml');
211    }
212
213    /** Can an RSS feed be generated? */
214    public static function rss(): bool
215    {
216        return static::resolveMockedInstance('rss') ?? Hyde::hasSiteUrl()
217            && static::hasMarkdownPosts()
218            && Config::getBool('hyde.rss.enabled', true)
219            && extension_loaded('simplexml')
220            && count(MarkdownPost::files()) > 0;
221    }
222
223    /**
224     * Get an array representation of the features and their status.
225     *
226     * @return array<string, bool>
227     *
228     * @example ['html-pages' => true, 'markdown-pages' => false, ...]
229     */
230    public function toArray(): array
231    {
232        return collect(get_class_methods(static::class))
233            ->filter(fn (string $method): bool => str_starts_with($method, 'has'))
234            ->mapWithKeys(fn (string $method): array => [
235                Str::kebab(substr($method, 3)) => static::{$method}(),
236            ])->toArray();
237    }
238
239    protected static function getDefaultOptions(): array
240    {
241        return [
242            // Page Modules
243            Feature::HtmlPages,
244            Feature::MarkdownPosts,
245            Feature::BladePages,
246            Feature::MarkdownPages,
247            Feature::DocumentationPages,
248
249            // Frontend Features
250            Feature::Darkmode,
251            Feature::DocumentationSearch,
252
253            // Integrations
254            Feature::Torchlight,
255        ];
256    }
257}