Completed
Push — master ( c0a2da...4e3077 )
by Basil
02:43
created

ThemeController::renderLayout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 36
rs 9.344
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace luya\console\commands;
4
5
use luya\helpers\FileHelper;
6
use luya\helpers\Json;
7
use luya\theme\ThemeConfig;
8
use Yii;
9
use yii\helpers\Console;
10
use yii\helpers\Inflector;
11
12
/**
13
 * Command to create a new LUYA theme.
14
 *
15
 * @author Bennet Klarhoelter <[email protected]>
16
 * @since 1.0.21
17
 */
18
class ThemeController extends \luya\console\Command
19
{
20
    /**
21
     * @inheritdoc
22
     */
23
    public $defaultAction = 'create';
24
    
25
    /**
26
     * Create a new theme.
27
     *
28
     * @param string|null $themeName
29
     *
30
     * @return int
31
     * @throws \yii\base\Exception
32
     */
33
    public function actionCreate(string $themeName = null)
34
    {
35
        Console::clearScreenBeforeCursor();
36
        
37
        $themeName = $this->prompt("Enter the name (lower case) of the theme you like to generate:", ['default' => $themeName]);
38
        
39
        $newName = preg_replace("/[^a-z]/", "", strtolower($themeName));
40
        if ($newName !== $themeName) {
41
            if (!$this->confirm("We have changed the name to '{$newName}'. Do you want to proceed with this name?")) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->confirm("We have ...oceed with this name?") of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
42
                return $this->outputError('Abort by user.');
43
            } else {
44
                $themeName = $newName;
45
            }
46
        }
47
        
48
        $availableModules = implode(', ', array_column(Yii::$app->getFrontendModules(), 'id'));
49
        $themeLocation = $this->prompt("Enter the theme location where to generate (as path alias e.g. app, $availableModules):", ['default' => 'app']);
50
        $themeLocation = '@' . ltrim($themeLocation, '@');
51
        
52
        preg_match("#^@[A-z]+#", $themeLocation, $newThemeLocation);
53
        
54
        if ($newThemeLocation[0] !== $themeLocation) {
55
            if (!$this->confirm("We have changed the name to '{$newThemeLocation[0]}'. Do you want to proceed with this name?")) {
56
                return $this->outputError('Abort by user.');
57
            } else {
58
                $themeLocation = $newThemeLocation[0];
59
            }
60
        }
61
        
62
        $basePath = $themeLocation . '/themes/' . $themeName;
63
        $themeFolder = Yii::getAlias($basePath);
64
        
65
        if (file_exists($themeFolder)) {
66
            return $this->outputError("The folder " . $themeFolder . " exists already.");
67
        }
68
        
69
        $this->outputInfo("Theme path alias: " . $basePath);
70
        $this->outputInfo("Theme real path: " . $themeFolder);
71
        if (!$this->confirm("Do you want continue?")) {
72
            return $this->outputError('Abort by user.');
73
        }
74
        
75
        $folders = [
76
            '',
77
            'resources',
78
            'views',
79
            'views/layouts',
80
            'views/cmslayouts',
81
        ];
82
        
83
        foreach ($folders as $folder) {
84
            FileHelper::createDirectory($themeFolder . DIRECTORY_SEPARATOR . $folder);
85
        }
86
        
87
        $contents = [
88
            $themeFolder. DIRECTORY_SEPARATOR . 'theme.json' => $this->renderJson($basePath, $themeName),
89
            $themeFolder. DIRECTORY_SEPARATOR . ucfirst($themeName) . 'Asset.php' => $this->renderAssetClass($themeName),
90
            $themeFolder. DIRECTORY_SEPARATOR . 'resources/'. $themeName .'-asset/style.css' => '',
91
            $themeFolder. DIRECTORY_SEPARATOR . 'views/layouts/theme.php' => $this->renderLayout($themeName),
92
            $themeFolder. DIRECTORY_SEPARATOR . 'views/cmslayouts/theme.php' => $this->renderCmsLayout($themeName),
0 ignored issues
show
Unused Code introduced by
The call to ThemeController::renderCmsLayout() has too many arguments starting with $themeName.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
93
        ];
94
        
95
        foreach ($contents as $fileName => $content) {
96
            FileHelper::writeFile($fileName, $content);
97
        }
98
        
99
        return $this->outputSuccess("Theme files has been created successfully. Please run `".$_SERVER['PHP_SELF']." import` to import the theme into the database.");
100
    }
101
    
102
    /**
103
     * Render the json template.
104
     *
105
     * @param string $basePath
106
     * @param string $themeName
107
     * @return string
108
     */
109
    private function renderJson(string $basePath, string $themeName)
110
    {
111
        $themeConfig = new ThemeConfig($basePath, []);
112
        $themeConfig->name = $themeName;
113
        
114
        if ($this->confirm('Inherit from other theme?')) {
115
            $themeConfig->parentTheme = $this->prompt(
116
                "Enter the base path (e.g. `@app/themes/blank`) of the parent theme:",
117
                [
118
                    'default' => null,
119
                    'validator' => [$this, 'validateParentTheme'],
120
                ]
121
            );
122
        }
123
        
124
        return Json::encode($themeConfig->toArray(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
125
    }
126
    
127
    /**
128
     * @param $input
129
     * @param $error
130
     *
131
     * @return bool
132
     */
133
    private function validateParentTheme($input, &$error)
134
    {
135
        if (!preg_match('/^@[a-z]+$/', $input)) {
136
            $error = 'The theme name must be only letter chars!';
137
            return false;
138
        } elseif (Yii::getAlias($input, false) === false) {
139
            $error = 'The theme base path not exists!';
140
            return false;
141
        }
142
        
143
        return true;
144
    }
145
    
146
    /**
147
     * @param $themeName
148
     *
149
     * @return string
150
     */
151
    private function renderAssetClass($themeName)
152
    {
153
        $className = ucfirst($themeName) . 'Asset';
154
        return "<?php
155
namespace app\\themes\\{$themeName};
156
157
use luya\web\Asset;
158
159
class {$className} extends Asset
160
{
161
    public \$css = [
162
        \'{$themeName}.css\',
163
    ];
164
}";
165
    }
166
    
167
    /**
168
     * @param $themeName
169
     *
170
     * @return string
171
     */
172
    private function renderLayout($themeName)
173
    {
174
        $className = ucfirst($themeName) . 'Asset';
175
    
176
        return '<?php
177
/**
178
 * @var $this \luya\web\View
179
 */
180
use luya\themes\frontend\\'.$className.';
181
182
'.$className.'::register($this);
183
184
$this->beginPage();
185
?>
186
<!DOCTYPE html>
187
<html lang="<?= Yii::$app->composition->language; ?>">
188
    <head>
189
        <title><?= $this->title; ?></title>
190
        <meta charset="utf-8">
191
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
192
        <meta http-equiv="x-ua-compatible" content="ie=edge">
193
        <?php $this->head() ?>
194
    </head>
195
    <body class="homepage">
196
    <?php $this->beginBody() ?>
197
198
        <div id="wrapper">
199
            <?php echo $content ?>
200
        </div>
201
202
    <?php $this->endBody() ?>
203
    </body>
204
</html>
205
<?php $this->endPage() ?>
206
';
207
    }
208
    
209
    /**
210
     * @return string
211
     */
212
    private function renderCmsLayout()
213
    {
214
        return '<?= $placeholders[\'content\'] ?>';
215
    }
216
}
217