Passed
Push — master ( 287999...316c8c )
by Dev
11:15
created

StaticService::copyAssets()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 14
nc 6
nop 0
dl 0
loc 23
ccs 0
cts 16
cp 0
crap 72
rs 8.4444
c 1
b 0
f 0
1
<?php
2
3
namespace PiedWeb\CMSBundle\Service;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use PiedWeb\CMSBundle\Entity\PageInterface as Page;
7
use PiedWeb\CMSBundle\Repository\PageRepository;
8
use PiedWeb\CMSBundle\Service\PageCanonicalService as PageCanonical;
9
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
10
use Symfony\Component\Filesystem\Filesystem;
11
use Symfony\Component\HttpFoundation\Request;
12
use Symfony\Component\HttpFoundation\RequestStack;
13
use Symfony\Contracts\Translation\TranslatorInterface;
14
use Twig\Environment as Twig;
15
use WyriHaximus\HtmlCompress\Factory as HtmlCompressor;
16
use WyriHaximus\HtmlCompress\Parser as HtmlCompressorParser;
0 ignored issues
show
Bug introduced by
The type WyriHaximus\HtmlCompress\Parser was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
18
class StaticService
19
{
20
    /**
21
     * Contain files relative to SEO wich will be hard copied.
22
     *
23
     * @var array
24
     */
25
    protected $robotsFiles = ['robots.txt', 'feed.xml', 'sitemap.xml', 'sitemap.txt'];
26
27
    /**
28
     * @var array
29
     */
30
    protected $dontCopy = ['index.php', '.htaccess'];
31
32
    /**
33
     * @var EntityManagerInterface
34
     */
35
    protected $em;
36
37
    /**
38
     * @var Filesystem
39
     */
40
    protected $filesystem;
41
42
    /**
43
     * @var Twig
44
     */
45
    protected $twig;
46
47
    /**
48
     * @var string
49
     */
50
    protected $webDir;
51
52
    /**
53
     * @var string
54
     */
55
    protected $staticDir;
56
57
    /**
58
     * @var RequestStack
59
     */
60
    protected $requesStack;
61
62
    /**
63
     * @var \PiedWeb\CMSBundle\Service\PageCanonicalService
64
     */
65
    protected $pageCanonical;
66
67
    /**
68
     * @var TranslatorInterface
69
     */
70
    protected $translator;
71
72
    /**
73
     * @var HtmlCompressorParser
74
     */
75
    protected $parser;
76
77
    /**
78
     * @var ParameterBagInterface
79
     */
80
    protected $params;
81
82
    /**
83
     * Used in .htaccess generation.
84
     *
85
     * @var string
86
     */
87
    protected $redirections = '';
88
89
    public function __construct(
90
        EntityManagerInterface $em,
91
        Twig $twig,
92
        ParameterBagInterface $params,
93
        RequestStack $requesStack,
94
        PageCanonical $pageCanonical,
95
        TranslatorInterface $translator,
96
        string $webDir
97
    ) {
98
        $this->em = $em;
99
        $this->filesystem = new Filesystem();
100
        $this->twig = $twig;
101
        $this->params = $params;
102
        $this->requesStack = $requesStack;
103
        $this->webDir = $webDir;
104
        $this->pageCanonical = $pageCanonical;
105
        $this->translator = $translator;
106
        $this->staticDir = $this->params->get('pwc.static.dir');
107
        $this->parser = HtmlCompressor::construct();
0 ignored issues
show
Documentation Bug introduced by
It seems like WyriHaximus\HtmlCompress\Factory::construct() of type WyriHaximus\HtmlCompress\HtmlCompressor is incompatible with the declared type WyriHaximus\HtmlCompress\Parser of property $parser.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
108
    }
109
110
    /**
111
     * Main Logic is here.
112
     *
113
     * @throws \RuntimeException
114
     * @throws \LogicException
115
     */
116
    public function dump()
117
    {
118
        if (!method_exists($this->filesystem, 'dumpFile')) {
119
            throw new \RuntimeException('Method dumpFile() is not available. Upgrade your Filesystem.');
120
        }
121
122
        $this->filesystem->remove($this->staticDir);
123
124
        $this->generatePages();
125
        $this->generateErrorPages();
126
        $this->copyRobotsFiles();
127
        $this->generateServerManagerFile();
128
        $this->copyAssets();
129
        $this->copyMediaToDownload();
130
    }
131
132
    /**
133
     * Symlink doesn't work on github page, symlink only for apache if conf say OK to symlink.
134
     */
135
    protected function mustSymlink()
136
    {
137
        return $this->params->get('pwc.static.generateForApache') ?
138
            $this->params->get('pwc.static.symlinkMedia') : false;
139
    }
140
141
    /**
142
     * Generate .htaccess for Apache or CNAME for github
143
     * Must be run after generatePages() !!
144
     */
145
    protected function generateServerManagerFile()
146
    {
147
        if ($this->params->get('pwc.static.generateForApache')) {
148
            $this->generateHtaccess();
149
        } else { //if ($this->params->has('pwc.static.generateForGithubPages')) {
150
            $this->generateCname();
151
        }
152
    }
153
154
    /**
155
     * Copy files relative to SEO (robots, sitemaps, etc.).
156
     */
157
    protected function copyRobotsFiles(): void
158
    {
159
        array_map([$this, 'copy'], $this->robotsFiles);
160
    }
161
162
    // todo
163
    // docs
164
    // https://help.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site
165
    protected function generateCname()
166
    {
167
        $this->filesystem->dumpFile($this->staticDir.'/CNAME', $this->params->get('pwc.static.domain'));
168
    }
169
170
    protected function generateHtaccess()
171
    {
172
        if (!$this->params->has('pwc.static.domain')) {
173
            throw new \Exception('Before, you need to configure (in config/packages/piedweb_cms.yaml) static_domain.');
174
        }
175
176
        $htaccess = $this->twig->render('@PiedWebCMS/static/htaccess.twig', [
177
            'domain' => $this->params->get('pwc.static.domain'),
178
            'redirections' => $this->redirections,
179
        ]);
180
        $this->filesystem->dumpFile($this->staticDir.'/.htaccess', $htaccess);
181
    }
182
183
    protected function copy(string $file): void
184
    {
185
        if (file_exists($file)) {
186
            copy(
187
                str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$file),
188
                $this->staticDir.'/'.$file
189
            );
190
        }
191
    }
192
193
    /**
194
     * Copy (or symlink) for all assets in public
195
     * (and media previously generated by liip in public).
196
     */
197
    protected function copyAssets(): void
198
    {
199
        $symlink = $this->mustSymlink();
200
201
        $dir = dir($this->webDir);
202
        while (false !== $entry = $dir->read()) {
203
            if ('.' == $entry || '..' == $entry) {
204
                continue;
205
            }
206
            if (!in_array($entry, $this->robotsFiles) && !in_array($entry, $this->dontCopy)) {
207
                //$this->symlink(
208
                if (true === $symlink) {
209
                    $this->filesystem->symlink(
210
                        str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$entry),
211
                        $this->staticDir.'/'.$entry
212
                    );
213
                } else {
214
                    $action = is_file($this->webDir.'/'.$entry) ? 'copy' : 'mirror';
215
                    $this->filesystem->$action($this->webDir.'/'.$entry, $this->staticDir.'/'.$entry);
216
                }
217
            }
218
        }
219
        $dir->close();
220
    }
221
222
    /**
223
     * Copy or Symlink "not image" media to download folder.
224
     *
225
     * @return void
226
     */
227
    protected function copyMediaToDownload()
228
    {
229
        $symlink = $this->mustSymlink();
230
231
        if (!file_exists($this->staticDir.'/download')) {
232
            $this->filesystem->mkdir($this->staticDir.'/download/');
233
            $this->filesystem->mkdir($this->staticDir.'/download/media');
234
        }
235
236
        $dir = dir($this->webDir.'/../media');
237
        while (false !== $entry = $dir->read()) {
238
            if ('.' == $entry || '..' == $entry) {
239
                continue;
240
            }
241
            // if the file is an image, it's ever exist (maybe it's slow to check every files)
242
            if (!file_exists($this->webDir.'/media/default/'.$entry)) {
243
                if (true === $symlink) {
244
                    $this->filesystem->symlink('../../../media/'.$entry, $this->staticDir.'/download/media/'.$entry);
245
                } else {
246
                    $this->filesystem->copy(
247
                        $this->webDir.'/../media/'.$entry,
248
                        $this->staticDir.'/download/media/'.$entry
249
                    );
250
                }
251
            }
252
        }
253
254
        //$this->filesystem->$action($this->webDir.'/../media', $this->staticDir.'/download/media');
255
    }
256
257
    protected function generatePages(): void
258
    {
259
        $pages = $this->getPages();
260
261
        foreach ($pages as $page) {
262
            $this->generatePage($page);
263
            $this->generateFeedFor($page);
264
        }
265
    }
266
267
    /**
268
     * The function cache redirection found during generatePages and
269
     * format in self::$redirection the content for the .htaccess.
270
     *
271
     * @return void
272
     */
273
    protected function addRedirection(Page $page)
274
    {
275
        $this->redirections .= 'Redirect ';
276
        $this->redirections .= $page->getRedirectionCode().' ';
277
        $this->redirections .= $this->pageCanonical->generatePathForPage($page->getRealSlug());
278
        $this->redirections .= ' '.$page->getRedirection();
279
        $this->redirections .= PHP_EOL;
280
    }
281
282
    public function generatePage(Page $page)
283
    {
284
        // set current locale to avoid twig error
285
        $request = new Request();
286
        $request->setLocale($page->getLocale());
287
        $this->requesStack->push($request);
288
289
        //$this->translator->setLocale($page->getLocale());
290
291
        // check if it's a redirection
292
        if (false !== $page->getRedirection()) {
293
            $this->addRedirection($page);
294
295
            return;
296
        }
297
298
        $dump = $this->render($page);
299
        $this->filesystem->dumpFile($this->getFilePath($page), $dump);
300
    }
301
302
    protected function getFilePath(Page $page)
303
    {
304
        $slug = '' == $page->getRealSlug() ? 'index' : $page->getRealSlug();
305
        $route = $this->pageCanonical->generatePathForPage($slug);
306
307
        return $this->staticDir.$route.'.html';
308
    }
309
310
    /**
311
     * Generate static file for feed indexing children pages
312
     * (only if children pages exists).
313
     *
314
     * @return void
315
     */
316
    protected function generateFeedFor(Page $page)
317
    {
318
        if ($page->getChildrenPages()->count() > 0) {
319
            $dump = $this->renderFeed($page);
320
            $this->filesystem->dumpFile(preg_replace('/.html$/', '.xml', $this->getFilePath($page)), $dump);
321
        }
322
    }
323
324
    protected function generateErrorPages(): void
325
    {
326
        $this->generateErrorPage();
327
328
        // todo i18n error in .htaccess
329
        $locales = explode('|', $this->params->get('pwc.locales'));
330
331
        foreach ($locales as $locale) {
332
            $this->filesystem->mkdir($this->staticDir.'/'.$locale);
333
            $this->generateErrorPage($locale);
334
        }
335
    }
336
337
    protected function generateErrorPage($locale = null, $uri = '404.html')
338
    {
339
        if (null !== $locale) {
340
            $request = new Request();
341
            $request->setLocale($locale);
342
            $this->requesStack->push($request);
343
        }
344
345
        $dump = $this->parser->compress($this->twig->render('@Twig/Exception/error.html.twig'));
346
        $this->filesystem->dumpFile($this->staticDir.(null !== $locale ? '/'.$locale : '').'/'.$uri, $dump);
347
    }
348
349
    protected function getPageRepository(): PageRepository
350
    {
351
        return $this->em->getRepository($this->params->get('pwc.entity_page'));
352
    }
353
354
    protected function getPages()
355
    {
356
        $query = $this->getPageRepository()->getQueryToFindPublished('p');
357
358
        return $query->getQuery()->getResult();
359
    }
360
361
    protected function render(Page $page): string
362
    {
363
        $template = $this->params->get('pwc.default_page_template');
364
365
        return $this->parser->compress($this->twig->render($template, ['page' => $page]));
366
    }
367
368
    // todo i18n feed ...
369
    protected function renderFeed(Page $page): string
370
    {
371
        $template = '@PiedWebCMS/page/rss.xml.twig';
372
373
        return $this->parser->compress($this->twig->render($template, ['page' => $page]));
374
    }
375
}
376