Completed
Pull Request — master (#18)
by Vladimir
02:19
created

PageManager::compileFromFilePath()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 11
nc 3
nop 2
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
1
<?php
2
3
namespace allejo\stakx\Manager;
4
5
use allejo\stakx\Exception\TrackedItemNotFoundException;
6
use allejo\stakx\Object\ContentItem;
7
use allejo\stakx\Object\PageView;
8
use allejo\stakx\System\FileExplorer;
9
use allejo\stakx\System\Folder;
10
use Twig_Error_Syntax;
11
use Twig_Template;
12
13
/**
14
 * This class is responsible for handling all of the PageViews within a website.
15
 *
16
 * PageManager will parse all available dynamic and static PageViews. After, dynamic PageViews will be prepared by
17
 * setting the appropriate values for each ContentItem such as permalinks. Lastly, this class will compile all of the
18
 * PageViews and write them to the target directory.
19
 *
20
 * @package allejo\stakx\Manager
21
 */
22
class PageManager extends TrackingManager
23
{
24
    /**
25
     * The relative (to the stakx project) file path to the redirect template
26
     *
27
     * @var string|bool
28
     */
29
    private $redirectTemplate;
30
31
    /**
32
     * @var ContentItem[][]
33
     */
34
    private $collections;
35
36
    /**
37
     * @var Folder
38
     */
39
    private $targetDir;
40
41
    private $siteMenu;
42
43
    /**
44
     * @var \Twig_Environment
45
     */
46
    private $twig;
47
48
    /**
49
     * PageManager constructor
50
     */
51
    public function __construct()
52
    {
53
        parent::__construct();
54
55
        $this->siteMenu = array();
56
    }
57
58
    public function setCollections (&$collections)
59
    {
60
        if (empty($collections)) { return; }
61
62
        $this->collections = &$collections;
63
    }
64
65
    public function setRedirectTemplate ($filePath)
66
    {
67
        $this->redirectTemplate = $filePath;
68
    }
69
70
    /**
71
     * @param Folder $folder The relative target directory as specified from the configuration file
72
     */
73
    public function setTargetFolder (&$folder)
74
    {
75
        $this->targetDir = &$folder;
76
    }
77
78
    public function configureTwig ($configuration, $options)
79
    {
80
        $twig = new TwigManager();
81
        $twig->configureTwig($configuration, $options);
82
83
        $this->twig = TwigManager::getInstance();
84
    }
85
86
    /**
87
     * An array representing the website's menu structure with children and grandchildren made from static PageViews
88
     *
89
     * @return array
90
     */
91
    public function getSiteMenu ()
92
    {
93
        return $this->siteMenu;
94
    }
95
96
    /**
97
     * Go through all of the PageView directories and create a respective PageView for each and classify them as a
98
     * dynamic or static PageView.
99
     *
100
     * @param $pageViewFolders
101
     */
102
    public function parsePageViews ($pageViewFolders)
103
    {
104
        if (empty($pageViewFolders)) { return; }
105
106
        /**
107
         * The name of the folder where PageViews are located
108
         *
109
         * @var $pageViewFolder string
110
         */
111
        foreach ($pageViewFolders as $pageViewFolderName)
112
        {
113
            $pageViewFolder = $this->fs->absolutePath($pageViewFolderName);
114
115
            if (!$this->fs->exists($pageViewFolder))
116
            {
117
                continue;
118
            }
119
120
            // @TODO Replace this with a regular expression or have wildcard support
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
121
            $this->scanTrackableItems($pageViewFolder, array(
122
                'refresh' => false,
123
                'fileExplorer' => FileExplorer::INCLUDE_ONLY_FILES
124
            ), array('.html', '.twig'));
125
            $this->saveFolderDefinition($pageViewFolderName);
126
        }
127
    }
128
129
    /**
130
     * Compile dynamic and static PageViews
131
     */
132
    public function compileAll ()
133
    {
134
        foreach (array_keys($this->trackedItemsFlattened) as $filePath)
135
        {
136
            $this->compileFromFilePath($filePath);
137
        }
138
    }
139
140
    public function compileSome ($filter = array())
141
    {
142
        /** @var PageView $pageView */
143
        foreach ($this->trackedItemsFlattened as $pageView)
144
        {
145
            if ($pageView->hasTwigDependency($filter['namespace'], $filter['dependency']))
146
            {
147
                $this->compilePageView($pageView);
148
            }
149
        }
150
    }
151
152
    /**
153
     * @param ContentItem $contentItem
154
     */
155
    public function compileContentItem (&$contentItem)
156
    {
157
        $pageView = $contentItem->getPageView();
158
159
        // This ContentItem doesn't have an individual PageView dedicated to displaying this item
160
        if (is_null($pageView))
161
        {
162
            return;
163
        }
164
165
        $template = $this->createTemplate($pageView);
166
        $contentItem->evaluateFrontMatter(
167
            $pageView->getFrontMatter(false)
168
        );
169
170
        $output = $template->render(array(
171
            'this' => $contentItem
172
        ));
173
174
        $this->targetDir->writeFile($contentItem->getTargetFile(), $output);
175
    }
176
177
    /**
178
     * Add a new ContentItem to the respective parent PageView of the ContentItem
179
     *
180
     * @param ContentItem $contentItem
181
     */
182
    public function updatePageView ($contentItem)
183
    {
184
        /** @var PageView $pageView */
185
        foreach ($this->trackedItems['dynamic'] as &$pageView)
186
        {
187
            $fm = $pageView->getFrontMatter(false);
188
189
            if ($fm['collection'] == $contentItem->getCollection())
190
            {
191
                $pageView->addContentItem($contentItem);
192
            }
193
        }
194
    }
195
196
    /**
197
     * Update an existing Twig variable that's injected globally
198
     *
199
     * @param string $variable
200
     * @param string $value
201
     */
202
    public function updateTwigVariable ($variable, $value)
203
    {
204
        $this->twig->addGlobal($variable, $value);
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210
    public function refreshItem($filePath)
211
    {
212
        $this->compileFromFilePath($filePath, true);
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218
    protected function handleTrackableItem($filePath, $options = array())
219
    {
220
        $pageView = new PageView($filePath);
221
        $namespace = 'static';
222
223
        if ($pageView->isDynamicPage())
224
        {
225
            $namespace = 'dynamic';
226
            $frontMatter = $pageView->getFrontMatter(false);
227
            $collection = $frontMatter['collection'];
228
229
            foreach ($this->collections[$collection] as &$item)
230
            {
231
                $item->evaluateFrontMatter($frontMatter);
232
                $pageView->addContentItem($item);
233
            }
234
        }
235
236
        $this->addObjectToTracker($pageView, $pageView->getRelativeFilePath(), $namespace);
237
        $this->saveTrackerOptions($pageView->getRelativeFilePath(), array(
238
            'viewType' => $namespace
239
        ));
240
241
        if ($namespace === 'static')
242
        {
243
            $this->addToSiteMenu($pageView);
244
        }
245
    }
246
247
    /**
248
     * Compile a given PageView
249
     *
250
     * @param string $filePath The file path to the PageView to compile
251
     * @param bool   $refresh  When set to true, the PageView will reread its contents
252
     *
253
     * @throws \Exception
254
     */
255
    private function compileFromFilePath ($filePath, $refresh = false)
256
    {
257
        if (!$this->isTracked($filePath))
258
        {
259
            throw new TrackedItemNotFoundException('PageView not found');
260
        }
261
262
        /** @var PageView $pageView */
263
        $pageView = &$this->trackedItemsFlattened[$filePath];
264
265
        $this->compilePageView($pageView, $refresh);
266
267
        // Create redirect files as needed
268
        foreach ($pageView->getRedirects() as $redirect)
0 ignored issues
show
Bug introduced by
The expression $pageView->getRedirects() of type null|array<integer,string> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
269
        {
270
            $redirectPageView = PageView::createRedirect(
271
                $redirect,
272
                $pageView->getPermalink(),
273
                $this->redirectTemplate
274
            );
275
276
            $this->compilePageView($redirectPageView);
277
        }
278
    }
279
280
    /**
281
     * @param PageView $pageView
282
     * @param bool     $refresh
283
     */
284
    private function compilePageView ($pageView, $refresh = false)
285
    {
286
        if ($refresh)
287
        {
288
            $pageView->refreshFileContent();
289
        }
290
291
        if ($pageView->isDynamicPage())
292
        {
293
            $this->compileDynamicPageView($pageView);
294
        }
295
        else
296
        {
297
            $this->compileStaticPageView($pageView);
298
        }
299
    }
300
301
    /**
302
     * @param PageView $pageView
303
     */
304
    private function compileDynamicPageView (&$pageView)
305
    {
306
        $template = $this->createTemplate($pageView);
307
308
        $pageViewFrontMatter = $pageView->getFrontMatter(false);
309
        $collection = $pageViewFrontMatter['collection'];
310
311
        /** @var ContentItem $contentItem */
312
        foreach ($this->collections[$collection] as &$contentItem)
313
        {
314
            $output = $template->render(array(
315
                'this' => $contentItem
316
            ));
317
318
            $this->output->notice("Writing file: {file}", array('file' => $contentItem->getTargetFile()));
319
            $this->targetDir->writeFile($contentItem->getTargetFile(), $output);
320
        }
321
    }
322
323
    /**
324
     * @param PageView $pageView
325
     */
326
    private function compileStaticPageView (&$pageView)
327
    {
328
        $this->twig->addGlobal('__currentTemplate', $pageView->getFilePath());
329
330
        $template = $this->createTemplate($pageView);
331
        $output = $template->render(array(
332
            'this' => $pageView->getFrontMatter()
333
        ));
334
335
        $this->output->notice("Writing file: {file}", array('file' => $pageView->getTargetFile()));
336
        $this->targetDir->writeFile($pageView->getTargetFile(), $output);
337
    }
338
339
    /**
340
     * Add a static PageView to the menu array. Dynamic PageViews are not added to the menu
341
     *
342
     * @param PageView $pageView
343
     */
344
    private function addToSiteMenu (&$pageView)
345
    {
346
        $frontMatter = $pageView->getFrontMatter();
347
348
        if (!array_key_exists('permalink', $frontMatter) ||
349
            (array_key_exists('menu', $frontMatter) && !$frontMatter['menu']))
350
        {
351
            return;
352
        }
353
354
        $url = $pageView->getPermalink();
355
        $root = &$this->siteMenu;
356
        $permalink = trim($url, DIRECTORY_SEPARATOR);
357
        $dirs = explode(DIRECTORY_SEPARATOR, $permalink);
358
359
        while (count($dirs) > 0)
360
        {
361
            $name = array_shift($dirs);
362
            $name = (!empty($name)) ? $name : '.';
363
364
            if (!is_null($name) && count($dirs) == 0)
365
            {
366
                $children = array();
367
368
                if (array_key_exists($name, $root) && is_array($root[$name]))
369
                {
370
                    $children = $root[$name]['children'];
371
                }
372
373
                $root[$name] = &$pageView;
374
                $root = &$root[$name]->getChildren();
375
376
                if (!empty($children))
377
                {
378
                    $root = $children;
379
                }
380
            }
381
            else
382
            {
383
                $root[$name]['children'] = array();
384
                $root = &$root[$name]['children'];
385
            }
386
        }
387
    }
388
389
    /**
390
     * @param PageView $pageView
391
     *
392
     * @return Twig_Template
393
     * @throws Twig_Error_Syntax
394
     */
395
    private function createTemplate ($pageView)
396
    {
397
        try
398
        {
399
            return $this->twig->createTemplate($pageView->getContent());
400
        }
401
        catch (Twig_Error_Syntax $e)
402
        {
403
            $e->setTemplateFile($pageView->getRelativeFilePath());
404
405
            throw $e;
406
        }
407
    }
408
}