1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
19:
20: App::uses('AppShell', 'Console/Command');
21: App::uses('File', 'Utility');
22: App::uses('Folder', 'Utility');
23: App::uses('String', 'Utility');
24: App::uses('Security', 'Utility');
25:
26: 27: 28: 29: 30:
31: class ProjectTask extends AppShell {
32:
33: 34: 35: 36: 37:
38: public $configPath = null;
39:
40: 41: 42: 43: 44: 45:
46: public function execute() {
47: $project = null;
48: if (isset($this->args[0])) {
49: $project = $this->args[0];
50: } else {
51: $appContents = array_diff(scandir(APP), array('.', '..'));
52: if (empty($appContents)) {
53: $suggestedPath = rtrim(APP, DS);
54: } else {
55: $suggestedPath = APP . 'myapp';
56: }
57: }
58:
59: while (!$project) {
60: $prompt = __d('cake_console', "What is the path to the project you want to bake?");
61: $project = $this->in($prompt, null, $suggestedPath);
62: }
63:
64: if ($project && !Folder::isAbsolute($project) && isset($_SERVER['PWD'])) {
65: $project = $_SERVER['PWD'] . DS . $project;
66: }
67:
68: $response = false;
69: while ($response == false && is_dir($project) === true && file_exists($project . 'Config' . 'core.php')) {
70: $prompt = __d('cake_console', '<warning>A project already exists in this location:</warning> %s Overwrite?', $project);
71: $response = $this->in($prompt, array('y', 'n'), 'n');
72: if (strtolower($response) === 'n') {
73: $response = $project = false;
74: }
75: }
76:
77: $success = true;
78: if ($this->bake($project)) {
79: $path = Folder::slashTerm($project);
80:
81: if ($this->securitySalt($path) === true) {
82: $this->out(__d('cake_console', ' * Random hash key created for \'Security.salt\''));
83: } else {
84: $this->err(__d('cake_console', 'Unable to generate random hash for \'Security.salt\', you should change it in %s', APP . 'Config' . DS . 'core.php'));
85: $success = false;
86: }
87:
88: if ($this->securityCipherSeed($path) === true) {
89: $this->out(__d('cake_console', ' * Random seed created for \'Security.cipherSeed\''));
90: } else {
91: $this->err(__d('cake_console', 'Unable to generate random seed for \'Security.cipherSeed\', you should change it in %s', APP . 'Config' . DS . 'core.php'));
92: $success = false;
93: }
94:
95: if ($this->consolePath($path) === true) {
96: $this->out(__d('cake_console', ' * app/Console/cake.php path set.'));
97: } else {
98: $this->err(__d('cake_console', 'Unable to set console path for app/Console.'));
99: $success = false;
100: }
101:
102: $hardCode = false;
103: if ($this->cakeOnIncludePath()) {
104: $this->out(__d('cake_console', '<info>CakePHP is on your `include_path`. CAKE_CORE_INCLUDE_PATH will be set, but commented out.</info>'));
105: } else {
106: $this->out(__d('cake_console', '<warning>CakePHP is not on your `include_path`, CAKE_CORE_INCLUDE_PATH will be hard coded.</warning>'));
107: $this->out(__d('cake_console', 'You can fix this by adding CakePHP to your `include_path`.'));
108: $hardCode = true;
109: }
110: $success = $this->corePath($path, $hardCode) === true;
111: if ($success) {
112: $this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php', CAKE_CORE_INCLUDE_PATH));
113: $this->out(__d('cake_console', ' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php', CAKE_CORE_INCLUDE_PATH));
114: } else {
115: $this->err(__d('cake_console', 'Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s', $path . 'webroot' . DS . 'index.php'));
116: $success = false;
117: }
118: if ($success && $hardCode) {
119: $this->out(__d('cake_console', ' * <warning>Remember to check these values after moving to production server</warning>'));
120: }
121:
122: $Folder = new Folder($path);
123: if (!$Folder->chmod($path . 'tmp', 0777)) {
124: $this->err(__d('cake_console', 'Could not set permissions on %s', $path . DS . 'tmp'));
125: $this->out(__d('cake_console', 'chmod -R 0777 %s', $path . DS . 'tmp'));
126: $success = false;
127: }
128: if ($success) {
129: $this->out(__d('cake_console', '<success>Project baked successfully!</success>'));
130: } else {
131: $this->out(__d('cake_console', 'Project baked but with <warning>some issues.</warning>.'));
132: }
133: return $path;
134: }
135: }
136:
137: 138: 139: 140: 141:
142: public function cakeOnIncludePath() {
143: $paths = explode(PATH_SEPARATOR, ini_get('include_path'));
144: foreach ($paths as $path) {
145: if (file_exists($path . DS . 'Cake' . DS . 'bootstrap.php')) {
146: return true;
147: }
148: }
149: return false;
150: }
151:
152: 153: 154: 155: 156: 157: 158: 159: 160: 161:
162: public function bake($path, $skel = null, $skip = array('empty')) {
163: if (!$skel && !empty($this->params['skel'])) {
164: $skel = $this->params['skel'];
165: }
166: while (!$skel) {
167: $skel = $this->in(
168: __d('cake_console', "What is the path to the directory layout you wish to copy?"),
169: null,
170: CAKE . 'Console' . DS . 'Templates' . DS . 'skel'
171: );
172: if (!$skel) {
173: $this->err(__d('cake_console', 'The directory path you supplied was empty. Please try again.'));
174: } else {
175: while (is_dir($skel) === false) {
176: $skel = $this->in(
177: __d('cake_console', 'Directory path does not exist please choose another:'),
178: null,
179: CAKE . 'Console' . DS . 'Templates' . DS . 'skel'
180: );
181: }
182: }
183: }
184:
185: $app = basename($path);
186:
187: $this->out(__d('cake_console', '<info>Skel Directory</info>: ') . $skel);
188: $this->out(__d('cake_console', '<info>Will be copied to</info>: ') . $path);
189: $this->hr();
190:
191: $looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n', 'q'), 'y');
192:
193: switch (strtolower($looksGood)) {
194: case 'y':
195: $Folder = new Folder($skel);
196: if (!empty($this->params['empty'])) {
197: $skip = array();
198: }
199:
200: if ($Folder->copy(array('to' => $path, 'skip' => $skip))) {
201: $this->hr();
202: $this->out(__d('cake_console', '<success>Created:</success> %s in %s', $app, $path));
203: $this->hr();
204: } else {
205: $this->err(__d('cake_console', "<error>Could not create</error> '%s' properly.", $app));
206: return false;
207: }
208:
209: foreach ($Folder->messages() as $message) {
210: $this->out(String::wrap(' * ' . $message), 1, Shell::VERBOSE);
211: }
212:
213: return true;
214: case 'n':
215: unset($this->args[0]);
216: $this->execute();
217: return false;
218: case 'q':
219: $this->out(__d('cake_console', '<error>Bake Aborted.</error>'));
220: return false;
221: }
222: }
223:
224: 225: 226: 227: 228: 229: 230:
231: public function consolePath($path) {
232: $File = new File($path . 'Console' . DS . 'cake.php');
233: $contents = $File->read();
234: if (preg_match('/(__CAKE_PATH__)/', $contents, $match)) {
235: $root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " \$ds . '" : "'";
236: $replacement = $root . str_replace(DS, "' . \$ds . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'";
237: $result = str_replace($match[0], $replacement, $contents);
238: if ($File->write($result)) {
239: return true;
240: }
241: return false;
242: }
243: return false;
244: }
245:
246: 247: 248: 249: 250: 251:
252: public function securitySalt($path) {
253: $File = new File($path . 'Config' . DS . 'core.php');
254: $contents = $File->read();
255: if (preg_match('/([\s]*Configure::write\(\'Security.salt\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
256: $string = Security::generateAuthKey();
257: $result = str_replace($match[0], "\t" . 'Configure::write(\'Security.salt\', \'' . $string . '\');', $contents);
258: if ($File->write($result)) {
259: return true;
260: }
261: return false;
262: }
263: return false;
264: }
265:
266: 267: 268: 269: 270: 271:
272: public function securityCipherSeed($path) {
273: $File = new File($path . 'Config' . DS . 'core.php');
274: $contents = $File->read();
275: if (preg_match('/([\s]*Configure::write\(\'Security.cipherSeed\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
276: App::uses('Security', 'Utility');
277: $string = substr(bin2hex(Security::generateAuthKey()), 0, 30);
278: $result = str_replace($match[0], "\t" . 'Configure::write(\'Security.cipherSeed\', \'' . $string . '\');', $contents);
279: if ($File->write($result)) {
280: return true;
281: }
282: return false;
283: }
284: return false;
285: }
286:
287: 288: 289: 290: 291: 292: 293:
294: public function corePath($path, $hardCode = true) {
295: if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) {
296: $filename = $path . 'webroot' . DS . 'index.php';
297: if (!$this->_replaceCorePath($filename, $hardCode)) {
298: return false;
299: }
300: $filename = $path . 'webroot' . DS . 'test.php';
301: if (!$this->_replaceCorePath($filename, $hardCode)) {
302: return false;
303: }
304: return true;
305: }
306: }
307:
308: 309: 310: 311: 312: 313: 314:
315: protected function _replaceCorePath($filename, $hardCode) {
316: $contents = file_get_contents($filename);
317:
318: $root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'";
319: $corePath = $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'";
320:
321: $result = str_replace('__CAKE_PATH__', $corePath, $contents, $count);
322: if ($hardCode) {
323: $result = str_replace('//define(\'CAKE_CORE', 'define(\'CAKE_CORE', $result);
324: }
325: if (!file_put_contents($filename, $result)) {
326: return false;
327: }
328: if ($count == 0) {
329: return false;
330: }
331: return true;
332: }
333:
334: 335: 336: 337: 338: 339:
340: public function cakeAdmin($name) {
341: $path = (empty($this->configPath)) ? APP . 'Config' . DS : $this->configPath;
342: $File = new File($path . 'core.php');
343: $contents = $File->read();
344: if (preg_match('%(\s*[/]*Configure::write\(\'Routing.prefixes\',[\s\'a-z,\)\(]*\);)%', $contents, $match)) {
345: $result = str_replace($match[0], "\n" . 'Configure::write(\'Routing.prefixes\', array(\'' . $name . '\'));', $contents);
346: if ($File->write($result)) {
347: Configure::write('Routing.prefixes', array($name));
348: return true;
349: } else {
350: return false;
351: }
352: } else {
353: return false;
354: }
355: }
356:
357: 358: 359: 360: 361:
362: public function getPrefix() {
363: $admin = '';
364: $prefixes = Configure::read('Routing.prefixes');
365: if (!empty($prefixes)) {
366: if (count($prefixes) == 1) {
367: return $prefixes[0] . '_';
368: }
369: if ($this->interactive) {
370: $this->out();
371: $this->out(__d('cake_console', 'You have more than one routing prefix configured'));
372: }
373: $options = array();
374: foreach ($prefixes as $i => $prefix) {
375: $options[] = $i + 1;
376: if ($this->interactive) {
377: $this->out($i + 1 . '. ' . $prefix);
378: }
379: }
380: $selection = $this->in(__d('cake_console', 'Please choose a prefix to bake with.'), $options, 1);
381: return $prefixes[$selection - 1] . '_';
382: }
383: if ($this->interactive) {
384: $this->hr();
385: $this->out(__d('cake_console', 'You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/Config/core.php to use prefix routing.'));
386: $this->out(__d('cake_console', 'What would you like the prefix route to be?'));
387: $this->out(__d('cake_console', 'Example: www.example.com/admin/controller'));
388: while ($admin == '') {
389: $admin = $this->in(__d('cake_console', 'Enter a routing prefix:'), null, 'admin');
390: }
391: if ($this->cakeAdmin($admin) !== true) {
392: $this->out(__d('cake_console', '<error>Unable to write to</error> /app/Config/core.php.'));
393: $this->out(__d('cake_console', 'You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/Config/core.php to use prefix routing.'));
394: $this->_stop();
395: }
396: return $admin . '_';
397: }
398: return '';
399: }
400:
401: 402: 403: 404: 405:
406: public function getOptionParser() {
407: $parser = parent::getOptionParser();
408: return $parser->description(
409: __d('cake_console', 'Generate a new CakePHP project skeleton.')
410: )->addArgument('name', array(
411: 'help' => __d('cake_console', 'Application directory to make, if it starts with "/" the path is absolute.')
412: ))->addOption('empty', array(
413: 'boolean' => true,
414: 'help' => __d('cake_console', 'Create empty files in each of the directories. Good if you are using git')
415: ))->addOption('skel', array(
416: 'default' => current(App::core('Console')) . 'Templates' . DS . 'skel',
417: 'help' => __d('cake_console', 'The directory layout to use for the new application skeleton. Defaults to cake/Console/Templates/skel of CakePHP used to create the project.')
418: ));
419: }
420:
421: }
422: