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: }
51:
52: while (!$project) {
53: $prompt = __d('cake_console', "What is the path to the project you want to bake?");
54: $project = $this->in($prompt, null, APP . 'myapp');
55: }
56:
57:
58: if ($project && !Folder::isAbsolute($project) && isset($_SERVER['PWD'])) {
59: $project = $_SERVER['PWD'] . DS . $project;
60: }
61:
62: $response = false;
63: while ($response == false && is_dir($project) === true && file_exists($project . 'Config' . 'core.php')) {
64: $prompt = __d('cake_console', '<warning>A project already exists in this location:</warning> %s Overwrite?', $project);
65: $response = $this->in($prompt, array('y', 'n'), 'n');
66: if (strtolower($response) === 'n') {
67: $response = $project = false;
68: }
69: }
70:
71: $success = true;
72: if ($this->bake($project)) {
73: $path = Folder::slashTerm($project);
74: if ($this->createHome($path)) {
75: $this->out(__d('cake_console', ' * Welcome page created'));
76: } else {
77: $this->err(__d('cake_console', 'The Welcome page was <error>NOT</error> created'));
78: $success = false;
79: }
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:
163: public function bake($path, $skel = null, $skip = array('empty')) {
164: if (!$skel && !empty($this->params['skel'])) {
165: $skel = $this->params['skel'];
166: }
167: while (!$skel) {
168: $skel = $this->in(
169: __d('cake_console', "What is the path to the directory layout you wish to copy?"),
170: null,
171: CAKE . 'Console' . DS . 'Templates' . DS . 'skel'
172: );
173: if (!$skel) {
174: $this->err(__d('cake_console', 'The directory path you supplied was empty. Please try again.'));
175: } else {
176: while (is_dir($skel) === false) {
177: $skel = $this->in(
178: __d('cake_console', 'Directory path does not exist please choose another:'),
179: null,
180: CAKE . 'Console' . DS . 'Templates' . DS . 'skel'
181: );
182: }
183: }
184: }
185:
186: $app = basename($path);
187:
188: $this->out(__d('cake_console', '<info>Skel Directory</info>: ') . $skel);
189: $this->out(__d('cake_console', '<info>Will be copied to</info>: ') . $path);
190: $this->hr();
191:
192: $looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n', 'q'), 'y');
193:
194: switch (strtolower($looksGood)) {
195: case 'y':
196: $Folder = new Folder($skel);
197: if (!empty($this->params['empty'])) {
198: $skip = array();
199: }
200:
201: if ($Folder->copy(array('to' => $path, 'skip' => $skip))) {
202: $this->hr();
203: $this->out(__d('cake_console', '<success>Created:</success> %s in %s', $app, $path));
204: $this->hr();
205: } else {
206: $this->err(__d('cake_console', "<error>Could not create</error> '%s' properly.", $app));
207: return false;
208: }
209:
210: foreach ($Folder->messages() as $message) {
211: $this->out(String::wrap(' * ' . $message), 1, Shell::VERBOSE);
212: }
213:
214: return true;
215: case 'n':
216: unset($this->args[0]);
217: $this->execute();
218: return false;
219: case 'q':
220: $this->out(__d('cake_console', '<error>Bake Aborted.</error>'));
221: return false;
222: }
223: }
224:
225: 226: 227: 228: 229: 230:
231: public function createHome($dir) {
232: $app = basename($dir);
233: $path = $dir . 'View' . DS . 'Pages' . DS;
234: $source = CAKE . 'Console' . DS . 'Templates' . DS .'default' . DS . 'views' . DS . 'home.ctp';
235: include($source);
236: return $this->createFile($path.'home.ctp', $output);
237: }
238:
239: 240: 241: 242: 243: 244: 245:
246: public function consolePath($path) {
247: $File = new File($path . 'Console' . DS . 'cake.php');
248: $contents = $File->read();
249: if (preg_match('/(__CAKE_PATH__)/', $contents, $match)) {
250: $root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " \$ds . '" : "'";
251: $replacement = $root . str_replace(DS, "' . \$ds . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'";
252: $result = str_replace($match[0], $replacement, $contents);
253: if ($File->write($result)) {
254: return true;
255: }
256: return false;
257: }
258: return false;
259: }
260:
261: 262: 263: 264: 265: 266:
267: public function securitySalt($path) {
268: $File = new File($path . 'Config' . DS . 'core.php');
269: $contents = $File->read();
270: if (preg_match('/([\s]*Configure::write\(\'Security.salt\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
271: $string = Security::generateAuthKey();
272: $result = str_replace($match[0], "\t" . 'Configure::write(\'Security.salt\', \'' . $string . '\');', $contents);
273: if ($File->write($result)) {
274: return true;
275: }
276: return false;
277: }
278: return false;
279: }
280:
281: 282: 283: 284: 285: 286:
287: public function securityCipherSeed($path) {
288: $File = new File($path . 'Config' . DS . 'core.php');
289: $contents = $File->read();
290: if (preg_match('/([\s]*Configure::write\(\'Security.cipherSeed\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
291: App::uses('Security', 'Utility');
292: $string = substr(bin2hex(Security::generateAuthKey()), 0, 30);
293: $result = str_replace($match[0], "\t" . 'Configure::write(\'Security.cipherSeed\', \'' . $string . '\');', $contents);
294: if ($File->write($result)) {
295: return true;
296: }
297: return false;
298: }
299: return false;
300: }
301:
302: 303: 304: 305: 306: 307: 308:
309: public function corePath($path, $hardCode = true) {
310: if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) {
311: $filename = $path . 'webroot' . DS . 'index.php';
312: if (!$this->_replaceCorePath($filename, $hardCode)) {
313: return false;
314: }
315: $filename = $path . 'webroot' . DS . 'test.php';
316: if (!$this->_replaceCorePath($filename, $hardCode)) {
317: return false;
318: }
319: return true;
320: }
321: }
322:
323: 324: 325: 326: 327: 328: 329:
330: protected function _replaceCorePath($filename, $hardCode) {
331: $contents = file_get_contents($filename);
332:
333: $root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'";
334: $corePath = $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'";
335:
336: $result = str_replace('__CAKE_PATH__', $corePath, $contents, $count);
337: if ($hardCode) {
338: $result = str_replace('//define(\'CAKE_CORE', 'define(\'CAKE_CORE', $result);
339: }
340: if (!file_put_contents($filename, $result)) {
341: return false;
342: }
343: if ($count == 0) {
344: return false;
345: }
346: return true;
347: }
348:
349: 350: 351: 352: 353: 354:
355: public function cakeAdmin($name) {
356: $path = (empty($this->configPath)) ? APP . 'Config' . DS : $this->configPath;
357: $File = new File($path . 'core.php');
358: $contents = $File->read();
359: if (preg_match('%(\s*[/]*Configure::write\(\'Routing.prefixes\',[\s\'a-z,\)\(]*\);)%', $contents, $match)) {
360: $result = str_replace($match[0], "\n" . 'Configure::write(\'Routing.prefixes\', array(\'' . $name . '\'));', $contents);
361: if ($File->write($result)) {
362: Configure::write('Routing.prefixes', array($name));
363: return true;
364: } else {
365: return false;
366: }
367: } else {
368: return false;
369: }
370: }
371:
372: 373: 374: 375: 376:
377: public function getPrefix() {
378: $admin = '';
379: $prefixes = Configure::read('Routing.prefixes');
380: if (!empty($prefixes)) {
381: if (count($prefixes) == 1) {
382: return $prefixes[0] . '_';
383: }
384: if ($this->interactive) {
385: $this->out();
386: $this->out(__d('cake_console', 'You have more than one routing prefix configured'));
387: }
388: $options = array();
389: foreach ($prefixes as $i => $prefix) {
390: $options[] = $i + 1;
391: if ($this->interactive) {
392: $this->out($i + 1 . '. ' . $prefix);
393: }
394: }
395: $selection = $this->in(__d('cake_console', 'Please choose a prefix to bake with.'), $options, 1);
396: return $prefixes[$selection - 1] . '_';
397: }
398: if ($this->interactive) {
399: $this->hr();
400: $this->out(__d('cake_console', 'You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/Config/core.php to use prefix routing.'));
401: $this->out(__d('cake_console', 'What would you like the prefix route to be?'));
402: $this->out(__d('cake_console', 'Example: www.example.com/admin/controller'));
403: while ($admin == '') {
404: $admin = $this->in(__d('cake_console', 'Enter a routing prefix:'), null, 'admin');
405: }
406: if ($this->cakeAdmin($admin) !== true) {
407: $this->out(__d('cake_console', '<error>Unable to write to</error> /app/Config/core.php.'));
408: $this->out(__d('cake_console', 'You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/Config/core.php to use prefix routing.'));
409: $this->_stop();
410: }
411: return $admin . '_';
412: }
413: return '';
414: }
415:
416: 417: 418: 419: 420:
421: public function getOptionParser() {
422: $parser = parent::getOptionParser();
423: return $parser->description(
424: __d('cake_console', 'Generate a new CakePHP project skeleton.')
425: )->addArgument('name', array(
426: 'help' => __d('cake_console', 'Application directory to make, if it starts with "/" the path is absolute.')
427: ))->addOption('empty', array(
428: 'help' => __d('cake_console', 'Create empty files in each of the directories. Good if you are using git')
429: ))->addOption('skel', array(
430: 'default' => current(App::core('Console')) . 'Templates' . DS . 'skel',
431: '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.')
432: ));
433: }
434:
435: }
436: