Skip to content

Commit 86fe095

Browse files
committed
feat: add Config\Email setup
1 parent 70e49c5 commit 86fe095

2 files changed

Lines changed: 150 additions & 29 deletions

File tree

src/Commands/Setup.php

Lines changed: 120 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@
66

77
use CodeIgniter\CLI\BaseCommand;
88
use CodeIgniter\CLI\CLI;
9+
use CodeIgniter\CodeIgniter;
910
use CodeIgniter\Commands\Database\Migrate;
1011
use CodeIgniter\Shield\Commands\Setup\ContentReplacer;
12+
use CodeIgniter\Shield\Commands\Utils\InputOutput;
13+
use CodeIgniter\Test\Filters\CITestStreamFilter;
14+
use Config\Email as EmailConfig;
1115
use Config\Services;
1216

1317
class Setup extends BaseCommand
1418
{
19+
private static ?InputOutput $io = null;
20+
1521
/**
1622
* The group the command is lumped under
1723
* when listing commands.
@@ -89,6 +95,7 @@ private function publishConfig(): void
8995
$this->setupRoutes();
9096

9197
$this->setSecurityCSRF();
98+
$this->setupEmail();
9299

93100
$this->runMigrations();
94101
}
@@ -166,7 +173,7 @@ protected function writeFile(string $file, string $content): void
166173

167174
if (
168175
! $overwrite
169-
&& CLI::prompt(" File '{$cleanPath}' already exists in destination. Overwrite?", ['n', 'y']) === 'n'
176+
&& $this->prompt(" File '{$cleanPath}' already exists in destination. Overwrite?", ['n', 'y']) === 'n'
170177
) {
171178
CLI::error(" Skipped {$cleanPath}. If you wish to overwrite, please use the '-f' option or reply 'y' to the prompt.");
172179

@@ -175,7 +182,7 @@ protected function writeFile(string $file, string $content): void
175182
}
176183

177184
if (write_file($path, $content)) {
178-
CLI::write(CLI::color(' Created: ', 'green') . $cleanPath);
185+
$this->write(CLI::color(' Created: ', 'green') . $cleanPath);
179186
} else {
180187
CLI::error(" Error creating {$cleanPath}.");
181188
}
@@ -206,7 +213,7 @@ protected function add(string $file, string $code, string $pattern, string $repl
206213
}
207214

208215
if (write_file($path, $output)) {
209-
CLI::write(CLI::color(' Updated: ', 'green') . $cleanPath);
216+
$this->write(CLI::color(' Updated: ', 'green') . $cleanPath);
210217
} else {
211218
CLI::error(" Error updating {$cleanPath}.");
212219
}
@@ -232,7 +239,7 @@ private function replace(string $file, array $replaces): bool
232239
}
233240

234241
if (write_file($path, $output)) {
235-
CLI::write(CLI::color(' Updated: ', 'green') . $cleanPath);
242+
$this->write(CLI::color(' Updated: ', 'green') . $cleanPath);
236243

237244
return true;
238245
}
@@ -297,13 +304,71 @@ private function setSecurityCSRF(): void
297304

298305
// check $csrfProtection = 'session'
299306
if ($output === $content) {
300-
CLI::write(CLI::color(' Security Setup: ', 'green') . 'Everything is fine.');
307+
$this->write(CLI::color(' Security Setup: ', 'green') . 'Everything is fine.');
301308

302309
return;
303310
}
304311

305312
if (write_file($path, $output)) {
306-
CLI::write(CLI::color(' Updated: ', 'green') . "We have updated file '{$cleanPath}' for security reasons.");
313+
$this->write(CLI::color(' Updated: ', 'green') . "We have updated file '{$cleanPath}' for security reasons.");
314+
} else {
315+
CLI::error(" Error updating file '{$cleanPath}'.");
316+
}
317+
}
318+
319+
private function setupEmail(): void
320+
{
321+
$file = 'Config/Email.php';
322+
323+
$path = $this->distPath . $file;
324+
$cleanPath = clean_path($path);
325+
326+
if (! is_file($path)) {
327+
CLI::error(" Not found file '{$cleanPath}'.");
328+
329+
return;
330+
}
331+
332+
$config = new EmailConfig();
333+
$fromEmail = $config->fromEmail;
334+
$fromName = $config->fromName;
335+
336+
if ($fromEmail !== '' && $fromName !== '') {
337+
$this->write(CLI::color(' Email Setup: ', 'green') . 'Everything is fine.');
338+
339+
return;
340+
}
341+
342+
$content = file_get_contents($path);
343+
$output = $content;
344+
345+
if ($fromEmail === '') {
346+
$set = $this->prompt(' The required Config\Email::$fromEmail is not set. Do you set now?', ['y', 'n']);
347+
348+
if ($set === 'y') {
349+
// Input from email
350+
$fromEmail = $this->prompt(' What is your email?', null, 'required|valid_email');
351+
352+
$pattern = '/^ public .*\$fromEmail\s+= \'\';/mu';
353+
$replace = ' public string $fromEmail = \'' . $fromEmail . '\';';
354+
$output = preg_replace($pattern, $replace, $content);
355+
}
356+
}
357+
358+
if ($fromName === '') {
359+
$set = $this->prompt(' The required Config\Email::$fromName is not set. Do you set now?', ['y', 'n']);
360+
361+
if ($set === 'y') {
362+
$fromName = $this->prompt(' What is your name?', null, 'required');
363+
364+
$pattern = '/^ public .*\$fromName\s+= \'\';/mu';
365+
$replace = ' public string $fromName = \'' . $fromName . '\';';
366+
$output = preg_replace($pattern, $replace, $output);
367+
}
368+
}
369+
370+
if (write_file($path, $output)) {
371+
$this->write(CLI::color(' Updated: ', 'green') . $cleanPath);
307372
} else {
308373
CLI::error(" Error updating file '{$cleanPath}'.");
309374
}
@@ -312,20 +377,65 @@ private function setSecurityCSRF(): void
312377
private function runMigrations(): void
313378
{
314379
if (
315-
$this->cliPrompt(' Run `spark migrate --all` now?', ['y', 'n']) === 'n'
380+
$this->prompt(' Run `spark migrate --all` now?', ['y', 'n']) === 'n'
316381
) {
317382
return;
318383
}
319384

320385
$command = new Migrate(Services::logger(), Services::commands());
386+
387+
// This is a hack for testing.
388+
// @TODO Remove CITestStreamFilter and refactor when CI 4.5.0 or later is supported.
389+
CITestStreamFilter::registration();
390+
CITestStreamFilter::addOutputFilter();
391+
CITestStreamFilter::addErrorFilter();
392+
321393
$command->run(['all' => null]);
394+
395+
CITestStreamFilter::removeOutputFilter();
396+
CITestStreamFilter::removeErrorFilter();
397+
398+
// Capture the output, and write for testing.
399+
// @TODO Remove CITestStreamFilter and refactor when CI 4.5.0 or later is supported.
400+
$output = CITestStreamFilter::$buffer;
401+
$this->write($output);
402+
403+
CITestStreamFilter::$buffer = '';
404+
}
405+
406+
private function prompt(string $field, $options = null, $validation = null): string
407+
{
408+
return self::$io->prompt($field, $options, $validation);
409+
}
410+
411+
private function write(
412+
string $text = '',
413+
?string $foreground = null,
414+
?string $background = null
415+
): void {
416+
self::$io->write($text, $foreground, $background);
417+
}
418+
419+
private function ensureInputOutput(): void
420+
{
421+
if (self::$io === null) {
422+
self::$io = new InputOutput();
423+
}
424+
}
425+
426+
/**
427+
* @internal Testing purpose only
428+
*/
429+
public static function setInputOutput(InputOutput $io): void
430+
{
431+
self::$io = $io;
322432
}
323433

324434
/**
325-
* This method is for testing.
435+
* @internal Testing purpose only
326436
*/
327-
protected function cliPrompt(string $field, array $options): string
437+
public static function resetInputOutput(): void
328438
{
329-
return CLI::prompt($field, $options);
439+
self::$io = null;
330440
}
331441
}

tests/Commands/SetupTest.php

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Tests\Commands;
66

77
use CodeIgniter\Shield\Commands\Setup;
8-
use CodeIgniter\Test\Filters\CITestStreamFilter;
8+
use CodeIgniter\Shield\Test\MockInputOutput;
99
use Config\Services;
1010
use org\bovigo\vfs\vfsStream;
1111
use Tests\Support\TestCase;
@@ -15,38 +15,48 @@
1515
*/
1616
final class SetupTest extends TestCase
1717
{
18-
protected function setUp(): void
19-
{
20-
parent::setUp();
21-
22-
CITestStreamFilter::registration();
23-
CITestStreamFilter::addOutputFilter();
24-
}
18+
private ?MockInputOutput $io = null;
19+
private $streamFilter;
2520

2621
protected function tearDown(): void
2722
{
2823
parent::tearDown();
2924

30-
CITestStreamFilter::removeOutputFilter();
31-
CITestStreamFilter::removeErrorFilter();
25+
Setup::resetInputOutput();
26+
}
27+
28+
/**
29+
* Set MockInputOutput and user inputs.
30+
*
31+
* @param array<int, string> $inputs User inputs
32+
* @phpstan-param list<string> $inputs
33+
*/
34+
private function setMockIo(array $inputs): void
35+
{
36+
$this->io = new MockInputOutput();
37+
$this->io->setInputs($inputs);
38+
Setup::setInputOutput($this->io);
3239
}
3340

3441
public function testRun(): void
3542
{
43+
// Set MockIO and your inputs.
44+
$this->setMockIo([
45+
'y',
46+
'admin@example.com',
47+
'y',
48+
'Site Administrator',
49+
'y',
50+
]);
51+
3652
$root = vfsStream::setup('root');
3753
vfsStream::copyFromFileSystem(
3854
APPPATH,
3955
$root
4056
);
4157
$appFolder = $root->url() . '/';
4258

43-
$command = $this->getMockBuilder(Setup::class)
44-
->setConstructorArgs([Services::logger(), Services::commands()])
45-
->onlyMethods(['cliPrompt'])
46-
->getMock();
47-
$command
48-
->method('cliPrompt')
49-
->willReturn('y');
59+
$command = new Setup(Services::logger(), Services::commands());
5060

5161
$this->setPrivateProperty($command, 'distPath', $appFolder);
5262

@@ -66,15 +76,16 @@ public function testRun(): void
6676
$security = file_get_contents($appFolder . 'Config/Security.php');
6777
$this->assertStringContainsString('$csrfProtection = \'session\';', $security);
6878

69-
$result = str_replace(["\033[0;32m", "\033[0m"], '', CITestStreamFilter::$buffer);
79+
$result = str_replace(["\033[0;32m", "\033[0m"], '', $this->io->getOutputs());
7080

7181
$this->assertStringContainsString(
7282
' Created: vfs://root/Config/Auth.php
7383
Created: vfs://root/Config/AuthGroups.php
7484
Created: vfs://root/Config/AuthToken.php
7585
Updated: vfs://root/Controllers/BaseController.php
7686
Updated: vfs://root/Config/Routes.php
77-
Updated: We have updated file \'vfs://root/Config/Security.php\' for security reasons.',
87+
Updated: We have updated file \'vfs://root/Config/Security.php\' for security reasons.
88+
Updated: vfs://root/Config/Email.php',
7889
$result
7990
);
8091
$this->assertStringContainsString(

0 commit comments

Comments
 (0)