PageRenderTime 27ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Cake/Console/Command/Task/ProjectTask.php

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