1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
17:
18: App::uses('AppShell', 'Console/Command');
19: App::uses('Controller', 'Controller');
20: App::uses('BakeTask', 'Console/Command/Task');
21:
22: 23: 24: 25: 26:
27: class ViewTask extends BakeTask {
28:
29: 30: 31: 32: 33:
34: public $tasks = array('Project', 'Controller', 'DbConfig', 'Template');
35:
36: 37: 38: 39: 40:
41: public $path = null;
42:
43: 44: 45: 46: 47:
48: public $controllerName = null;
49:
50: 51: 52: 53: 54:
55: public $template = null;
56:
57: 58: 59: 60: 61:
62: public $scaffoldActions = array('index', 'view', 'add', 'edit');
63:
64: 65: 66: 67: 68: 69:
70: public $noTemplateActions = array('delete');
71:
72: 73: 74: 75: 76:
77: public function initialize() {
78: $this->path = current(App::path('View'));
79: }
80:
81: 82: 83: 84: 85:
86: public function execute() {
87: parent::execute();
88: if (empty($this->args)) {
89: $this->_interactive();
90: }
91: if (empty($this->args[0])) {
92: return;
93: }
94: if (!isset($this->connection)) {
95: $this->connection = 'default';
96: }
97: $action = null;
98: $this->controllerName = $this->_controllerName($this->args[0]);
99:
100: $this->Project->interactive = false;
101: if (strtolower($this->args[0]) === 'all') {
102: return $this->all();
103: }
104:
105: if (isset($this->args[1])) {
106: $this->template = $this->args[1];
107: }
108: if (isset($this->args[2])) {
109: $action = $this->args[2];
110: }
111: if (!$action) {
112: $action = $this->template;
113: }
114: if ($action) {
115: return $this->bake($action, true);
116: }
117:
118: $vars = $this->_loadController();
119: $methods = $this->_methodsToBake();
120:
121: foreach ($methods as $method) {
122: $content = $this->getContent($method, $vars);
123: if ($content) {
124: $this->bake($method, $content);
125: }
126: }
127: }
128:
129: 130: 131: 132: 133:
134: protected function _methodsToBake() {
135: $methods = array_diff(
136: array_map('strtolower', get_class_methods($this->controllerName . 'Controller')),
137: array_map('strtolower', get_class_methods('AppController'))
138: );
139: $scaffoldActions = false;
140: if (empty($methods)) {
141: $scaffoldActions = true;
142: $methods = $this->scaffoldActions;
143: }
144: $adminRoute = $this->Project->getPrefix();
145: foreach ($methods as $i => $method) {
146: if ($adminRoute && !empty($this->params['admin'])) {
147: if ($scaffoldActions) {
148: $methods[$i] = $adminRoute . $method;
149: continue;
150: } elseif (strpos($method, $adminRoute) === false) {
151: unset($methods[$i]);
152: }
153: }
154: if ($method[0] === '_' || $method === strtolower($this->controllerName . 'Controller')) {
155: unset($methods[$i]);
156: }
157: }
158: return $methods;
159: }
160:
161: 162: 163: 164: 165:
166: public function all() {
167: $this->Controller->interactive = false;
168: $tables = $this->Controller->listAll($this->connection, false);
169:
170: $actions = null;
171: if (isset($this->args[1])) {
172: $actions = array($this->args[1]);
173: }
174: $this->interactive = false;
175: foreach ($tables as $table) {
176: $model = $this->_modelName($table);
177: $this->controllerName = $this->_controllerName($model);
178: App::uses($model, 'Model');
179: if (class_exists($model)) {
180: $vars = $this->_loadController();
181: if (!$actions) {
182: $actions = $this->_methodsToBake();
183: }
184: $this->bakeActions($actions, $vars);
185: $actions = null;
186: }
187: }
188: }
189:
190: 191: 192: 193: 194:
195: protected function _interactive() {
196: $this->hr();
197: $this->out(sprintf("Bake View\nPath: %s", $this->getPath()));
198: $this->hr();
199:
200: $this->DbConfig->interactive = $this->Controller->interactive = $this->interactive = true;
201:
202: if (empty($this->connection)) {
203: $this->connection = $this->DbConfig->getConfig();
204: }
205:
206: $this->Controller->connection = $this->connection;
207: $this->controllerName = $this->Controller->getName();
208:
209: $prompt = __d('cake_console', "Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite %s views if they exist.", $this->controllerName);
210: $interactive = $this->in($prompt, array('y', 'n'), 'n');
211:
212: if (strtolower($interactive) === 'n') {
213: $this->interactive = false;
214: }
215:
216: $prompt = __d('cake_console', "Would you like to create some CRUD views\n(index, add, view, edit) for this controller?\nNOTE: Before doing so, you'll need to create your controller\nand model classes (including associated models).");
217: $wannaDoScaffold = $this->in($prompt, array('y', 'n'), 'y');
218:
219: $wannaDoAdmin = $this->in(__d('cake_console', "Would you like to create the views for admin routing?"), array('y', 'n'), 'n');
220:
221: if (strtolower($wannaDoScaffold) === 'y' || strtolower($wannaDoAdmin) === 'y') {
222: $vars = $this->_loadController();
223: if (strtolower($wannaDoScaffold) === 'y') {
224: $actions = $this->scaffoldActions;
225: $this->bakeActions($actions, $vars);
226: }
227: if (strtolower($wannaDoAdmin) === 'y') {
228: $admin = $this->Project->getPrefix();
229: $regularActions = $this->scaffoldActions;
230: $adminActions = array();
231: foreach ($regularActions as $action) {
232: $adminActions[] = $admin . $action;
233: }
234: $this->bakeActions($adminActions, $vars);
235: }
236: $this->hr();
237: $this->out();
238: $this->out(__d('cake_console', "View Scaffolding Complete.\n"));
239: } else {
240: $this->customAction();
241: }
242: }
243:
244: 245: 246: 247: 248: 249: 250: 251: 252:
253: protected function _loadController() {
254: if (!$this->controllerName) {
255: $this->err(__d('cake_console', 'Controller not found'));
256: }
257:
258: $plugin = null;
259: if ($this->plugin) {
260: $plugin = $this->plugin . '.';
261: }
262:
263: $controllerClassName = $this->controllerName . 'Controller';
264: App::uses($controllerClassName, $plugin . 'Controller');
265: if (!class_exists($controllerClassName)) {
266: $file = $controllerClassName . '.php';
267: $this->err(__d('cake_console', "The file '%s' could not be found.\nIn order to bake a view, you'll need to first create the controller.", $file));
268: return $this->_stop();
269: }
270: $controllerObj = new $controllerClassName();
271: $controllerObj->plugin = $this->plugin;
272: $controllerObj->constructClasses();
273: $modelClass = $controllerObj->modelClass;
274: $modelObj = $controllerObj->{$controllerObj->modelClass};
275:
276: if ($modelObj) {
277: $primaryKey = $modelObj->primaryKey;
278: $displayField = $modelObj->displayField;
279: $singularVar = Inflector::variable($modelClass);
280: $singularHumanName = $this->_singularHumanName($this->controllerName);
281: $schema = $modelObj->schema(true);
282: $fields = array_keys($schema);
283: $associations = $this->_associations($modelObj);
284: } else {
285: $primaryKey = $displayField = null;
286: $singularVar = Inflector::variable(Inflector::singularize($this->controllerName));
287: $singularHumanName = $this->_singularHumanName($this->controllerName);
288: $fields = $schema = $associations = array();
289: }
290: $pluralVar = Inflector::variable($this->controllerName);
291: $pluralHumanName = $this->_pluralHumanName($this->controllerName);
292:
293: return compact('modelClass', 'schema', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
294: 'singularHumanName', 'pluralHumanName', 'fields', 'associations');
295: }
296:
297: 298: 299: 300: 301: 302: 303:
304: public function bakeActions($actions, $vars) {
305: foreach ($actions as $action) {
306: $content = $this->getContent($action, $vars);
307: $this->bake($action, $content);
308: }
309: }
310:
311: 312: 313: 314: 315:
316: public function customAction() {
317: $action = '';
318: while (!$action) {
319: $action = $this->in(__d('cake_console', 'Action Name? (use lowercase_underscored function name)'));
320: if (!$action) {
321: $this->out(__d('cake_console', 'The action name you supplied was empty. Please try again.'));
322: }
323: }
324: $this->out();
325: $this->hr();
326: $this->out(__d('cake_console', 'The following view will be created:'));
327: $this->hr();
328: $this->out(__d('cake_console', 'Controller Name: %s', $this->controllerName));
329: $this->out(__d('cake_console', 'Action Name: %s', $action));
330: $this->out(__d('cake_console', 'Path: %s', $this->getPath() . $this->controllerName . DS . Inflector::underscore($action) . ".ctp"));
331: $this->hr();
332: $looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n'), 'y');
333: if (strtolower($looksGood) === 'y') {
334: $this->bake($action, ' ');
335: return $this->_stop();
336: }
337: $this->out(__d('cake_console', 'Bake Aborted.'));
338: }
339:
340: 341: 342: 343: 344: 345: 346:
347: public function bake($action, $content = '') {
348: if ($content === true) {
349: $content = $this->getContent($action);
350: }
351: if (empty($content)) {
352: return false;
353: }
354: $this->out("\n" . __d('cake_console', 'Baking `%s` view file...', $action), 1, Shell::QUIET);
355: $path = $this->getPath();
356: $filename = $path . $this->controllerName . DS . Inflector::underscore($action) . '.ctp';
357: return $this->createFile($filename, $content);
358: }
359:
360: 361: 362: 363: 364: 365: 366:
367: public function getContent($action, $vars = null) {
368: if (!$vars) {
369: $vars = $this->_loadController();
370: }
371:
372: $this->Template->set('action', $action);
373: $this->Template->set('plugin', $this->plugin);
374: $this->Template->set($vars);
375: $template = $this->getTemplate($action);
376: if ($template) {
377: return $this->Template->generate('views', $template);
378: }
379: return false;
380: }
381:
382: 383: 384: 385: 386: 387:
388: public function getTemplate($action) {
389: if ($action != $this->template && in_array($action, $this->noTemplateActions)) {
390: return false;
391: }
392: if (!empty($this->template) && $action != $this->template) {
393: return $this->template;
394: }
395: $themePath = $this->Template->getThemePath();
396: if (file_exists($themePath . 'views' . DS . $action . '.ctp')) {
397: return $action;
398: }
399: $template = $action;
400: $prefixes = Configure::read('Routing.prefixes');
401: foreach ((array)$prefixes as $prefix) {
402: if (strpos($template, $prefix) !== false) {
403: $template = str_replace($prefix . '_', '', $template);
404: }
405: }
406: if (in_array($template, array('add', 'edit'))) {
407: $template = 'form';
408: } elseif (preg_match('@(_add|_edit)$@', $template)) {
409: $template = str_replace(array('_add', '_edit'), '_form', $template);
410: }
411: return $template;
412: }
413:
414: 415: 416: 417: 418:
419: public function getOptionParser() {
420: $parser = parent::getOptionParser();
421:
422: $parser->description(
423: __d('cake_console', 'Bake views for a controller, using built-in or custom templates.')
424: )->addArgument('controller', array(
425: 'help' => __d('cake_console', 'Name of the controller views to bake. Can be Plugin.name as a shortcut for plugin baking.')
426: ))->addArgument('action', array(
427: 'help' => __d('cake_console', "Will bake a single action's file. core templates are (index, add, edit, view)")
428: ))->addArgument('alias', array(
429: 'help' => __d('cake_console', 'Will bake the template in <action> but create the filename after <alias>.')
430: ))->addOption('plugin', array(
431: 'short' => 'p',
432: 'help' => __d('cake_console', 'Plugin to bake the view into.')
433: ))->addOption('admin', array(
434: 'help' => __d('cake_console', 'Set to only bake views for a prefix in Routing.prefixes'),
435: 'boolean' => true
436: ))->addOption('theme', array(
437: 'short' => 't',
438: 'help' => __d('cake_console', 'Theme to use when baking code.')
439: ))->addOption('connection', array(
440: 'short' => 'c',
441: 'help' => __d('cake_console', 'The connection the connected model is on.')
442: ))->addOption('force', array(
443: 'short' => 'f',
444: 'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
445: ))->addSubcommand('all', array(
446: 'help' => __d('cake_console', 'Bake all CRUD action views for all controllers. Requires models and controllers to exist.')
447: ))->epilog(
448: __d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
449: );
450:
451: return $parser;
452: }
453:
454: 455: 456: 457: 458: 459:
460: protected function _associations(Model $model) {
461: $keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
462: $associations = array();
463:
464: foreach ($keys as $type) {
465: foreach ($model->{$type} as $assocKey => $assocData) {
466: list(, $modelClass) = pluginSplit($assocData['className']);
467: $associations[$type][$assocKey]['primaryKey'] = $model->{$assocKey}->primaryKey;
468: $associations[$type][$assocKey]['displayField'] = $model->{$assocKey}->displayField;
469: $associations[$type][$assocKey]['foreignKey'] = $assocData['foreignKey'];
470: $associations[$type][$assocKey]['controller'] = Inflector::pluralize(Inflector::underscore($modelClass));
471: $associations[$type][$assocKey]['fields'] = array_keys($model->{$assocKey}->schema(true));
472: }
473: }
474: return $associations;
475: }
476:
477: }
478: