Location: PHPKode > projects > Recess PHP Framework > recess/recess/apps/tools/controllers/RecessToolsAppsController.class.php
<?php
Library::import('recess.framework.controllers.Controller');
Library::import('recess.database.pdo.RecessType');

/**
 * !RespondsWith Layouts, Json
 * !Prefix apps/
 */
class RecessToolsAppsController extends Controller {
	
	public function init() {
		if(RecessConf::$mode == RecessConf::PRODUCTION) {
			throw new RecessResponseException('Recess Tools are available only during development. Please disable the application in a production environment.', ResponseCodes::HTTP_NOT_FOUND, array());
		}
	}
	
	/** !Route GET */
	public function home() {
		$this->apps = RecessConf::$applications;
		if(isset($this->request->get->flash)) {
			$this->flash = $this->request->get['flash'];
		}
	}
	
	/** !Route GET, uninstall/$appClass */
	public function uninstall($appClass) {
		//Library::getFullyQualifiedClassName($appClass);
		$this->app = new $appClass;
	}
	
	/** !Route GET, new */
	public function newApp() {
		$writeable = is_writable($_ENV['dir.apps']);
		
		$this->appsDirWriteable = $writeable;
		if($this->appsDirWriteable) {
			$this->form = $this->getNewAppForm();
			return $this->ok('newAppWizard');
		} else {
			return $this->ok('newAppInstructions');
		}
	}
	
	/** !Route POST, new */
	public function newAppPost() {
		$form = $this->getNewAppForm($this->request->post);
		$form->assertNotEmpty('appName');
		$form->assertNotEmpty('programmaticName');
		if($form->hasErrors()) {
			$this->form = $form;
			return $this->conflict('newAppWizard');
		} else {
			Library::import('recess.lang.Inflector');
			$this->form = $this->getNewAppStep2Form($this->request->post);
			$this->form->routingPrefix->setValue(Inflector::toCamelCaps($this->form->programmaticName->getValue()) . '/');
			return $this->ok('newAppWizardStep2');
		}
	}
	
	/** !Route POST, new/step2 */
	function newAppStep2 () {
		$form = $this->getNewAppStep2Form($this->request->post);
		$this->generateApp();
		return $this->ok('newAppWizardComplete');
	}
	
	private function generateApp() {
		Library::import('recess.lang.Inflector');
		
		$appName = $this->request->post['appName'];
		$programmaticName = Inflector::toProperCaps($this->request->post['programmaticName']);
		$camelProgrammaticName = Inflector::toCamelCaps($programmaticName);
		
		$this->applicationClass = $programmaticName . 'Application';
		$this->applicationFullClass = $camelProgrammaticName . '.' . $this->applicationClass;
		$this->appName = $appName;
		
		$routesPrefix = $this->request->post['routingPrefix'];
		if(substr($routesPrefix,-1) != '/') { $routesPrefix .= '/'; }
		$appDir = $_ENV['dir.apps'] . $camelProgrammaticName;
		
		$this->messages = array();
		$this->messages[] = $this->tryCreatingDirectory($appDir, 'application');
		
		$appReplacements = array('appName' => $appName, 'programmaticName' => $programmaticName, 'camelProgrammaticName' => $camelProgrammaticName, 'routesPrefix' => $routesPrefix);
		$this->messages[] = $this->tryGeneratingFile('Application Class', $this->application->codeTemplatesDir . 'Application.template.php', $appDir . '/' . $programmaticName . 'Application.class.php', $appReplacements);
		
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/models', 'models');
		
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/controllers', 'controllers');
		$this->messages[] = $this->tryGeneratingFile('Home Controller', $this->application->codeTemplatesDir . 'scaffolding/controllers/HomeController.template.php', $appDir . '/controllers/' . $programmaticName . 'HomeController.class.php', $appReplacements);
		
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/views', 'views');
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/views/parts', 'common parts');
		$this->messages[] = $this->tryGeneratingFile('Navigation Part', $this->application->codeTemplatesDir . 'scaffolding/views/parts/navigation.part.template.php', $appDir . '/views/parts/navigation.part.php', $appReplacements);
		$this->messages[] = $this->tryGeneratingFile('Style Part', $this->application->codeTemplatesDir . 'scaffolding/views/parts/style.part.template.php', $appDir . '/views/parts/style.part.php', $appReplacements);
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/views/home', 'home views');
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/views/layouts', 'layouts');
		$this->messages[] = $this->tryGeneratingFile('Home Template', $this->application->codeTemplatesDir . 'scaffolding/views/home/index.template.php', $appDir . '/views/home/index.html.php', $appReplacements);
		$this->messages[] = $this->tryGeneratingFile('Master Layout', $this->application->codeTemplatesDir . 'scaffolding/views/master.layout.template.php', $appDir . '/views/layouts/master.layout.php', $appReplacements);
		
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/public', 'public');
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/public/css', 'css');
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/public/js', 'javascript');
		$this->messages[] = $this->tryCreatingDirectory($appDir . '/public/img', 'images');
	}
	
	private function tryCreatingDirectory($path, $name) {
		$message = '';
		try { 
			$message = 'Creating ' . $name . ' dir "' . $path . '" ... ';
			mkdir($path);
			$message .= 'ok.';
		} catch (Exception $e) {
			if(file_exists($path)) $message .= ' already exists.';
			else $message .= 'failed.';
		}
		return $message;
	}
	
	private function tryGeneratingFile($name, $template, $outputFile, $values, $allowSlashes = false) {
		$templateContents = file_get_contents($template);
		$search = array_keys($values);
		foreach($search as $key => $value) {
			$search[$key] = '/\{\{' . $value . '\}\}/';
		}
		$replace = array_values($values);
		foreach($replace as $key => $value) {
			if(!$allowSlashes) { 
				$value = addSlashes($value);
			}
			$replace[$key] = $value;
		}
		$output = preg_replace($search,$replace,$templateContents);
		
		$message = '';
		try {
			$message = 'Generating ' . $name . ' at "' . $outputFile . '" ... ';
			if(file_exists($outputFile)) {
				throw new Exception('file exists');
			}
			file_put_contents($outputFile, $output);
			$message .= 'ok.';
		} catch(Exception $e) {
			if(file_exists($outputFile)) $message .= ' already exists. Not overwriting.';
			else $message .= 'failed.';
		}
		return $message;
	}
	
	private function getNewAppForm($fillValues = array()) {
		Library::import('recess.framework.forms.Form');
		$form = new Form('');
		$form->method = "POST";
		$form->flash = "";
		$form->action = $this->urlTo('newApp');
		$form->inputs['appName'] = new TextInput('appName', '', '','');
		$form->inputs['programmaticName'] = new TextInput('programmaticName', '', '','');
		$form->fill($fillValues);
		return $form;
	}
	
	private function getNewAppStep2Form($fillValues = array()) {
		Library::import('recess.framework.forms.Form');
		$form = new Form('');
		$form->method = "POST";
		$form->flash = "";
		$form->action = $this->urlTo('newAppStep2');
		$form->inputs['appName'] = new HiddenInput('appName', '');
		$form->inputs['programmaticName'] = new HiddenInput('programmaticName', '');
		$form->inputs['routingPrefix'] = new TextInput('routingPrefix', '','','');
		$form->fill($fillValues);
		return $form;
	}

	/** !Route GET, $appClass */
	public function app($appClass) {
		$application = $this->getApplication($appClass);
		if(!$application instanceof Application) {
			return $application; // App not found
		}
		
		$this->app = $application;
	}
	
	/** !Route GET, app/$app/model/gen */
	public function createModel($app) {
		$this->sources = Databases::getSources();
		$this->tables = Databases::getDefaultSource()->getTables();
		$this->app = $app;
	}
	
	/** !Route POST, app/$app/model/gen */
	public function generateModel($app) {
		$values = $this->request->post;
		
		$modelName = $values['modelName'];
		$tableExists = $values['tableExists'] == 'yes' ? true : false;
		if($tableExists) {
			$dataSource = $values['existingDataSource'];
			$createTable = false;
			$tableName = $values['existingTableName'];
		} else {
			$dataSource = $values['dataSource'];
			$createTable = $values['createTable'] == 'Yes' ? true : false;
			$tableName = $values['tableName'];
		}
		$propertyNames = $values['fields'];
		$primaryKey = $values['primaryKey'];
		$types = $values['types'];
		
		Library::import('recess.database.orm.Model', true); 
		// Forcing b/c ModelDescriptor is in Model
			
		$modelDescriptor = new ModelDescriptor($modelName, false);
		$modelDescriptor->setSource($dataSource);
		$modelDescriptor->setTable($tableName, false);
		
		$pkFound = false;
		foreach($propertyNames as $i => $name) {
			if($name == "") continue;
			$property = new ModelProperty();
			$property->name = trim($name);
			if($name == $primaryKey) {
				$property->isPrimaryKey = true;
			}
			if($types[$i] == 'Integer Autoincrement') {
				if($property->isPrimaryKey) {
					$property->type = RecessType::INTEGER;
					$property->isAutoIncrement = true;
				} else {
					$property->type = RecessType::INTEGER;
				}
			} else {
				$property->type = $types[$i];
			}
			$modelDescriptor->properties[] = $property;
		}
		
		Library::import('recess.database.orm.ModelGen');
		$this->modelCode = ModelGen::toCode($modelDescriptor, $_ENV['dir.temp'] . 'Model.class.php');
		
		$app = new $app;
		if(strpos($app->modelsPrefix,'recess.apps.') !== false) {
			$base = $_ENV['dir.recess'];
		} else {
			$base = $_ENV['dir.apps'];
		}
		$path = $base . str_replace(Library::dotSeparator,Library::pathSeparator,$app->modelsPrefix);
		$path .= $modelName . '.class.php';
		$this->path = $path;
		
		$this->modelWasSaved = false;
		$this->codeGenMessage = '';
		try {
			if(file_exists($this->path)) {
				if(file_get_contents($this->path) == $this->modelCode) {
					$this->modelWasSaved = true;
				} else {
					$this->codeGenMessage = 'File already exists!';
				}
			} else {
				file_put_contents($this->path, $this->modelCode);			
				$this->modelWasSaved = true;
			}
		} catch(Exception $e) {	
			$this->codeGenMessage = 'File could not be saved. Is models directory writeable?';
			$this->modelWasSaved = false;
		}
		
		$this->modelName = $modelName;
		$this->appName = get_class($app);
		$this->tableGenAttempted = $createTable;
		$this->tableWasCreated = false;
		$this->tableSql = '';
		if($createTable) {
			$modelSource = Databases::getSource($dataSource);
			$this->tableSql = $modelSource->createTableSql($modelDescriptor);
			try {
				$modelSource->exec($this->tableSql);
				$this->tableWasCreated = true;
			} catch(Exception $e) {
				$this->tableWasCreated = false;
			}
		}
		
		return $this->ok('createModelComplete');
	}
	
	/** !Route GET, $app/model/$model/scaffolding */
	public function generateScaffolding($app, $model) {
		$app = new $app;
		if(strpos($app->controllersPrefix,'recess.apps.') !== false) {
			$base = $_ENV['dir.recess'];
		} else {
			$base = $_ENV['dir.apps'];
		}
		Library::import('recess.lang.Inflector');
		$controllersDir = $base . str_replace(Library::dotSeparator,Library::pathSeparator,$app->controllersPrefix);
		$viewsDir = $app->viewsDir;
		
		Library::import($app->modelsPrefix . $model);
		$replacements = 
			array(	'modelName' => $model, 
					'modelNameLower' => Inflector::toCamelCaps($model),
					'fullyQualifiedModel' => $app->modelsPrefix . $model, 
					'primaryKey' => Model::primaryKeyName($model),
					'viewsPrefix' => Inflector::toCamelCaps($model),
					'routesPrefix' => Inflector::toCamelCaps($model),);
		
		$this->messages[] = $this->tryGeneratingFile('RESTful ' . $model . ' Controller', $this->application->codeTemplatesDir . 'scaffolding/controllers/ResourceController.template.php', $controllersDir . $model . 'Controller.class.php', $replacements);
		
		$indexFieldTemplate = $this->getTemplate($this->application->codeTemplatesDir . 'scaffolding/views/resource/indexField.template.php');
		$indexDateFieldTemplate = $this->getTemplate($this->application->codeTemplatesDir . 'scaffolding/views/resource/indexDateField.template.php');
		$editFormInputTemplate = $this->getTemplate($this->application->codeTemplatesDir . 'scaffolding/views/resource/editFormInput.template.php');
		
		$indexFields = '';
		$formFields = '';
		foreach(Model::getProperties($model) as $property) {
			if($property->isPrimaryKey) continue;
			$values = array(
							'fieldName' => $property->name,
							'primaryKey' => Model::primaryKeyName($model),
							'modelName' => $model,
							'modelNameLower' => Inflector::toCamelCaps($model),
							'fieldNameEnglish' => Inflector::toEnglish($property->name) );
			switch($property->type) {
				case RecessType::DATE:
				case RecessType::DATETIME:
				case RecessType::TIME:
				case RecessType::TIMESTAMP:
					$template = $indexDateFieldTemplate;
					break;
				default:
					$template = $indexFieldTemplate;
					break;
			}
			$formFields .= $this->fillTemplate($editFormInputTemplate, $values);
			$indexFields .= $this->fillTemplate($template, $values);
		}
		
		$replacements['fields'] = $indexFields;
		$replacements['editFields'] = $formFields;
		
		$viewsDir = $app->viewsDir . $replacements['viewsPrefix'] . '/';
		$this->messages[] = $this->tryCreatingDirectory($viewsDir, $model . ' views dir');
		$this->messages[] = $this->tryGeneratingFile('resource layout', $this->application->codeTemplatesDir . 'scaffolding/views/resource/resource.layout.template.php', $viewsDir . '../layouts/' . $replacements['viewsPrefix'] . '.layout.php', $replacements);
		$this->messages[] = $this->tryGeneratingFile('index view', $this->application->codeTemplatesDir . 'scaffolding/views/resource/index.template.php', $viewsDir . 'index.html.php', $replacements);
		$this->messages[] = $this->tryGeneratingFile('editForm view', $this->application->codeTemplatesDir . 'scaffolding/views/resource/editForm.template.php', $viewsDir . 'editForm.html.php', $replacements, true);
		$this->messages[] = $this->tryGeneratingFile('form part', $this->application->codeTemplatesDir . 'scaffolding/views/resource/form.part.template.php', $viewsDir . 'form.part.php', $replacements, true);
		$this->messages[] = $this->tryGeneratingFile('static details', $this->application->codeTemplatesDir . 'scaffolding/views/resource/details.template.php', $viewsDir . 'details.html.php', $replacements);
		$this->messages[] = $this->tryGeneratingFile('details part', $this->application->codeTemplatesDir . 'scaffolding/views/resource/details.part.template.php', $viewsDir . 'details.part.php', $replacements);
		$this->appName = get_class($app);
		$this->modelName = $model;
	}
	
	protected function getTemplate($templateFile) {
		try {
			return file_get_contents($templateFile);
		} catch (Exception $e) {
			return '';
		}
	}
	
	protected function fillTemplate($template, $values) {
		$search = array_keys($values);
		foreach($search as $key => $value) {
			$search[$key] = '/\{\{' . $value . '\}\}/';
		}
		$replace = array_values($values);
		foreach($replace as $key => $value) {
			$replace[$key] = addslashes($value);
		}
		return preg_replace($search,$replace,$template);
	}
	
	/** !Route GET, model/gen/analyzeModelName/$modelName */
	public function analyzeModelName($modelName) {
		Library::import('recess.lang.Inflector');
		$this->tableName = Inflector::toPlural(Inflector::toUnderscores($modelName));
		$this->isValid = preg_match('/^[a-zA-Z][_a-zA-z0-9]*$/', $modelName) == 1;
	}
	
	/** !Route GET, model/gen/getTables/$sourceName */
	public function getTables($sourceName) {
		$this->tables = Databases::getSource($sourceName)->getTables();
	}
	
	/** !Route GET, model/gen/getTableProps/$sourceName/$tableName */
	public function getTableProps($sourceName, $tableName) {
		$source = Databases::getSource($sourceName);
		if($source == null) {
			return $this->redirect($this->urlTo('home'));
		} else {
			$this->source = $source;
		}
		$this->sourceName = $sourceName;
		$this->table = $tableName;
		$this->columns = $this->source->getTableDescriptor($tableName)->getColumns();
	}
	
	/** !Route GET, $app/controller/gen */
	public function createController($app) {
		
		$application = $this->getApplication($app);
		if(!$application instanceof Application) {
			return $application; // App not found
		}
		
		$this->app = $application;
		
		return $this->ok('genController');
	}
	
	private function getApplication($appClass) {
		foreach(RecessConf::$applications as $app) {
			if(get_class($app) == $appClass) {
				return $app;
			}
		}
		return $this->forwardNotFound($this->urlTo('home'), 'Application ' . $appClass . ' does not exist or is not enabled.');
	}
	
}

?>
Return current item: Recess PHP Framework