Passed
Push — master ( 501fc3...f64e27 )
by Paweł
47:53
created

ThemeGenerateCommand::writeConfigFile()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 38
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 38
ccs 0
cts 0
cp 0
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 18
nc 1
nop 5
crap 2
1
<?php
2
3
/*
4
 * This file is part of the Superdesk Web Publisher Template Engine Bundle.
5
 *
6
 * Copyright 2016 Sourcefabric z.ú. and contributors.
7
 *
8
 * For the full copyright and license information, please see the
9
 * AUTHORS and LICENSE files distributed with this source code.
10
 *
11
 * @copyright 2016 Sourcefabric z.ú
12
 * @license http://www.superdesk.org/license
13
 */
14
15
namespace SWP\Bundle\CoreBundle\Command;
16
17
use SWP\Bundle\CoreBundle\Document\Tenant;
18
use SWP\Component\MultiTenancy\Exception\OrganizationNotFoundException;
19
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
20
use Symfony\Component\Console\Input\InputArgument;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Question\ChoiceQuestion;
24
use Symfony\Component\Console\Question\ConfirmationQuestion;
25
use Symfony\Component\Console\Question\Question;
26
use Symfony\Component\Filesystem\Filesystem;
27
28
class ThemeGenerateCommand extends ContainerAwareCommand
29
{
30
    const HOME_TWIG = 'home.html.twig';
31
32
    /**
33
     * {@inheritdoc}
34
     */
35 84
    protected function configure()
36
    {
37
        $this
38 84
            ->setName('theme:generate')
39 84
            ->setDescription('Creates basic theme structure with routes and empty templates.')
40 84
            ->addArgument(
41 84
                'organizationName',
42 84
                InputArgument::REQUIRED,
43 84
                'Organization Name',
44 84
                null
45
            )
46 84
            ->addArgument(
47 84
                'themeName',
48 84
                InputArgument::REQUIRED,
49 84
                'Theme Name',
50 84
                null
51
            )
52 84
            ->setHelp(
53 84
                'The <info>%command.name%</info> command creates a skeleton theme in your application themes folder (app/themes)'
54
            );
55 84
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60 1
    protected function execute(InputInterface $input, OutputInterface $output)
61
    {
62 1
        $themeName = $input->getArgument('themeName');
63
        try {
64 1
            $tenant = $this->getTenant($input, $output);
65
            if (null === $tenant) {
66
                return;
67
            }
68
69
            $themeDir = $this->createSkeleton(new Filesystem(), $tenant->getCode(), $themeName);
70
            $this->writeConfigFile($input, $output, $tenant, $themeDir, $themeName);
71
            $this->updateTenantReferenceToTheme($tenant, $themeName);
72
            $output->writeln('Theme '.$themeName.' has been generated successfully!');
73 1
        } catch (\Exception $e) {
74 1
            $output->writeln('Theme '.$themeName.' could not be generated!');
75 1
            $output->writeln($e->getMessage());
76
        }
77 1
    }
78
79
    /**
80
     * Gets the tenant based on the input - prompts a user to choose an existing tenant of the given organisation, or to create a new one.
81
     *
82
     * @param InputInterface  $input  An InputInterface instance
83
     * @param OutputInterface $output An OutputInterface instance
84
     *
85
     * @return mixed|null
86
     *
87
     * @throws \Exception
88
     */
89 1
    protected function getTenant(InputInterface $input, OutputInterface $output)
90
    {
91 1
        $organizationRepository = $this->getContainer()->get('swp.repository.organization');
92 1
        $organizationName = $input->getArgument('organizationName');
93 1
        $organization = $organizationRepository->findOneByName($organizationName);
94 1
        if (null === $organization) {
95 1
            throw new OrganizationNotFoundException($organizationName);
96
        }
97
98
        $helper = $this->getHelper('question');
99
        $question = new ConfirmationQuestion('Create new tenant?', false, '/^(y|j)/i');
100
        if (!$helper->ask($input, $output, $question)) {
101
            $tenants = $organization->getTenants()->toArray();
102
            $numTenants = count($tenants);
103
            if (!$numTenants) {
104
                throw new \Exception('Organization has no tenants');
105
            }
106
107
            $tenant = reset($tenants);
108
            if ($numTenants > 1) {
109
                $tenantNames = array_map(function ($tenant) {
110
                    return $tenant->getName();
111
                }, $tenants);
112
113
                $tenants = array_combine($tenantNames, $tenants);
114
115
                $question = new ChoiceQuestion(
116
                    'Please select a tenant',
117
                    $tenantNames,
118
                    $tenantNames[0]
119
                );
120
                $question->setErrorMessage('Name %s is not an option');
121
122
                $tenantName = $helper->ask($input, $output, $question);
123
                $tenant = $tenants[$tenantName];
124
            }
125
126
            return $tenant;
127
        } else {
128
            $output->writeln('Creation of tenant here still to be implemented - you can create a tenant using the swp:tenant:create command');
129
        }
130
    }
131
132
    /**
133
     * @param Filesystem $fileSystem
134
     * @param            $tenantCode
135
     * @param            $themeName
136
     *
137
     * @return string
138
     *
139
     * @throws \Exception
140
     */
141
    protected function createSkeleton(Filesystem $fileSystem, $tenantCode, $themeName)
142
    {
143
        $configFilename = $this->getContainer()->getParameter('swp.theme.configuration.filename');
144
        $themesDir = $this->getContainer()->getParameter('swp.theme.configuration.default_directory');
145
146
        $paths = [
147
            'phone/views/'.self::HOME_TWIG,
148
            'tablet/views/'.self::HOME_TWIG,
149
            'views/index.html.twig',
150
            'translations/messages.en.xlf',
151
            'translations/messages.de.xlf',
152
            'public/css',
153
            'public/js',
154
            'public/images',
155
            $configFilename,
156
        ];
157
158
        $themeDir = implode(\DIRECTORY_SEPARATOR, [$themesDir, $tenantCode, $themeName]);
159
        if ($fileSystem->exists($themeDir)) {
160
            throw new \Exception('Theme '.$themeName.' already exists!');
161
        }
162
163
        $this->makePath($fileSystem, $themesDir, [$tenantCode, $themeName]);
164
165
        foreach ($paths as $path) {
166
            $elements = explode(\DIRECTORY_SEPARATOR, $path);
167
168
            $file = null;
169
            if (strpos(end($elements), '.')) {
170
                $file = array_pop($elements);
171
            }
172
173
            $path = $this->makePath($fileSystem, $themeDir, $elements);
174
            if (null !== $file) {
175
                $this->makeFile($fileSystem, $path, $file);
176
            }
177
        }
178
179
        return $themeDir;
180
    }
181
182
    /**
183
     * @param InputInterface  $input
184
     * @param OutputInterface $output
185
     * @param Tenant          $tenant
186
     * @param                 $themeDir
187
     * @param                 $themeName
188
     */
189
    protected function writeConfigFile(InputInterface $input, OutputInterface $output, Tenant $tenant, $themeDir, $themeName)
190
    {
191
        $output->writeln('To generate config file, please provide a few values.');
192
193
        $values = $this->getValuesFromUser($input,
194
            $output,
195
            [
196
                'title' => $themeName,
197
                'description' => $tenant->getOrganization()->getName().' '.$themeName.' theme',
198
                'author name' => 'anon',
199
                'author email' => 'anon',
200
                'author homepage' => 'homepage',
201
                'author role' => 'anon',
202
            ]
203
        );
204
        array_unshift($values, sprintf('swp/%s', $themeName));
205
206
        $contents =
207
<<<'EOT'
208
{
209
    "name": "%s",
210
    "title": "%s",
211
    "description": "%s",
212
    "authors": [
213
        {
214
            "name": "%s",
215
            "email": "%s",
216
            "homepage": "%s",
217
            "role": "%s"
218
        }
219
    ]
220
}
221
EOT;
222
        $contents = vsprintf($contents, $values);
223
        $configFilename = $this->getContainer()->getParameter('swp.theme.configuration.filename');
224
225
        file_put_contents($themeDir.\DIRECTORY_SEPARATOR.$configFilename, $contents);
226
    }
227
228
    /**
229
     * @param InputInterface  $input
230
     * @param OutputInterface $output
231
     * @param array           $keysAndDefaults
232
     *
233
     * @return array
234
     */
235
    protected function getValuesFromUser(InputInterface $input, OutputInterface $output, array $keysAndDefaults)
236
    {
237
        $results = [];
238
        $helper = $this->getHelper('question');
239
        foreach ($keysAndDefaults as $key => $default) {
240
            $question = new Question($key.': ', $default);
241
            $results[$key] = $helper->ask($input, $output, $question);
242
        }
243
244
        return $results;
245
    }
246
247
    /**
248
     * @param Tenant $tenant
249
     * @param $themeName
250
     */
251
    protected function updateTenantReferenceToTheme(Tenant $tenant, $themeName)
252
    {
253
        $tenant->setThemeName(sprintf('swp/%s', $themeName));
254
        $documentManager = $this->getContainer()->get('doctrine_phpcr.odm.document_manager');
255
        $documentManager->flush();
256
    }
257
258
    /**
259
     * @param Filesystem $fileSystem
260
     * @param $baseDir
261
     * @param array $subDirs
262
     *
263
     * @return string
264
     */
265
    protected function makePath(Filesystem $fileSystem, $baseDir, array $subDirs)
266
    {
267
        $path = $baseDir;
268
        foreach ($subDirs as $dir) {
269
            $path .= \DIRECTORY_SEPARATOR.$dir;
270
            $fileSystem->mkdir($path);
271
        }
272
273
        return $path;
274
    }
275
276
    /**
277
     * @param Filesystem $filesystem
278
     * @param $baseDir
279
     * @param $fileName
280
     *
281
     * @return string
282
     */
283
    protected function makeFile(FileSystem $filesystem, $baseDir, $fileName)
284
    {
285
        $fileName = $baseDir.\DIRECTORY_SEPARATOR.$fileName;
286
        $filesystem->touch($fileName);
287
288
        return $fileName;
289
    }
290
}
291