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