Passed
Pull Request — 6 (#1291)
by Arnaud
08:00 queued 11s
created

Page::getFrontmatter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php
2
/**
3
 * This file is part of the Cecil/Cecil package.
4
 *
5
 * Copyright (c) Arnaud Ligny <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Cecil\Collection\Page;
14
15
use Cecil\Collection\Item;
16
use Cecil\Exception\RuntimeException;
17
use Cecil\Util;
18
use Cocur\Slugify\Slugify;
19
use Symfony\Component\Finder\SplFileInfo;
20
21
/**
22
 * Class Page.
23
 */
24
class Page extends Item
25
{
26
    const SLUGIFY_PATTERN = '/(^\/|[^._a-z0-9\/]|-)+/'; // should be '/^\/|[^_a-z0-9\/]+/'
27
28
    /** @var bool True if page is not created from a Markdown file. */
29
    protected $virtual;
30
31
    /** @var SplFileInfo */
32
    protected $file;
33
34
    /** @var string Homepage, Page, Section, etc. */
35
    protected $type;
36
37
    /** @var string */
38
    protected $folder;
39
40
    /** @var string */
41
    protected $slug;
42
43
    /** @var string folder + slug. */
44
    protected $path;
45
46
    /** @var string */
47
    protected $section;
48
49
    /** @var string */
50
    protected $frontmatter;
51
52
    /** @var string Body before conversion. */
53
    protected $body;
54
55
    /** @var array Front matter before conversion. */
56
    protected $fmVariables = [];
57
58
    /** @var string Body after Markdown conversion. */
59
    protected $html;
60
61
    /** @var Slugify */
62
    private static $slugifier;
63 1
64
    public function __construct(string $id)
65 1
    {
66 1
        parent::__construct($id);
67 1
        $this->setVirtual(true);
68
        $this->setType(Type::PAGE);
69 1
        // default variables
70 1
        $this->setVariables([
71 1
            'title'            => 'Page Title',
72 1
            'date'             => new \DateTime(),
73
            'updated'          => new \DateTime(),
74
            'weight'           => null,
75
            'filepath'         => null,
76 1
            'published'        => true,
77
            'content_template' => 'page.content.twig',
78 1
        ]);
79
    }
80
81
    /**
82
     * Turns a path (string) into a slug (URI).
83 1
     */
84
    public static function slugify(string $path): string
85 1
    {
86 1
        if (!self::$slugifier instanceof Slugify) {
87
            self::$slugifier = Slugify::create(['regexp' => self::SLUGIFY_PATTERN]);
88
        }
89 1
90
        return self::$slugifier->slugify($path);
91
    }
92
93
    /**
94
     * Creates the ID from the file path.
95 1
     */
96
    public static function createId(SplFileInfo $file): string
97 1
    {
98 1
        $relativepath = self::slugify(str_replace(DIRECTORY_SEPARATOR, '/', $file->getRelativePath()));
99
        $basename = self::slugify(PrefixSuffix::subPrefix($file->getBasename('.'.$file->getExtension())));
100 1
        // case of "README" -> index
101
        $basename = (string) str_ireplace('readme', 'index', $basename);
102 1
        // case of section's index: "section/index" -> "section"
103
        if (!empty($relativepath) && PrefixSuffix::sub($basename) == 'index') {
104 1
            // case of a localized section
105
            if (PrefixSuffix::hasSuffix($basename)) {
106
                return $relativepath.'.'.PrefixSuffix::getSuffix($basename);
107
            }
108 1
109
            return $relativepath;
110
        }
111 1
112
        return trim(Util::joinPath($relativepath, $basename), '/');
113
    }
114
115
    /**
116
     * Returns the ID of a page without language suffix.
117 1
     */
118
    public function getIdWithoutLang(): string
119 1
    {
120
        return PrefixSuffix::sub($this->getId());
121
    }
122
123
    /**
124
     * Set file.
125 1
     */
126
    public function setFile(SplFileInfo $file): self
127 1
    {
128 1
        $this->setVirtual(false);
129
        $this->file = $file;
130
131
        /*
132
         * File path components
133 1
         */
134 1
        $fileRelativePath = str_replace(DIRECTORY_SEPARATOR, '/', $this->file->getRelativePath());
135 1
        $fileExtension = $this->file->getExtension();
136
        $fileName = $this->file->getBasename('.'.$fileExtension);
137 1
        // case of "README" -> "index"
138
        $fileName = (string) str_ireplace('readme', 'index', $fileName);
139 1
        // case of "index" = home page
140 1
        if (empty($this->file->getRelativePath()) && PrefixSuffix::sub($fileName) == 'index') {
141
            $this->setType(Type::HOMEPAGE);
142
        }
143
        /*
144
         * Set protected variables
145 1
         */
146 1
        $this->setFolder($fileRelativePath); // ie: "blog"
147 1
        $this->setSlug($fileName); // ie: "post-1"
148
        $this->setPath($this->getFolder().'/'.$this->getSlug()); // ie: "blog/post-1"
149
        /*
150
         * Set default variables
151 1
         */
152 1
        $this->setVariables([
153 1
            'title'    => PrefixSuffix::sub($fileName),
154 1
            'date'     => (new \DateTime())->setTimestamp($this->file->getCTime()),
155 1
            'updated'  => (new \DateTime())->setTimestamp($this->file->getMTime()),
156
            'filepath' => $this->file->getRelativePathname(),
157
        ]);
158
        /*
159
         * Set specific variables
160
         */
161 1
        // is file has a prefix?
162 1
        if (PrefixSuffix::hasPrefix($fileName)) {
163 1
            $prefix = PrefixSuffix::getPrefix($fileName);
164
            if ($prefix !== null) {
165 1
                // prefix is a valid date?
166 1
                if (Util\Date::isDateValid($prefix)) {
167
                    $this->setVariable('date', (string) $prefix);
168
                } else {
169 1
                    // prefix is an integer: used for sorting
170
                    $this->setVariable('weight', (int) $prefix);
171
                }
172
            }
173
        }
174 1
        // is file has a language suffix?
175 1
        if (PrefixSuffix::hasSuffix($fileName)) {
176
            $this->setVariable('language', PrefixSuffix::getSuffix($fileName));
177
        }
178 1
        // set reference between translations
179
        $this->setVariable('langref', $this->getPath());
180 1
181
        return $this;
182
    }
183
184
    /**
185
     * Returns file real path.
186 1
     */
187
    public function getFilePath(): ?string
188 1
    {
189 1
        return $this->file->getRealPath() === false ? null : $this->file->getRealPath();
190 1
    }
191 1
192
    /**
193 1
     * Parse file content.
194
     */
195
    public function parse(): self
196
    {
197
        $parser = new Parser($this->file);
198
        $parsed = $parser->parse();
199 1
        $this->frontmatter = $parsed->getFrontmatter();
200
        $this->body = $parsed->getBody();
201 1
202
        return $this;
203
    }
204
205
    /**
206
     * Get frontmatter.
207 1
     */
208
    public function getFrontmatter(): ?string
209 1
    {
210
        return $this->frontmatter;
211
    }
212
213
    /**
214
     * Get body as raw.
215 1
     */
216
    public function getBody(): ?string
217 1
    {
218
        return $this->body;
219 1
    }
220
221
    /**
222
     * Set virtual status.
223
     */
224
    public function setVirtual(bool $virtual): self
225 1
    {
226
        $this->virtual = $virtual;
227 1
228
        return $this;
229
    }
230
231
    /**
232
     * Is current page is virtual?
233 1
     */
234
    public function isVirtual(): bool
235 1
    {
236
        return $this->virtual;
237 1
    }
238
239
    /**
240
     * Set page type.
241
     */
242
    public function setType(string $type): self
243 1
    {
244
        $this->type = new Type($type);
245 1
246
        return $this;
247
    }
248
249
    /**
250
     * Get page type.
251 1
     */
252
    public function getType(): string
253 1
    {
254
        return (string) $this->type;
255 1
    }
256
257
    /**
258
     * Set path without slug.
259
     */
260
    public function setFolder(string $folder): self
261 1
    {
262
        $this->folder = self::slugify($folder);
263 1
264
        return $this;
265
    }
266
267
    /**
268
     * Get path without slug.
269 1
     */
270
    public function getFolder(): ?string
271 1
    {
272 1
        return $this->folder;
273
    }
274
275 1
    /**
276 1
     * Set slug.
277
     */
278 1
    public function setSlug(string $slug): self
279
    {
280 1
        if (!$this->slug) {
281
            $slug = self::slugify(PrefixSuffix::sub($slug));
282
        }
283
        // force slug and update path
284
        if ($this->slug && $this->slug != $slug) {
285
            $this->setPath($this->getFolder().'/'.$slug);
286 1
        }
287
        $this->slug = $slug;
288 1
289
        return $this;
290
    }
291
292
    /**
293
     * Get slug.
294 1
     */
295
    public function getSlug(): string
296 1
    {
297
        return $this->slug;
298
    }
299 1
300 1
    /**
301
     * Set path.
302 1
     */
303
    public function setPath(string $path): self
304
    {
305
        $path = self::slugify(PrefixSuffix::sub($path));
306 1
307 1
        // case of homepage
308
        if ($path == 'index') {
309 1
            $this->path = '';
310
311
            return $this;
312 1
        }
313 1
314 1
        // case of custom sections' index (ie: content/section/index.md)
315
        if (substr($path, -6) == '/index') {
316 1
            $path = substr($path, 0, strlen($path) - 6);
317
        }
318
        $this->path = $path;
319 1
320 1
        // case of root pages
321
        $lastslash = strrpos($this->path, '/');
322 1
        if ($lastslash === false) {
323 1
            $this->slug = $this->path;
324
325 1
            return $this;
326
        }
327
328
        if (!$this->virtual && $this->getSection() === null) {
329
            $this->section = explode('/', $this->path)[0];
330
        }
331 1
        $this->folder = substr($this->path, 0, $lastslash);
332
        $this->slug = substr($this->path, -(strlen($this->path) - $lastslash - 1));
333 1
334
        return $this;
335
    }
336
337
    /**
338
     * Get path.
339
     */
340
    public function getPath(): ?string
341
    {
342
        return $this->path;
343
    }
344
345
    /**
346
     * @see getPath()
347 1
     */
348
    public function getPathname(): ?string
349 1
    {
350
        return $this->getPath();
351 1
    }
352
353
    /**
354
     * Set section.
355
     */
356
    public function setSection(string $section): self
357 1
    {
358
        $this->section = $section;
359 1
360
        return $this;
361
    }
362
363
    /**
364
     * Get section.
365 1
     */
366
    public function getSection(): ?string
367 1
    {
368
        return !empty($this->section) ? $this->section : null;
369 1
    }
370
371
    /**
372
     * Set body as HTML.
373
     */
374
    public function setBodyHtml(string $html): self
375 1
    {
376
        $this->html = $html;
377 1
378
        return $this;
379
    }
380
381
    /**
382
     * Get body as HTML.
383 1
     */
384
    public function getBodyHtml(): ?string
385 1
    {
386
        return $this->html;
387
    }
388
389
    /**
390
     * @see getBodyHtml()
391
     */
392
    public function getContent(): ?string
393
    {
394
        return $this->getBodyHtml();
395
    }
396
397 1
    /*
398
     * Helpers to set and get variables.
399 1
     */
400 1
401
    /**
402
     * Set an array as variables.
403 1
     *
404
     * @throws RuntimeException
405
     */
406
    public function setVariables(array $variables): self
407
    {
408
        foreach ($variables as $key => $value) {
409 1
            $this->setVariable($key, $value);
410
        }
411 1
412
        return $this;
413
    }
414
415
    /**
416
     * Get all variables.
417
     */
418
    public function getVariables(): array
419
    {
420
        return $this->properties;
421
    }
422 1
423
    /**
424 1
     * Set a variable.
425 1
     *
426
     * @param string $name
427 1
     * @param mixed  $value
428 1
     *
429
     * @throws RuntimeException
430 1
     */
431
    public function setVariable(string $name, $value): self
432
    {
433
        if (is_bool($value)) {
434
            $value = $value ?: 0;
435
        }
436
        switch ($name) {
437
            case 'date':
438 1
                try {
439 1
                    $date = Util\Date::dateToDatetime($value);
440 1
                } catch (\Exception $e) {
441 1
                    throw new RuntimeException(\sprintf('Expected date format (ie: "2012-10-08") for "date" in "%s" instead of "%s"', $this->getId(), (string) $value));
442 1
                }
443
                $this->offsetSet('date', $date);
444 1
                break;
445 1
            case 'draft':
446 1
                if ($value === true) {
447 1
                    $this->offsetSet('published', false);
448 1
                }
449
                break;
450
            case 'path':
451
            case 'slug':
452
                $slugify = self::slugify((string) $value);
453
                if ($value != $slugify) {
454
                    throw new RuntimeException(\sprintf('"%s" variable should be "%s" (not "%s") in "%s"', $name, $slugify, (string) $value, $this->getId()));
455
                }
456
                /** @see setPath() */
457
                /** @see setSlug() */
458
                $method = 'set'.\ucfirst($name);
459 1
                $this->$method($value);
460 1
                break;
461 1
            default:
462
                $this->offsetSet($name, $value);
463 1
        }
464
465
        return $this;
466 1
    }
467
468
    /**
469
     * Is variable exists?
470
     */
471
    public function hasVariable(string $name): bool
472 1
    {
473
        return $this->offsetExists($name);
474 1
    }
475
476
    /**
477
     * Get a variable.
478
     *
479
     * @return mixed|null
480
     */
481
    public function getVariable(string $name)
482 1
    {
483
        if ($this->offsetExists($name)) {
484 1
            return $this->offsetGet($name);
485 1
        }
486
    }
487 1
488
    /**
489
     * Unset a variable.
490
     */
491
    public function unVariable(string $name): self
492 1
    {
493
        if ($this->offsetExists($name)) {
494 1
            $this->offsetUnset($name);
495 1
        }
496
497
        return $this;
498 1
    }
499
500
    /**
501
     * Set front matter (only) variables.
502
     */
503
    public function setFmVariables(array $variables): self
504 1
    {
505
        $this->fmVariables = $variables;
506 1
507
        return $this;
508 1
    }
509
510
    /**
511
     * Get front matter variables.
512
     */
513
    public function getFmVariables(): array
514 1
    {
515
        return $this->fmVariables;
516 1
    }
517
}
518