Completed
Push — master ( b34843...6180fe )
by Morris
41:44 queued 25:33
created

GenerateCommand::completeArgumentValues()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 2
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2017 Joas Schilling <[email protected]>
4
 * @copyright Copyright (c) 2017, ownCloud GmbH
5
 *
6
 * @author Joas Schilling <[email protected]>
7
 *
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OC\Core\Command\Db\Migrations;
25
26
27
use OC\DB\MigrationService;
28
use OC\Migration\ConsoleOutput;
29
use OCP\App\IAppManager;
30
use OCP\IDBConnection;
31
use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareInterface;
32
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
33
use Symfony\Component\Console\Command\Command;
34
use Symfony\Component\Console\Exception\RuntimeException;
35
use Symfony\Component\Console\Input\InputArgument;
36
use Symfony\Component\Console\Input\InputInterface;
37
use Symfony\Component\Console\Output\OutputInterface;
38
39
class GenerateCommand extends Command implements CompletionAwareInterface {
40
41
	protected static $_templateSimple =
42
		'<?php
43
namespace {{namespace}};
44
45
use OCP\DB\ISchemaWrapper;
46
use OCP\Migration\SimpleMigrationStep;
47
use OCP\Migration\IOutput;
48
49
/**
50
 * Auto-generated migration step: Please modify to your needs!
51
 */
52
class {{classname}} extends SimpleMigrationStep {
53
54
	/**
55
	 * @param IOutput $output
56
	 * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
57
	 * @param array $options
58
	 * @since 13.0.0
59
	 */
60
	public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
61
	}
62
63
	/**
64
	 * @param IOutput $output
65
	 * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
66
	 * @param array $options
67
	 * @return null|ISchemaWrapper
68
	 * @since 13.0.0
69
	 */
70
	public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
71
{{schemabody}}
72
	}
73
74
	/**
75
	 * @param IOutput $output
76
	 * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
77
	 * @param array $options
78
	 * @since 13.0.0
79
	 */
80
	public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
81
	}
82
}
83
';
84
85
	/** @var IDBConnection */
86
	protected $connection;
87
88
	/** @var IAppManager */
89
	protected $appManager;
90
91
	/**
92
	 * @param IDBConnection $connection
93
	 * @param IAppManager $appManager
94
	 */
95
	public function __construct(IDBConnection $connection, IAppManager $appManager) {
96
		$this->connection = $connection;
97
		$this->appManager = $appManager;
98
99
		parent::__construct();
100
	}
101
102
	protected function configure() {
103
		$this
104
			->setName('migrations:generate')
105
			->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on')
106
			->addArgument('version', InputArgument::REQUIRED, 'Major version of this app, to allow versions on parallel development branches')
107
		;
108
109
		parent::configure();
110
	}
111
112
	public function execute(InputInterface $input, OutputInterface $output) {
113
		$appName = $input->getArgument('app');
114
		$version = $input->getArgument('version');
115
116
		if (!preg_match('/^\d{1,16}$/',$version)) {
117
			$output->writeln('<error>The given version is invalid. Only 0-9 are allowed (max. 16 digits)</error>');
118
			return 1;
119
		}
120
121
		$ms = new MigrationService($appName, $this->connection, new ConsoleOutput($output));
122
123
		$date = date('YmdHis');
124
		$path = $this->generateMigration($ms, 'Version' . $version . 'Date' . $date);
125
126
		$output->writeln("New migration class has been generated to <info>$path</info>");
127
		return 0;
128
	}
129
130
	/**
131
	 * @param string $optionName
132
	 * @param CompletionContext $context
133
	 * @return string[]
134
	 */
135
	public function completeOptionValues($optionName, CompletionContext $context) {
136
		return [];
137
	}
138
139
	/**
140
	 * @param string $argumentName
141
	 * @param CompletionContext $context
142
	 * @return string[]
143
	 */
144
	public function completeArgumentValues($argumentName, CompletionContext $context) {
145
		if ($argumentName === 'app') {
146
			$allApps = \OC_App::getAllApps();
147
			return array_diff($allApps, \OC_App::getEnabledApps(true, true));
148
		}
149
150
		if ($argumentName === 'version') {
151
			$appName = $context->getWordAtIndex($context->getWordIndex() - 1);
152
153
			$version = explode('.', $this->appManager->getAppVersion($appName));
154
			return [$version[0] . sprintf('%1$03d', $version[1])];
155
		}
156
157
		return [];
158
	}
159
160
	/**
161
	 * @param MigrationService $ms
162
	 * @param string $className
163
	 * @param string $schemaBody
164
	 * @return string
165
	 */
166
	protected function generateMigration(MigrationService $ms, $className, $schemaBody = '') {
167
		if ($schemaBody === '') {
168
			$schemaBody = "\t\t" . 'return null;';
169
		}
170
171
172
		$placeHolders = [
173
			'{{namespace}}',
174
			'{{classname}}',
175
			'{{schemabody}}',
176
		];
177
		$replacements = [
178
			$ms->getMigrationsNamespace(),
179
			$className,
180
			$schemaBody,
181
		];
182
		$code = str_replace($placeHolders, $replacements, self::$_templateSimple);
183
		$dir = $ms->getMigrationsDirectory();
184
185
		$this->ensureMigrationDirExists($dir);
186
		$path = $dir . '/' . $className . '.php';
187
188
		if (file_put_contents($path, $code) === false) {
189
			throw new RuntimeException('Failed to generate new migration step.');
190
		}
191
192
		return $path;
193
	}
194
195
	protected function ensureMigrationDirExists($directory) {
196
		if (file_exists($directory) && is_dir($directory)) {
197
			return;
198
		}
199
200
		if (file_exists($directory)) {
201
			throw new \RuntimeException("Could not create folder \"$directory\"");
202
		}
203
204
		$this->ensureMigrationDirExists(dirname($directory));
205
206
		if (!@mkdir($directory) && !is_dir($directory)) {
207
			throw new \RuntimeException("Could not create folder \"$directory\"");
208
		}
209
	}
210
}
211