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('Controller', 'Controller');
22: App::uses('BakeTask', 'Console/Command/Task');
23:
24: 25: 26: 27: 28:
29: class ViewTask extends BakeTask {
30:
31: 32: 33: 34: 35:
36: public $tasks = array('Project', 'Controller', 'DbConfig', 'Template');
37:
38: 39: 40: 41: 42:
43: public $path = null;
44:
45: 46: 47: 48: 49:
50: public $controllerName = null;
51:
52: 53: 54: 55: 56:
57: public $template = null;
58:
59: 60: 61: 62: 63:
64: public $scaffoldActions = array('index', 'view', 'add', 'edit');
65:
66: 67: 68: 69: 70: 71:
72: public $noTemplateActions = array('delete');
73:
74: 75: 76: 77: 78:
79: public function initialize() {
80: $this->path = current(App::path('View'));
81: }
82:
83: 84: 85: 86: 87:
88: public function execute() {
89: parent::execute();
90: if (empty($this->args)) {
91: $this->_interactive();
92: }
93: if (empty($this->args[0])) {
94: return;
95: }
96: if (!isset($this->connection)) {
97: $this->connection = 'default';
98: }
99: $action = null;
100: $this->controllerName = $this->_controllerName($this->args[0]);
101:
102: $this->Project->interactive = false;
103: if (strtolower($this->args[0]) === 'all') {
104: return $this->all();
105: }
106:
107: if (isset($this->args[1])) {
108: $this->template = $this->args[1];
109: }
110: if (isset($this->args[2])) {
111: $action = $this->args[2];
112: }
113: if (!$action) {
114: $action = $this->template;
115: }
116: if ($action) {
117: return $this->bake($action, true);
118: }
119:
120: $vars = $this->_loadController();
121: $methods = $this->_methodsToBake();
122:
123: foreach ($methods as $method) {
124: $content = $this->getContent($method, $vars);
125: if ($content) {
126: $this->bake($method, $content);
127: }
128: }
129: }
130:
131: 132: 133: 134: 135:
136: protected function _methodsToBake() {
137: $methods = array_diff(
138: array_map('strtolower', get_class_methods($this->controllerName . 'Controller')),
139: array_map('strtolower', get_class_methods('AppController'))
140: );
141: $scaffoldActions = false;
142: if (empty($methods)) {
143: $scaffoldActions = true;
144: $methods = $this->scaffoldActions;
145: }
146: $adminRoute = $this->Project->getPrefix();
147: foreach ($methods as $i => $method) {
148: if ($adminRoute && !empty($this->params['admin'])) {
149: if ($scaffoldActions) {
150: $methods[$i] = $adminRoute . $method;
151: continue;
152: } elseif (strpos($method, $adminRoute) === false) {
153: unset($methods[$i]);
154: }
155: }
156: if ($method[0] === '_' || $method == strtolower($this->controllerName . 'Controller')) {
157: unset($methods[$i]);
158: }
159: }
160: return $methods;
161: }
162:
163: 164: 165: 166: 167:
168: public function all() {
169: $this->Controller->interactive = false;
170: $tables = $this->Controller->listAll($this->connection, false);
171:
172: $actions = null;
173: if (isset($this->args[1])) {
174: $actions = array($this->args[1]);
175: }
176: $this->interactive = false;
177: foreach ($tables as $table) {
178: $model = $this->_modelName($table);
179: $this->controllerName = $this->_controllerName($model);
180: App::uses($model, 'Model');
181: if (class_exists($model)) {
182: $vars = $this->_loadController();
183: if (!$actions) {
184: $actions = $this->_methodsToBake();
185: }
186: $this->bakeActions($actions, $vars);
187: $actions = null;
188: }
189: }
190: }
191:
192: 193: 194: 195: 196:
197: protected function _interactive() {
198: $this->hr();
199: $this->out(sprintf("Bake View\nPath: %s", $this->getPath()));
200: $this->hr();
201:
202: $this->DbConfig->interactive = $this->Controller->interactive = $this->interactive = true;
203:
204: if (empty($this->connection)) {
205: $this->connection = $this->DbConfig->getConfig();
206: }
207:
208: $this->Controller->connection = $this->connection;
209: $this->controllerName = $this->Controller->getName();
210:
211: $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);
212: $interactive = $this->in($prompt, array('y', 'n'), 'n');
213:
214: if (strtolower($interactive) === 'n') {
215: $this->interactive = false;
216: }
217:
218: $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).");
219: $wannaDoScaffold = $this->in($prompt, array('y', 'n'), 'y');
220:
221: $wannaDoAdmin = $this->in(__d('cake_console', "Would you like to create the views for admin routing?"), array('y', 'n'), 'n');
222:
223: if (strtolower($wannaDoScaffold) === 'y' || strtolower($wannaDoAdmin) === 'y') {
224: $vars = $this->_loadController();
225: if (strtolower($wannaDoScaffold) === 'y') {
226: $actions = $this->scaffoldActions;
227: $this->bakeActions($actions, $vars);
228: }
229: if (strtolower($wannaDoAdmin) === 'y') {
230: $admin = $this->Project->getPrefix();
231: $regularActions = $this->scaffoldActions;
232: $adminActions = array();
233: foreach ($regularActions as $action) {
234: $adminActions[] = $admin . $action;
235: }
236: $this->bakeActions($adminActions, $vars);
237: }
238: $this->hr();
239: $this->out();
240: $this->out(__d('cake_console', "View Scaffolding Complete.\n"));
241: } else {
242: $this->customAction();
243: }
244: }
245:
246: 247: 248: 249: 250: 251: 252: 253: 254:
255: protected function _loadController() {
256: if (!$this->controllerName) {
257: $this->err(__d('cake_console', 'Controller not found'));
258: }
259:
260: $plugin = null;
261: if ($this->plugin) {
262: $plugin = $this->plugin . '.';
263: }
264:
265: $controllerClassName = $this->controllerName . 'Controller';
266: App::uses($controllerClassName, $plugin . 'Controller');
267: if (!class_exists($controllerClassName)) {
268: $file = $controllerClassName . '.php';
269: $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));
270: return $this->_stop();
271: }
272: $controllerObj = new $controllerClassName();
273: $controllerObj->plugin = $this->plugin;
274: $controllerObj->constructClasses();
275: $modelClass = $controllerObj->modelClass;
276: $modelObj = $controllerObj->{$controllerObj->modelClass};
277:
278: if ($modelObj) {
279: $primaryKey = $modelObj->primaryKey;
280: $displayField = $modelObj->displayField;
281: $singularVar = Inflector::variable($modelClass);
282: $singularHumanName = $this->_singularHumanName($this->controllerName);
283: $schema = $modelObj->schema(true);
284: $fields = array_keys($schema);
285: $associations = $this->_associations($modelObj);
286: } else {
287: $primaryKey = $displayField = null;
288: $singularVar = Inflector::variable(Inflector::singularize($this->controllerName));
289: $singularHumanName = $this->_singularHumanName($this->controllerName);
290: $fields = $schema = $associations = array();
291: }
292: $pluralVar = Inflector::variable($this->controllerName);
293: $pluralHumanName = $this->_pluralHumanName($this->controllerName);
294:
295: return compact('modelClass', 'schema', 'primaryKey', 'displayField', 'singularVar', 'pluralVar',
296: 'singularHumanName', 'pluralHumanName', 'fields', 'associations');
297: }
298:
299: 300: 301: 302: 303: 304: 305:
306: public function bakeActions($actions, $vars) {
307: foreach ($actions as $action) {
308: $content = $this->getContent($action, $vars);
309: $this->bake($action, $content);
310: }
311: }
312:
313: 314: 315: 316: 317:
318: public function customAction() {
319: $action = '';
320: while (!$action) {
321: $action = $this->in(__d('cake_console', 'Action Name? (use lowercase_underscored function name)'));
322: if (!$action) {
323: $this->out(__d('cake_console', 'The action name you supplied was empty. Please try again.'));
324: }
325: }
326: $this->out();
327: $this->hr();
328: $this->out(__d('cake_console', 'The following view will be created:'));
329: $this->hr();
330: $this->out(__d('cake_console', 'Controller Name: %s', $this->controllerName));
331: $this->out(__d('cake_console', 'Action Name: %s', $action));
332: $this->out(__d('cake_console', 'Path: %s', $this->getPath() . $this->controllerName . DS . Inflector::underscore($action) . ".ctp"));
333: $this->hr();
334: $looksGood = $this->in(__d('cake_console', 'Look okay?'), array('y', 'n'), 'y');
335: if (strtolower($looksGood) === 'y') {
336: $this->bake($action, ' ');
337: return $this->_stop();
338: }
339: $this->out(__d('cake_console', 'Bake Aborted.'));
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 $type) {
456: foreach ($model->{$type} as $assocKey => $assocData) {
457: list(, $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: