Completed
Push — master ( 5b1e94...2b87f9 )
by Gaetano
09:14
created

GenerateCommand::getLimitationValues()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Command;
4
5
use Symfony\Component\Console\Input\InputArgument;
6
use Symfony\Component\Console\Input\InputInterface;
7
use Symfony\Component\Console\Input\InputOption;
8
use Symfony\Component\Console\Output\OutputInterface;
9
use Symfony\Component\HttpFoundation\File\Exception\FileException;
10
use Symfony\Component\Yaml\Yaml;
11
use eZ\Publish\API\Repository\Values\User\Limitation;
12
use Kaliop\eZMigrationBundle\Core\Helper\RoleHandler;
13
14
class GenerateCommand extends AbstractCommand
15
{
16
    const ADMIN_USER_ID = 14;
17
    const DIR_CREATE_PERMISSIONS = 0755;
18
19
    private $availableMigrationFormats = array('yml', 'php', 'sql');
20
    private $availableMigrationTypes = array('generic', 'role', 'db', 'php');
21
    private $thisBundle = 'EzMigrationBundle';
22
23
    /**
24
     * Configure the console command
25
     */
26
    protected function configure()
27
    {
28
        $this->setName('kaliop:migration:generate')
29
            ->setDescription('Generate a blank migration definition file.')
30
            ->addOption('format', null, InputOption::VALUE_REQUIRED, 'The format of migration file to generate (yml, php, sql)', 'yml')
31
            ->addOption('type', null, InputOption::VALUE_REQUIRED, 'The type of migration to generate (role, generic, db, php)', '')
32
            ->addOption('dbserver', null, InputOption::VALUE_REQUIRED, 'The type of the database server the sql migration is for, for type=db (mysql, postgresql, ...)', 'mysql')
33
            ->addOption('role', null, InputOption::VALUE_REQUIRED, 'The role identifier you would like to update, for type=role', null)
34
            ->addArgument('bundle', InputArgument::REQUIRED, 'The bundle to generate the migration definition file in. eg.: AcmeMigrationBundle')
35
            ->addArgument('name', InputArgument::OPTIONAL, 'The migration name (will be prefixed with current date)', null)
36
            ->setHelp(<<<EOT
37
The <info>kaliop:migration:generate</info> command generates a skeleton migration definition file:
38
39
    <info>./ezpublish/console kaliop:migration:generate bundlename</info>
40
41
You can optionally specify the file type to generate with <info>--format</info>:
42
43
    <info>./ezpublish/console kaliop:migration:generate --format=yml bundlename migrationname</info>
44
45
For SQL type migration you can optionally specify the database server type the migration is for with <info>--dbserver</info>:
46
47
    <info>./ezpublish/console kaliop:migration:generate --format=sql bundlename migrationname</info>
48
49
For role type migration you will receive a yaml file with the current role definition. You must define ALL the policies you wish for the role. Any not defined will be removed.
50
51
    <info>./ezpublish/console kaliop:migration:generate --role=Anonymous bundlename migrationname
52
53
For freeform php migrations, you will receive a php class definition
54
55
    <info>./ezpublish/console kaliop:migration:generate --format=php bundlename classname</info>
56
57
EOT
58
            );
59
    }
60
61
    /**
62
     * Run the command and display the results.
63
     *
64
     * @param InputInterface $input
65
     * @param OutputInterface $output
66
     * @return null|int null or 0 if everything went fine, or an error code
67
     * @throws \InvalidArgumentException When an unsupported file type is selected
68
     */
69
    public function execute(InputInterface $input, OutputInterface $output)
70
    {
71
        $bundleName = $input->getArgument('bundle');
72
        $name = $input->getArgument('name');
73
        $fileType = $input->getOption('format');
74
        $migrationType = $input->getOption('type');
75
        $role = $input->getOption('role');
76
        $dbServer = $input->getOption('dbserver');
77
78
        if ($bundleName == $this->thisBundle) {
79
            throw new \InvalidArgumentException("It is not allowed to create migrations in bundle '$bundleName'");
80
        }
81
82
        $activeBundles = array();
83
        foreach($this->getApplication()->getKernel()->getBundles() as $bundle)
84
        {
85
            $activeBundles[] = $bundle->getName();
86
        }
87
        arsort($activeBundles);
88
        if (!in_array($bundleName, $activeBundles)) {
89
            throw new \InvalidArgumentException("Bundle '$bundleName' does not exist or it is not enabled. Try with one of: " . implode(', ', $activeBundles));
90
        }
91
92
        $bundle = $this->getApplication()->getKernel()->getBundle($bundleName);
93
        $migrationDirectory = $bundle->getPath() . '/' . $this->getContainer()->getParameter('kaliop_bundle_migration.version_directory');
94
95
        // be kind to lazy users
96
        if ($migrationType == '') {
97
            if ($fileType == 'sql') {
98
                $migrationType = 'db';
99
            } elseif ($fileType == 'php') {
100
                $migrationType = 'php';
101
            } elseif ($role != '') {
102
                $migrationType = 'role';
103
            } else {
104
                $migrationType = 'generic';
105
            }
106
        }
107
108
        if (!in_array($fileType, $this->availableMigrationFormats)) {
109
            throw new \InvalidArgumentException('Unsupported migration file format ' . $fileType);
110
        }
111
112
        if (!in_array($migrationType, $this->availableMigrationTypes)) {
113
            throw new \InvalidArgumentException('Unsupported migration type ' . $fileType);
114
        }
115
116
        if (!is_dir($migrationDirectory)) {
117
            $output->writeln(sprintf(
118
                "Migrations directory <info>%s</info> does not exist. I will create it now....",
119
                $migrationDirectory
120
            ));
121
122
            if (mkdir($migrationDirectory, self::DIR_CREATE_PERMISSIONS, true)) {
123
                $output->writeln(sprintf(
124
                    "Migrations directory <info>%s</info> has been created",
125
                    $migrationDirectory
126
                ));
127
            } else {
128
                throw new FileException(sprintf(
129
                    "Failed to create migrations directory %s.",
130
                    $migrationDirectory
131
                ));
132
            }
133
        }
134
135
        $parameters = array(
136
            'dbserver' => $dbServer,
137
            'role' => $role,
138
        );
139
140
        $date = date('YmdHis');
141
142
        switch ($fileType) {
143
            case 'sql':
144
                /// @todo this logic should come from the DefinitionParser, really
145
                if ($name != '') {
146
                    $name = '_' . ltrim($name, '_');
147
                }
148
                $fileName = $date . '_' . $dbServer . $name . '.sql';
149
                break;
150
151
            case 'php':
152
                /// @todo this logic should come from the DefinitionParser, really
153
                $className = ltrim($name, '_');
154
                if ($className == '') {
155
                    $className = 'Migration';
156
                }
157
                // Make sure that php class names are unique, not only migration definition file names
158
                $existingMigrations = count(glob($migrationDirectory.'/*_' . $className .'*.php'));
159
                if ($existingMigrations) {
160
                    $className = $className . sprintf('%03d', $existingMigrations + 1);
161
                }
162
                $parameters = array_merge($parameters, array(
163
                    'namespace' => $bundle->getNamespace(),
164
                    'class_name' => $className
165
                ));
166
                $fileName = $date . '_' . $className . '.php';
167
                break;
168
169
            default:
170
                if ($name == '') {
171
                    $name = 'placeholder';
172
                }
173
                $fileName = $date . '_' . $name . '.yml';
174
        }
175
176
        $path = $migrationDirectory . '/' . $fileName;
177
178
        $this->generateMigrationFile($path, $fileType, $migrationType, $parameters);
179
180
        $output->writeln(sprintf("Generated new migration file: <info>%s</info>", $path));
181
    }
182
183
    /**
184
     * Generates a migration definition file.
185
     *
186
     * @param string $path filename to file to generate (full path)
187
     * @param string $fileType The type of migration file to generate
188
     * @param string $migrationType The type of migration to generate
189
     * @param array $parameters passed on to twig
190
     * @return string The path to the migration file
191
     * @throws \Exception
192
     */
193
    protected function generateMigrationFile($path, $fileType, $migrationType, array $parameters = array())
194
    {
195
        $template = $migrationType . 'Migration.' . $fileType . '.twig';
196
        $templatePath = $this->getApplication()->getKernel()->getBundle($this->thisBundle)->getPath() . '/Resources/views/MigrationTemplate/';
197
        if (!is_file($templatePath . $template)) {
198
            throw new \Exception("The combination of migration type '$migrationType' is not supported with format '$fileType'");
199
        }
200
201
        if ($migrationType == 'role') {
202
            $code = $this->generateRoleTemplate($parameters['role']);
203
        } else {
204
            $code = $this->getContainer()->get('twig')->render($this->thisBundle . ':MigrationTemplate:'.$template, $parameters);
205
        }
206
207
        file_put_contents($path, $code);
208
    }
209
210
    /**
211
     * @todo to be moved into the Executor/RoleManager class
212
     *
213
     * @param string$roleName
214
     * @return string
215
     */
216
    protected function generateRoleTemplate($roleName)
217
    {
218
        /** @var $container \Symfony\Component\DependencyInjection\ContainerInterface */
219
        $container = $this->getApplication()->getKernel()->getContainer();
220
        $repository = $container->get('ezpublish.api.repository');
221
        $repository->setCurrentUser($repository->getUserService()->loadUser(self::ADMIN_USER_ID));
222
223
        /** @var \eZ\Publish\Core\SignalSlot\RoleService $roleService */
224
        $roleService = $repository->getRoleService();
225
226
        /** @var RoleHandler $roleTranslationHandler */
227
        $roleTranslationHandler = $container->get('ez_migration_bundle.helper.role_handler');
228
229
        /** @var \eZ\Publish\API\Repository\Values\User\Role $role */
230
        $role = $roleService->loadRoleByIdentifier($roleName);
231
232
        $policies = array();
233
        /** @var \eZ\Publish\API\Repository\Values\User\Policy $policy */
234
        foreach ($role->getPolicies() as $policy)
235
        {
236
            $limitations = array();
237
238
            /** @var \eZ\Publish\API\Repository\Values\User\Limitation $limitation */
239
            foreach ($policy->getLimitations() as $limitation)
240
            {
241
                $limitations[] = $roleTranslationHandler->limitationWithIdentifiers($limitation);
242
            }
243
244
            $policies[] = array(
245
                'module' => $policy->module,
246
                'function' => $policy->function,
247
                'limitations' => $limitations
248
            );
249
        }
250
251
        $ymlArray = array(
252
            'mode' => 'update',
253
            'type' => 'role',
254
            'name' => $roleName,
255
            'policies' => $policies
256
        );
257
258
        return Yaml::dump(array($ymlArray), 5);
259
    }
260
}
261