Completed
Pull Request — master (#46)
by Vladimir
03:10
created

Website::isConfLess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright 2017 Vladimir Jimenez
5
 * @license   https://github.com/allejo/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx;
9
10
use allejo\stakx\Command\BuildableCommand;
11
use allejo\stakx\Core\StakxLogger;
12
use allejo\stakx\Manager\AssetManager;
13
use allejo\stakx\Manager\CollectionManager;
14
use allejo\stakx\Manager\DataManager;
15
use allejo\stakx\Manager\MenuManager;
16
use allejo\stakx\Manager\PageManager;
17
use allejo\stakx\Manager\ThemeManager;
18
use allejo\stakx\Manager\TwigManager;
19
use allejo\stakx\System\FileExplorer;
20
use allejo\stakx\System\Filesystem;
21
use allejo\stakx\System\Folder;
22
use Kwf\FileWatcher\Event\Modify;
23
use Kwf\FileWatcher\Watcher;
24
use Symfony\Component\Console\Output\OutputInterface;
25
26
class Website
27
{
28
    /**
29
     * The location of where the compiled website will be written to.
30
     *
31
     * @var Folder
32
     */
33
    private $outputDirectory;
34
35
    /**
36
     * The main configuration to be used to build the specified website.
37
     *
38
     * @var Configuration
39
     */
40
    private $configuration;
41
42
    /**
43
     * When set to true, the Stakx website will be built without a configuration file.
44
     *
45
     * @var bool
46
     */
47
    private $confLess;
48
49
    /**
50
     * When set to true, Twig templates will not have access to filters or functions which provide access to the
51
     * filesystem.
52
     *
53
     * @var bool
54
     */
55
    private $safeMode;
56
57
    /**
58
     * When set to true, Stakx will not clean the _site folder after a rebuild.
59
     *
60
     * @var bool
61
     */
62
    private $noClean;
63
64
    /**
65
     * @var StakxLogger
66
     */
67
    private $output;
68
69
    /**
70
     * @var AssetManager
71
     */
72
    private $am;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $am. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
73
74
    /**
75
     * @var CollectionManager
76
     */
77
    private $cm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $cm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
78
79
    /**
80
     * @var DataManager
81
     */
82
    private $dm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $dm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
83
84
    /**
85
     * @var Filesystem
86
     */
87
    private $fs;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
88
89
    /** @var MenuManager */
90
    private $mm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $mm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
91
92
    /**
93
     * @var PageManager
94
     */
95
    private $pm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $pm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
96
97
    /**
98
     * @var ThemeManager
99
     */
100
    private $tm;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $tm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
101
102
    /** @var Compiler */
103
    private $compiler;
104
105
    /**
106
     * Website constructor.
107
     *
108
     * @param OutputInterface $output
109
     */
110
    public function __construct(OutputInterface $output)
111
    {
112
        $this->output = new StakxLogger($output);
113
        $this->cm = new CollectionManager();
114
        $this->dm = new DataManager();
115
        $this->mm = new MenuManager();
116
        $this->pm = new PageManager();
117
        $this->fs = new Filesystem();
118
    }
119
120
    /**
121
     * Compile the website.
122
     *
123
     * @param bool $tracking Whether or not to keep track of files as they're compiled to save time in 'watch'
124
     */
125
    public function build($tracking = false)
126
    {
127
        Service::setParameter(BuildableCommand::WATCHING, $tracking);
128
129
        // Configure the environment
130
        $this->createFolderStructure(!$this->noClean);
131
132
        // Our output directory
133
        $this->outputDirectory = new Folder($this->getConfiguration()->getTargetFolder());
134
        $this->outputDirectory->setTargetDirectory($this->getConfiguration()->getBaseUrl());
135
136
        // Parse DataItems
137
        $this->dm->setLogger($this->output);
138
        $this->dm->enableTracking($tracking);
139
        $this->dm->parseDataItems($this->getConfiguration()->getDataFolders());
140
        $this->dm->parseDataSets($this->getConfiguration()->getDataSets());
141
142
        // Prepare Collections
143
        $this->cm->setLogger($this->output);
144
        $this->cm->enableTracking($tracking);
145
        $this->cm->parseCollections($this->getConfiguration()->getCollectionsFolders());
146
147
        // Handle PageViews
148
        $this->pm->setLogger($this->output);
149
        $this->pm->enableTracking($tracking);
150
        $this->pm->setCollections($this->cm->getCollections());
151
        $this->pm->parsePageViews($this->getConfiguration()->getPageViewFolders());
152
153
        // Handle the site's menu
154
        $this->mm->setLogger($this->output);
155
        $this->mm->buildFromPageViews($this->pm->getStaticPageViews());
156
157
        // Configure our Twig environment
158
        $theme = $this->configuration->getTheme();
159
        $twigEnv = new TwigManager();
160
        $twigEnv->configureTwig($this->getConfiguration(), array(
161
            'safe'    => $this->safeMode,
162
            'globals' => array(
163
                array('name' => 'site', 'value' => $this->getConfiguration()->getConfiguration()),
164
                array('name' => 'collections', 'value' => $this->cm->getJailedCollections()),
165
                array('name' => 'menu', 'value' => $this->mm->getSiteMenu()),
166
                array('name' => 'pages', 'value' => $this->pm->getJailedStaticPageViews()),
167
                array('name' => 'data', 'value' => $this->dm->getDataItems()),
168
            ),
169
        ));
170
171
        // Compile everything
172
        $this->compiler = new Compiler();
173
        $this->compiler->setLogger($this->output);
174
        $this->compiler->setRedirectTemplate($this->getConfiguration()->getRedirectTemplate());
175
        $this->compiler->setPageViews($this->pm->getPageViews(), $this->pm->getPageViewsFlattened());
176
        $this->compiler->setTargetFolder($this->outputDirectory);
177
        $this->compiler->setThemeName($theme);
178
        $this->compiler->compileAll();
179
180
        // At this point, we are looking at static files to copy over meaning we need to ignore all of the files that
181
        // make up the source of a stakx website
182
        $assetsToIgnore = array_merge(
183
            Configuration::$stakxSourceFiles,
184
            $this->getConfiguration()->getExcludes()
185
        );
186
187
        //
188
        // Theme Management
189
        //
190
        if (!is_null($theme))
191
        {
192
            $this->output->notice("Looking for '${theme}' theme...");
193
194
            $this->tm = new ThemeManager($theme);
195
            $this->tm->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
196
            $this->tm->setLogger($this->output);
197
            $this->tm->enableTracking($tracking);
198
            $this->tm->setFolder($this->outputDirectory);
199
            $this->tm->copyFiles();
200
        }
201
202
        //
203
        // Static file management
204
        //
205
        $this->am = new AssetManager();
206
        $this->am->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
207
        $this->am->setLogger($this->output);
208
        $this->am->setFolder($this->outputDirectory);
209
        $this->am->enableTracking($tracking);
210
        $this->am->copyFiles();
211
    }
212
213
    public function watch()
214
    {
215
        $this->output->writeln('Building website...');
216
        $this->build(true);
217
        $this->output->writeln(sprintf('Watching %s', getcwd()));
218
219
        $fileExplorer = FileExplorer::create(
0 ignored issues
show
Unused Code introduced by
$fileExplorer is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
220
            getcwd(),
221
            array_merge($this->getConfiguration()->getExcludes(), array(
222
                $this->getConfiguration()->getTargetFolder(),
223
            )),
224
            $this->getConfiguration()->getIncludes()
225
        );
226
227
        $newWatcher = Watcher::create(getcwd());
228
        $newWatcher->setExcludePatterns(array_merge(
229
            $this->getConfiguration()->getExcludes(),
230
            array(
231
                $this->getConfiguration()->getTargetFolder() . DIRECTORY_SEPARATOR,
232
            )
233
        ));
234
        $newWatcher->addListener(Modify::NAME, function ($e)
235
        {
236
            $filePath = $this->fs->getRelativePath($e->filename);
237
238
            try
239
            {
240
                $this->modificationWatcher($filePath);
241
            }
242
            catch (\Exception $e)
243
            {
244
                $this->output->error(sprintf('Your website failed to build with the following error: %s',
245
                    $e->getMessage()
246
                ));
247
            }
248
        });
249
250
        $this->output->writeln('Watch started successfully');
251
252
        $newWatcher->start();
253
    }
254
255
    /**
256
     * @return Configuration
257
     */
258
    public function getConfiguration()
259
    {
260
        return $this->configuration;
261
    }
262
263
    /**
264
     * @param string $configFile
265
     *
266
     * @throws \LogicException
267
     */
268
    public function setConfiguration($configFile)
269
    {
270
        if (!$this->fs->exists($configFile) && !$this->isConfLess())
271
        {
272
            $this->output->error('You are trying to build a website in a directory without a configuration file. Is this what you meant to do?');
273
            $this->output->error("To build a website without a configuration, use the '--no-conf' option");
274
275
            throw new \LogicException('Cannot build a website without a configuration when not in Configuration-less mode');
276
        }
277
278
        if ($this->isConfLess())
279
        {
280
            $configFile = '';
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $configFile. This often makes code more readable.
Loading history...
281
        }
282
283
        $this->configuration = new Configuration();
284
        $this->configuration->setLogger($this->output);
285
        $this->configuration->parse($configFile);
286
    }
287
288
    /**
289
     * Get whether or not the website is being built in Configuration-less mode.
290
     *
291
     * @return bool True when being built with no configuration file
292
     */
293
    public function isConfLess()
294
    {
295
        return $this->confLess;
296
    }
297
298
    /**
299
     * Set whether or not the website should be built with a configuration.
300
     *
301
     * @param bool $status True when a website should be built without a configuration
302
     */
303
    public function setConfLess($status)
304
    {
305
        $this->confLess = $status;
306
    }
307
308
    /**
309
     * Get whether or not the website is being built in safe mode.
310
     *
311
     * Safe mode is defined as disabling file system access from Twig and disabling user Twig extensions
312
     *
313
     * @return bool True when the website is being built in safe mode
314
     */
315
    public function isSafeMode()
316
    {
317
        return $this->safeMode;
318
    }
319
320
    /**
321
     * Set whether a website should be built in safe mode.
322
     *
323
     * @param bool $bool True if a website should be built in safe mode
324
     */
325
    public function setSafeMode($bool)
326
    {
327
        $this->safeMode = $bool;
328
    }
329
330
    /**
331
     * @return bool
332
     */
333
    public function isNoClean()
334
    {
335
        return $this->noClean;
336
    }
337
338
    /**
339
     * @param bool $noClean
340
     */
341
    public function setNoClean($noClean)
342
    {
343
        $this->noClean = $noClean;
344
    }
345
346
    private function creationWatcher($filePath)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
347
    {
348
        $this->output->writeln(sprintf('File creation detected: %s', $filePath));
349
350
        if ($this->pm->isHandled($filePath))
351
        {
352
            $this->pm->createNewItem($filePath);
353
            $this->pm->refreshItem($filePath);
354
        }
355
        elseif ($this->cm->isHandled($filePath))
356
        {
357
            $contentItem = $this->cm->createNewItem($filePath);
358
            TwigManager::getInstance()->addGlobal('collections', $this->cm->getCollections());
359
360
            $this->pm->trackNewContentItem($contentItem);
361
            $this->compiler->compileContentItem($contentItem);
0 ignored issues
show
Deprecated Code introduced by
The method allejo\stakx\Compiler::compileContentItem() has been deprecated.

This method has been deprecated.

Loading history...
362
            $this->compiler->compileSome(array(
363
                'namespace'  => 'collections',
364
                'dependency' => $contentItem->getCollection(),
365
            ));
366
        }
367 View Code Duplication
        elseif ($this->dm->isHandled($filePath))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
368
        {
369
            $change = $this->dm->createNewItem($filePath);
370
            TwigManager::getInstance()->addGlobal('data', $this->dm->getDataItems());
371
372
            $this->compiler->compileSome(array(
373
                'namespace'  => 'data',
374
                'dependency' => $change,
375
            ));
376
        }
377
        elseif (!is_null($this->tm) && $this->tm->isHandled($filePath))
378
        {
379
            $this->tm->createNewItem($filePath);
380
        }
381
        elseif ($this->am->isHandled($filePath))
382
        {
383
            $this->am->createNewItem($filePath);
384
        }
385
    }
386
387
    private function modificationWatcher($filePath)
388
    {
389
        $this->output->writeln(sprintf('File change detected: %s', $filePath));
390
391
        if ($this->compiler->isParentTemplate($filePath))
392
        {
393
            TwigManager::getInstance()->clearTemplateCache();
0 ignored issues
show
Deprecated Code introduced by
The method Twig_Environment::clearTemplateCache() has been deprecated with message: since 1.18.3 (to be removed in 2.0)

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
394
            $this->compiler->refreshParent($filePath);
395
        }
396
        elseif ($this->pm->isTracked($filePath))
397
        {
398
            $change = $this->pm->refreshItem($filePath);
399
400
            TwigManager::getInstance()->clearTemplateCache();
0 ignored issues
show
Deprecated Code introduced by
The method Twig_Environment::clearTemplateCache() has been deprecated with message: since 1.18.3 (to be removed in 2.0)

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
401
            $this->compiler->compilePageView($change);
402
        }
403
        elseif ($this->cm->isTracked($filePath))
404
        {
405
            $contentItem = &$this->cm->getContentItem($filePath);
406
            $contentItem->refreshFileContent();
407
408
            $this->compiler->compileContentItem($contentItem);
0 ignored issues
show
Deprecated Code introduced by
The method allejo\stakx\Compiler::compileContentItem() has been deprecated.

This method has been deprecated.

Loading history...
409
            $this->compiler->compileSome(array(
410
                'namespace'  => 'collections',
411
                'dependency' => $contentItem->getCollection(),
412
            ));
413
        }
414 View Code Duplication
        elseif ($this->dm->isTracked($filePath))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
415
        {
416
            $change = $this->dm->refreshItem($filePath);
417
            TwigManager::getInstance()->addGlobal('data', $this->dm->getDataItems());
418
419
            $this->compiler->compileSome(array(
420
                'namespace'  => 'data',
421
                'dependency' => $change,
422
            ));
423
        }
424
        elseif (!is_null($this->tm) && $this->tm->isTracked($filePath))
425
        {
426
            $this->tm->refreshItem($filePath);
427
        }
428
        elseif ($this->am->isTracked($filePath))
429
        {
430
            $this->am->refreshItem($filePath);
431
        }
432
    }
433
434
    /**
435
     * Prepare the Stakx environment by creating necessary cache folders.
436
     *
437
     * @param bool $cleanDirectory Clean the target directory
438
     */
439
    private function createFolderStructure($cleanDirectory)
440
    {
441
        $tarDir = $this->fs->absolutePath($this->configuration->getTargetFolder());
442
443
        if ($cleanDirectory)
444
        {
445
            $this->fs->remove($tarDir);
446
        }
447
448
        $this->fs->remove($this->fs->absolutePath('.stakx-cache'));
449
        $this->fs->mkdir($this->fs->absolutePath('.stakx-cache/twig'));
450
        $this->fs->mkdir($tarDir);
451
    }
452
}
453