Passed
Push — master ( bf69e0...103a68 )
by Dev
12:41
created

StaticService::generatePages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
ccs 0
cts 4
cp 0
crap 6
rs 10
c 0
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;
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();
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
        if ($this->params->get('pwc.static.generateForApache')) {
125
            $this->generateHtaccess();
126
            $symlink = $this->params->get('pwc.static.symlinkMedia');
127
        } else { //if ($this->params->has('pwc.static.generateForGithubPages')) {
128
            // symlink doesn't work on github page.
129
            $symlink = false;
130
            $this->generateCname();
131
        }
132
133
        $this->generatePages();
134
        $this->generateErrorPages();
135
        $this->copyRobotsFiles();
136
        $this->copyAssets($symlink);
137
        $this->copyMediaToDownload($symlink);
138
    }
139
140
    /**
141
     * Copy files relative to SEO (robots, sitemaps, etc.).
142
     */
143
    protected function copyRobotsFiles(): void
144
    {
145
        array_map([$this, 'copy'], $this->robotsFiles);
146
    }
147
148
    // todo
149
    // docs
150
    // https://help.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site
151
    protected function generateCname()
152
    {
153
        $this->filesystem->dumpFile($this->staticDir.'/CNAME', $this->params->get('pwc.static.domain'));
154
    }
155
156
    protected function generateHtaccess()
157
    {
158
        if (!$this->params->has('pwc.static.domain')) {
159
            throw new \Exception('Before, you need to configure (in config/packages/piedweb_cms.yaml) static_domain.');
160
        }
161
162
        $htaccess = $this->twig->render('@PiedWebCMS/static/htaccess.twig', [
163
            'domain' => $this->params->get('pwc.static.domain'),
164
            'redirections' => $this->redirections,
165
        ]);
166
        $this->filesystem->dumpFile($this->staticDir.'/.htaccess', $htaccess);
167
    }
168
169
    protected function copy(string $file): void
170
    {
171
        if (file_exists($file)) {
172
            copy(
173
                str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$file),
174
                $this->staticDir.'/'.$file
175
            );
176
        }
177
    }
178
179
    /**
180
     * Copy (or symlink) for all assets in public
181
     * (and media previously generated by liip in public).
182
     */
183
    protected function copyAssets(bool $symlink = true): void
184
    {
185
        $dir = dir($this->webDir);
186
        while (false !== $entry = $dir->read()) {
187
            if ('.' == $entry || '..' == $entry) {
188
                continue;
189
            }
190
            if (!in_array($entry, $this->robotsFiles) && !in_array($entry, $this->dontCopy)) {
191
                //$this->symlink(
192
                if (true === $symlink) {
193
                    $this->filesystem->symlink(
194
                        str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$entry),
195
                        $this->staticDir.'/'.$entry
196
                    );
197
                } else {
198
                    $action = is_file($this->webDir.'/'.$entry) ? 'copy' : 'mirror';
199
                    $this->filesystem->$action($this->webDir.'/'.$entry, $this->staticDir.'/'.$entry);
200
                }
201
            }
202
        }
203
        $dir->close();
204
    }
205
206
    /**
207
     * Copy or Symlink "not image" media to download folder.
208
     *
209
     * @return void
210
     */
211
    protected function copyMediaToDownload(bool $symlink = true)
212
    {
213
        if (!file_exists($this->staticDir.'/download')) {
214
            $this->filesystem->mkdir($this->staticDir.'/download/');
215
            $this->filesystem->mkdir($this->staticDir.'/download/media');
216
        }
217
218
        $dir = dir($this->webDir.'/../media');
219
        while (false !== $entry = $dir->read()) {
220
            if ('.' == $entry || '..' == $entry) {
221
                continue;
222
            }
223
            // if the file is an image, it's ever exist (maybe it's slow to check every files)
224
            if (!file_exists($this->webDir.'/media/default/'.$entry)) {
225
                if (true === $symlink) {
226
                    $this->filesystem->symlink('../../../media/'.$entry, $this->staticDir.'/download/media/'.$entry);
227
                } else {
228
                    $this->filesystem->copy(
229
                        $this->webDir.'/../media/'.$entry,
230
                        $this->staticDir.'/download/media/'.$entry
231
                    );
232
                }
233
            }
234
        }
235
236
        //$this->filesystem->$action($this->webDir.'/../media', $this->staticDir.'/download/media');
237
    }
238
239
    protected function generatePages(): void
240
    {
241
        $pages = $this->getPages();
242
243
        foreach ($pages as $page) {
244
            $this->generatePage($page);
245
            $this->generateFeedFor($page);
246
        }
247
    }
248
249
    /**
250
     * The function cache redirection found during generatePages and
251
     * format in self::$redirection the content for the .htaccess.
252
     *
253
     * @return void
254
     */
255
    protected function addRedirection(Page $page)
256
    {
257
        $this->redirections .= 'Redirect ';
258
        $this->redirections .= $page->getRedirectionCode().' ';
0 ignored issues
show
Bug introduced by
The method getRedirectionCode() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

258
        $this->redirections .= $page->/** @scrutinizer ignore-call */ getRedirectionCode().' ';
Loading history...
259
        $this->redirections .= $this->pageCanonical->generatePathForPage($page->getRealSlug());
0 ignored issues
show
Bug introduced by
The method getRealSlug() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

259
        $this->redirections .= $this->pageCanonical->generatePathForPage($page->/** @scrutinizer ignore-call */ getRealSlug());
Loading history...
260
        $this->redirections .= ' '.$page->getRedirection();
0 ignored issues
show
Bug introduced by
The method getRedirection() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

260
        $this->redirections .= ' '.$page->/** @scrutinizer ignore-call */ getRedirection();
Loading history...
261
            $this->redirections .= PHP_EOL;
262
    }
263
264
    public function generatePage(Page $page)
265
    {
266
        // set current locale to avoid twig error
267
        $request = new Request();
268
        $request->setLocale($page->getLocale());
0 ignored issues
show
Bug introduced by
The method getLocale() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

268
        $request->setLocale($page->/** @scrutinizer ignore-call */ getLocale());
Loading history...
269
            $this->requesStack->push($request);
270
271
        //$this->translator->setLocale($page->getLocale());
272
273
        // check if it's a redirection
274
        if (false !== $page->getRedirection()) {
275
            $this->addRedirection($page);
276
            return;
277
        }
278
279
        $dump = $this->render($page);
280
        $this->filesystem->dumpFile($this->getFilePath($page), $dump);
281
    }
282
283
    protected function getFilePath(Page $page)
284
    {
285
        $slug = '' == $page->getRealSlug() ? 'index' : $page->getRealSlug();
286
        $route = $this->pageCanonical->generatePathForPage($slug);
287
288
        return $this->staticDir.$route.'.html';
289
    }
290
291
    /**
292
     * Generate static file for feed indexing children pages
293
     * (only if children pages exists).
294
     *
295
     * @return void
296
     */
297
    protected function generateFeedFor(Page $page)
298
    {
299
        if ($page->getChildrenPages()->count() > 0) {
0 ignored issues
show
Bug introduced by
The method getChildrenPages() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

299
        if ($page->/** @scrutinizer ignore-call */ getChildrenPages()->count() > 0) {
Loading history...
300
            $dump = $this->renderFeed($page);
301
            $this->filesystem->dumpFile(preg_replace('/.html$/', '.xml', $this->getFilePath($page)), $dump);
302
        }
303
    }
304
305
    protected function generateErrorPages(): void
306
    {
307
        $this->generateErrorPage();
308
309
        // todo i18n error in .htaccess
310
        $locales = explode('|', $this->params->get('pwc.locales'));
311
312
        foreach ($locales as $locale) {
313
            $this->filesystem->mkdir($this->staticDir.'/'.$locale);
314
            $this->generateErrorPage($locale);
315
        }
316
    }
317
318
    protected function generateErrorPage($locale = null, $uri = '404.html')
319
    {
320
        if (null !== $locale) {
321
            $request = new Request();
322
            $request->setLocale($locale);
323
            $this->requesStack->push($request);
324
        }
325
326
        $dump = $this->parser->compress($this->twig->render('@Twig/Exception/error.html.twig'));
327
        $this->filesystem->dumpFile($this->staticDir.(null !== $locale ? '/'.$locale : '').'/'.$uri, $dump);
328
    }
329
330
    protected function getPageRepository() :PageRepository
331
    {
332
        return $this->em->getRepository($this->params->get('pwc.entity_page'));
333
    }
334
335
    protected function getPages()
336
    {
337
        $query = $this->getPageRepository()->getQueryToFindPublished('p');
338
339
        return $query->getQuery()->getResult();
340
    }
341
342
    protected function render(Page $page): string
343
    {
344
        $template = $this->params->get('pwc.default_page_template');
345
346
        return $this->parser->compress($this->twig->render($template, ['page' => $page]));
347
    }
348
349
    // todo i18n feed ...
350
    protected function renderFeed(Page $page): string
351
    {
352
        $template = '@PiedWebCMS/page/rss.xml.twig';
353
354
        return $this->parser->compress($this->twig->render($template, ['page' => $page]));
355
    }
356
}
357