Completed
Pull Request — master (#46)
by Vladimir
13:24 queued 03:28
created

Website::isSafeMode()   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\Exception\FileAwareException;
13
use allejo\stakx\Manager\AssetManager;
14
use allejo\stakx\Manager\CollectionManager;
15
use allejo\stakx\Manager\DataManager;
16
use allejo\stakx\Manager\MenuManager;
17
use allejo\stakx\Manager\PageManager;
18
use allejo\stakx\Manager\ThemeManager;
19
use allejo\stakx\Manager\TwigManager;
20
use allejo\stakx\System\FileExplorer;
21
use allejo\stakx\System\Filesystem;
22
use allejo\stakx\System\Folder;
23
use Kwf\FileWatcher\Event\AbstractEvent;
24
use Kwf\FileWatcher\Event\Create;
25
use Kwf\FileWatcher\Event\Modify;
26
use Kwf\FileWatcher\Watcher;
27
use Symfony\Component\Console\Output\OutputInterface;
28
29
class Website
30
{
31
    /**
32
     * The location of where the compiled website will be written to.
33
     *
34
     * @var Folder
35
     */
36
    private $outputDirectory;
37
38
    /**
39
     * The main configuration to be used to build the specified website.
40
     *
41
     * @var Configuration
42
     */
43
    private $configuration;
44
45
    /**
46
     * When set to true, the Stakx website will be built without a configuration file.
47
     *
48
     * @var bool
49
     */
50
    private $confLess;
51
52
    /**
53
     * @var StakxLogger
54
     */
55
    private $output;
56
57
    /**
58
     * @var AssetManager
59
     */
60
    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...
61
62
    /**
63
     * @var CollectionManager
64
     */
65
    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...
66
67
    /**
68
     * @var DataManager
69
     */
70
    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...
71
72
    /**
73
     * @var Filesystem
74
     */
75
    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...
76
77
    /** @var MenuManager */
78
    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...
79
80
    /**
81
     * @var PageManager
82
     */
83
    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...
84
85
    /**
86
     * @var ThemeManager
87
     */
88
    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...
89
90
    /** @var Compiler */
91
    private $compiler;
92
93
    /**
94
     * Website constructor.
95
     *
96
     * @param OutputInterface $output
97
     */
98
    public function __construct(OutputInterface $output)
99
    {
100
        $this->output = new StakxLogger($output);
101
        $this->cm = new CollectionManager();
102
        $this->dm = new DataManager();
103
        $this->mm = new MenuManager();
104
        $this->pm = new PageManager();
105
        $this->fs = new Filesystem();
106
    }
107
108
    /**
109
     * Compile the website.
110
     *
111
     * @param bool $tracking Whether or not to keep track of files as they're compiled to save time in 'watch'
112
     */
113
    public function build($tracking = false)
114
    {
115
        Service::setParameter(BuildableCommand::WATCHING, $tracking);
116
117
        // Configure the environment
118
        $this->createFolderStructure();
119
120
        // Our output directory
121
        $this->outputDirectory = new Folder($this->getConfiguration()->getTargetFolder());
122
        $this->outputDirectory->setTargetDirectory($this->getConfiguration()->getBaseUrl());
123
124
        // Parse DataItems
125
        $this->dm->setLogger($this->output);
126
        $this->dm->enableTracking($tracking);
127
        $this->dm->parseDataItems($this->getConfiguration()->getDataFolders());
128
        $this->dm->parseDataSets($this->getConfiguration()->getDataSets());
129
130
        // Prepare Collections
131
        $this->cm->setLogger($this->output);
132
        $this->cm->enableTracking($tracking);
133
        $this->cm->parseCollections($this->getConfiguration()->getCollectionsFolders());
134
135
        // Handle PageViews
136
        $this->pm->setLogger($this->output);
137
        $this->pm->enableTracking($tracking);
138
        $this->pm->setCollections($this->cm->getCollections());
139
        $this->pm->parsePageViews($this->getConfiguration()->getPageViewFolders());
140
141
        // Handle the site's menu
142
        $this->mm->setLogger($this->output);
143
        $this->mm->buildFromPageViews($this->pm->getStaticPageViews());
144
145
        // Configure our Twig environment
146
        $theme = $this->configuration->getTheme();
147
        $twigEnv = new TwigManager();
148
        $twigEnv->configureTwig($this->getConfiguration(), array(
149
            'safe'    => Service::getParameter(BuildableCommand::SAFE_MODE),
150
            'globals' => array(
151
                array('name' => 'site', 'value' => $this->getConfiguration()->getConfiguration()),
152
                array('name' => 'collections', 'value' => $this->cm->getJailedCollections()),
153
                array('name' => 'menu', 'value' => $this->mm->getSiteMenu()),
154
                array('name' => 'pages', 'value' => $this->pm->getJailedStaticPageViews()),
155
                array('name' => 'data', 'value' => $this->dm->getDataItems()),
156
            ),
157
        ));
158
159
        // Compile everything
160
        $this->compiler = new Compiler();
161
        $this->compiler->setLogger($this->output);
162
        $this->compiler->setRedirectTemplate($this->getConfiguration()->getRedirectTemplate());
163
        $this->compiler->setPageViews($this->pm->getPageViews(), $this->pm->getPageViewsFlattened());
164
        $this->compiler->setTargetFolder($this->outputDirectory);
165
        $this->compiler->setThemeName($theme);
166
        $this->compiler->compileAll();
167
168
        // At this point, we are looking at static files to copy over meaning we need to ignore all of the files that
169
        // make up the source of a stakx website
170
        $assetsToIgnore = array_merge(
171
            Configuration::$stakxSourceFiles,
172
            $this->getConfiguration()->getExcludes()
173
        );
174
175
        //
176
        // Theme Management
177
        //
178
        if (!is_null($theme))
179
        {
180
            $this->output->notice("Looking for '${theme}' theme...");
181
182
            $this->tm = new ThemeManager($theme);
183
            $this->tm->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
184
            $this->tm->setLogger($this->output);
185
            $this->tm->enableTracking($tracking);
186
            $this->tm->setFolder($this->outputDirectory);
187
            $this->tm->copyFiles();
188
        }
189
190
        //
191
        // Static file management
192
        //
193
        $this->am = new AssetManager();
194
        $this->am->configureFinder($this->getConfiguration()->getIncludes(), $assetsToIgnore);
195
        $this->am->setLogger($this->output);
196
        $this->am->setFolder($this->outputDirectory);
197
        $this->am->enableTracking($tracking);
198
        $this->am->copyFiles();
199
    }
200
201
    public function watch()
202
    {
203
        $this->output->writeln('Building website...');
204
        $this->build(true);
205
        $this->output->writeln(sprintf('Watching %s', getcwd()));
206
207
        $exclusions = array_merge($this->getConfiguration()->getExcludes(), array(
208
            $this->getConfiguration()->getTargetFolder()
209
        ));
210
        $fileExplorer = FileExplorer::create(
211
            getcwd(), $exclusions, $this->getConfiguration()->getIncludes()
212
        );
213
214
        $newWatcher = Watcher::create(getcwd());
215
        $newWatcher
216
            ->setLogger($this->output)
217
            ->setExcludePatterns(array_merge(
218
                $exclusions, FileExplorer::$vcsPatterns, array(Configuration::CACHE_FOLDER)
219
            ))
220
            ->setIterator($fileExplorer->getExplorer())
221
            ->addListener(Create::NAME, function ($e) { $this->watchListenerFunction($e); })
222
            ->addListener(Modify::NAME, function ($e) { $this->watchListenerFunction($e); })
223
        ;
224
225
        $this->output->writeln('Watch started successfully');
226
227
        $newWatcher->start();
228
    }
229
230
    private function watchListenerFunction(AbstractEvent $event)
231
    {
232
        $filePath = $this->fs->getRelativePath($event->filename);
233
234
        try
235
        {
236
            switch ($event::getEventName())
237
            {
238
                case Create::NAME:
239
                    $this->creationWatcher($filePath);
240
                    break;
241
242
                case Modify::NAME:
243
                    $this->modificationWatcher($filePath);
244
                    break;
245
            }
246
        }
247
        catch (FileAwareException $e)
248
        {
249
            $this->output->writeln(sprintf("Your website failed to build with the following error in file '%s': %s",
250
                $e->getPath(),
251
                $e->getMessage()
252
            ));
253
        }
254
        catch (\Exception $e)
255
        {
256
            $this->output->writeln(sprintf('Your website failed to build with the following error: %s',
257
                $e->getMessage()
258
            ));
259
        }
260
    }
261
262
    /**
263
     * @return Configuration
264
     */
265
    public function getConfiguration()
266
    {
267
        return $this->configuration;
268
    }
269
270
    /**
271
     * @param string $configFile
272
     *
273
     * @throws \LogicException
274
     */
275
    public function setConfiguration($configFile)
276
    {
277
        if (!$this->fs->exists($configFile) && !$this->isConfLess())
278
        {
279
            $this->output->error('You are trying to build a website in a directory without a configuration file. Is this what you meant to do?');
280
            $this->output->error("To build a website without a configuration, use the '--no-conf' option");
281
282
            throw new \LogicException('Cannot build a website without a configuration when not in Configuration-less mode');
283
        }
284
285
        if ($this->isConfLess())
286
        {
287
            $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...
288
        }
289
290
        $this->configuration = new Configuration();
291
        $this->configuration->setLogger($this->output);
292
        $this->configuration->parse($configFile);
293
    }
294
295
    /**
296
     * Get whether or not the website is being built in Configuration-less mode.
297
     *
298
     * @return bool True when being built with no configuration file
299
     */
300
    public function isConfLess()
301
    {
302
        return $this->confLess;
303
    }
304
305
    /**
306
     * Set whether or not the website should be built with a configuration.
307
     *
308
     * @param bool $status True when a website should be built without a configuration
309
     */
310
    public function setConfLess($status)
311
    {
312
        $this->confLess = $status;
313
    }
314
315
    private function creationWatcher($filePath)
316
    {
317
        $this->output->writeln(sprintf('File creation detected: %s', $filePath));
318
319
        if ($this->pm->isHandled($filePath))
320
        {
321
            $this->pm->createNewItem($filePath);
322
            $this->pm->refreshItem($filePath);
323
        }
324
        elseif ($this->cm->isHandled($filePath))
325
        {
326
            $contentItem = $this->cm->createNewItem($filePath);
327
            TwigManager::getInstance()->addGlobal('collections', $this->cm->getCollections());
328
329
            $this->pm->trackNewContentItem($contentItem);
330
            $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...
331
            $this->compiler->compileSome(array(
332
                'namespace'  => 'collections',
333
                'dependency' => $contentItem->getCollection(),
334
            ));
335
        }
336 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...
337
        {
338
            $change = $this->dm->createNewItem($filePath);
339
            TwigManager::getInstance()->addGlobal('data', $this->dm->getDataItems());
340
341
            $this->compiler->compileSome(array(
342
                'namespace'  => 'data',
343
                'dependency' => $change,
344
            ));
345
        }
346
        elseif (!is_null($this->tm) && $this->tm->isHandled($filePath))
347
        {
348
            $this->tm->createNewItem($filePath);
349
        }
350
        elseif ($this->am->isHandled($filePath))
351
        {
352
            $this->am->createNewItem($filePath);
353
        }
354
    }
355
356
    private function modificationWatcher($filePath)
357
    {
358
        $this->output->writeln(sprintf('File change detected: %s', $filePath));
359
360
        if ($this->compiler->isParentTemplate($filePath))
361
        {
362
            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...
363
            $this->compiler->refreshParent($filePath);
364
        }
365
        elseif ($this->pm->isTracked($filePath))
366
        {
367
            $change = $this->pm->refreshItem($filePath);
368
369
            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...
370
            $this->compiler->compilePageView($change);
371
        }
372
        elseif ($this->cm->isTracked($filePath))
373
        {
374
            $contentItem = &$this->cm->getContentItem($filePath);
375
            $contentItem->refreshFileContent();
376
377
            $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...
378
            $this->compiler->compileSome(array(
379
                'namespace'  => 'collections',
380
                'dependency' => $contentItem->getCollection(),
381
            ));
382
        }
383 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...
384
        {
385
            $change = $this->dm->refreshItem($filePath);
386
            TwigManager::getInstance()->addGlobal('data', $this->dm->getDataItems());
387
388
            $this->compiler->compileSome(array(
389
                'namespace'  => 'data',
390
                'dependency' => $change,
391
            ));
392
        }
393
        elseif (!is_null($this->tm) && $this->tm->isTracked($filePath))
394
        {
395
            $this->tm->refreshItem($filePath);
396
        }
397
        elseif ($this->am->isTracked($filePath))
398
        {
399
            $this->am->refreshItem($filePath);
400
        }
401
    }
402
403
    /**
404
     * Prepare the Stakx environment by creating necessary cache folders.
405
     *
406
     * @param bool $cleanDirectory Clean the target directory
0 ignored issues
show
Bug introduced by
There is no parameter named $cleanDirectory. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
407
     */
408
    private function createFolderStructure()
409
    {
410
        $targetDir = $this->fs->absolutePath($this->configuration->getTargetFolder());
411
412
        if (!Service::getParameter(BuildableCommand::NO_CLEAN))
413
        {
414
            $this->fs->remove($targetDir);
415
        }
416
417
        if (Service::getParameter(BuildableCommand::CLEAN_CACHE))
418
        {
419
            $this->fs->remove($this->fs->absolutePath(Configuration::CACHE_FOLDER));
420
            $this->fs->mkdir($this->fs->absolutePath($this->fs->appendPath(Configuration::CACHE_FOLDER, 'twig')));
421
        }
422
423
        $this->fs->mkdir($targetDir);
424
    }
425
}
426