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