├── .gitignore ├── App ├── App.php ├── Config │ ├── config.dist.json │ └── routes.json ├── Controller │ ├── DashboardController.php │ ├── DefaultController.php │ └── NewsController.php ├── Model │ ├── DashboardModel.php │ ├── DefaultModel.php │ └── NewsModel.php ├── Router │ └── AppRouter.php ├── Service │ ├── ConfigServiceProvider.php │ └── DatabaseServiceProvider.php ├── Setup │ ├── InstallScript.php │ └── sampleData.sql ├── Table │ ├── DefaultDatabaseTable.php │ └── NewsTable.php ├── Templates │ ├── dashboard │ │ └── dashboard.index.twig │ ├── index.twig │ └── news │ │ ├── news.edit.twig │ │ ├── news.index.twig │ │ └── news.view.twig └── View │ ├── Dashboard │ └── DashboardHtmlView.php │ ├── DefaultHtmlView.php │ ├── News │ └── NewsHtmlView.php │ └── Renderer │ ├── RendererInterface.php │ ├── Twig.php │ └── TwigExtension.php ├── LICENSE ├── README.md ├── composer.json ├── composer.lock └── www ├── .htaccess ├── index.php └── themes └── awesome ├── css └── theme.css └── images └── logo.png /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE based files and folders # 2 | .buildpath 3 | .project 4 | .settings 5 | .DS_Store 6 | .idea 7 | 8 | # Files and folders that should not be pushed to the repo # 9 | App/Config/config.json 10 | www/assets/css 11 | www/assets/js 12 | www/assets/img 13 | 14 | # Apple Files # 15 | .DS_STORE 16 | 17 | # COMPOSER Files # 18 | vendor/* 19 | composer.phar 20 | -------------------------------------------------------------------------------- /App/App.php: -------------------------------------------------------------------------------- 1 | setContainer($container); 62 | 63 | // Merge the config into the application 64 | $config = $container->get('config'); 65 | $this->config->merge($config); 66 | 67 | $this->theme = $this->config->get('theme.default'); 68 | 69 | define('BASE_URL', $this->get('uri.base.full')); 70 | define('DEFAULT_THEME', BASE_URL . 'themes/' . $this->theme); 71 | } 72 | 73 | /** 74 | * Method to run the Web application routines. 75 | * 76 | * @return void 77 | * 78 | * @since 1.0 79 | */ 80 | protected function doExecute() 81 | { 82 | try 83 | { 84 | // Instantiate the router 85 | $router = new AppRouter($this->input, $this); 86 | $maps = json_decode(file_get_contents(JPATH_CONFIGURATION . '/routes.json')); 87 | 88 | if (!$maps) 89 | { 90 | throw new \RuntimeException('Invalid router file.', 500); 91 | } 92 | 93 | $router->addMaps($maps, true); 94 | $router->setControllerPrefix('\\App'); 95 | $router->setDefaultController('\\Controller\\DefaultController'); 96 | 97 | // Fetch the controller 98 | /* @type ControllerInterface $controller */ 99 | $controller = $router->getController($this->get('uri.route')); 100 | $content = $controller->execute(); 101 | } 102 | catch (\Exception $exception) 103 | { 104 | header('HTTP/1.1 500 Internal Server Error', true, 500); 105 | 106 | $content = $exception->getMessage(); 107 | } 108 | 109 | $this->setBody($content); 110 | } 111 | 112 | /** 113 | * Enqueue a system message. 114 | * 115 | * @param string $msg The message to enqueue. 116 | * @param string $type The message type. Default is message. 117 | * 118 | * @return $this Method allows chaining 119 | * 120 | * @since 1.0 121 | */ 122 | public function enqueueMessage($msg, $type = 'message') 123 | { 124 | $this->getSession()->getFlashBag()->add($type, $msg); 125 | 126 | return $this; 127 | } 128 | 129 | /** 130 | * Get the DI container. 131 | * 132 | * @return Container 133 | * 134 | * @since 1.0 135 | */ 136 | public function getContainer() 137 | { 138 | return $this->container; 139 | } 140 | 141 | /** 142 | * Get a session object. 143 | * 144 | * @return Session 145 | * 146 | * @since 1.0 147 | */ 148 | public function getSession() 149 | { 150 | if (is_null($this->newSession)) 151 | { 152 | $this->newSession = new Session; 153 | $this->newSession->start(); 154 | } 155 | 156 | return $this->newSession; 157 | } 158 | 159 | /** 160 | * Clear the system message queue. 161 | * 162 | * @return void 163 | * 164 | * @since 1.0 165 | */ 166 | public function clearMessageQueue() 167 | { 168 | $this->getSession()->getFlashBag()->clear(); 169 | } 170 | 171 | /** 172 | * Get the system message queue. 173 | * 174 | * @return array The system message queue. 175 | * 176 | * @since 1.0 177 | */ 178 | public function getMessageQueue() 179 | { 180 | return $this->getSession()->getFlashBag()->peekAll(); 181 | } 182 | 183 | /** 184 | * Set the DI container. 185 | * 186 | * @param Container $container The DI container. 187 | * 188 | * @return $this Method allows chaining 189 | * 190 | * @since 1.0 191 | */ 192 | public function setContainer(Container $container) 193 | { 194 | $this->container = $container; 195 | 196 | return $this; 197 | } 198 | 199 | /** 200 | * Set the system message queue for a given type. 201 | * 202 | * @param string $type The type of message to set 203 | * @param mixed $message Either a single message or an array of messages 204 | * 205 | * @return void 206 | * 207 | * @since 1.0 208 | */ 209 | public function setMessageQueue($type, $message = '') 210 | { 211 | $this->getSession()->getFlashBag()->set($type, $message); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /App/Config/config.dist.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "driver": "mysql", 4 | "host": "localhost", 5 | "user": "root", 6 | "password": "root", 7 | "name": "framework-app", 8 | "prefix": "app_" 9 | }, 10 | "renderer": { 11 | "type": "twig" 12 | }, 13 | "theme": { 14 | "default": "awesome" 15 | }, 16 | "system": { 17 | "list_limit": "20", 18 | "gzip": "0", 19 | "offset": "UTC" 20 | }, 21 | "languages": [ 22 | "en-GB" 23 | ] 24 | } -------------------------------------------------------------------------------- /App/Config/routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "dashboard": "\\Controller\\DashboardController", 3 | "dashboard/:task": "\\Controller\\DashboardController", 4 | "news": "\\Controller\\NewsController", 5 | "news/:task": "\\Controller\\NewsController", 6 | "news/:task/:id": "\\Controller\\NewsController" 7 | } -------------------------------------------------------------------------------- /App/Controller/DashboardController.php: -------------------------------------------------------------------------------- 1 | getInput(), $this->getContainer()->get('db')); 34 | $dashboardModel->updateDatabase($this->getContainer()->get('config')); 35 | 36 | $this->getInput()->set('success', true); 37 | $this->getApplication()->redirect($this->getApplication()->get('uri.base.path')); 38 | } 39 | catch (\Exception $e) 40 | { 41 | throw new \RuntimeException(sprintf('Error: ' . $e->getMessage())); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /App/Controller/DefaultController.php: -------------------------------------------------------------------------------- 1 | getInput(); 52 | 53 | $task = $input->get('task','view'); 54 | 55 | // Get some data from the request 56 | $vName = $input->getWord('view', $this->defaultView); 57 | $vFormat = $input->getWord('format', 'html'); 58 | 59 | //TODO 60 | if (is_null($input->get('layout'))) 61 | { 62 | if ($task == 'view' && $input->get('id') == null) 63 | { 64 | $input->set('layout', 'index'); 65 | } 66 | elseif ($task == 'view') 67 | { 68 | $input->set('layout', 'view'); 69 | } 70 | elseif ($task != null) 71 | { 72 | $this->$task(); 73 | } 74 | } 75 | 76 | $lName = $input->get('layout'); 77 | 78 | $input->set('view', $vName); 79 | 80 | $base = '\\App'; 81 | 82 | $vClass = $base . '\\View\\' . ucfirst($vName) . '\\' . ucfirst($vName) . ucfirst($vFormat) . 'View'; 83 | $mClass = $base . '\\Model\\' . ucfirst($vName) . 'Model'; 84 | 85 | // If a model doesn't exist for our view, revert to the default model 86 | if (!class_exists($mClass)) 87 | { 88 | $mClass = $base . '\\Model\\DefaultModel'; 89 | 90 | // If there still isn't a class, panic. 91 | if (!class_exists($mClass)) 92 | { 93 | throw new \RuntimeException(sprintf('No model found for view %s', $vName)); 94 | } 95 | } 96 | 97 | // Make sure the view class exists, otherwise revert to the default 98 | if (!class_exists($vClass)) 99 | { 100 | $vClass = '\\App\\View\\DefaultHtmlView'; 101 | 102 | // If there still isn't a class, panic. 103 | if (!class_exists($vClass)) 104 | { 105 | throw new \RuntimeException(sprintf('Class %s not found', $vClass)); 106 | } 107 | } 108 | 109 | // Register the templates paths for the view 110 | $paths = array(); 111 | 112 | $path = JPATH_TEMPLATES . '/' . $vName . '/'; 113 | 114 | if (is_dir($path)) 115 | { 116 | $paths[] = $path; 117 | } 118 | 119 | /* @type DefaultHtmlView $view */ 120 | $view = new $vClass($this->getApplication(), new $mClass($this->getInput(), $this->getContainer()->get('db')), $paths); 121 | $view->setLayout($vName . '.' . $lName); 122 | 123 | try 124 | { 125 | // Render our view. 126 | return $view->render(); 127 | } 128 | catch (\Exception $e) 129 | { 130 | throw new \RuntimeException(sprintf('Error: ' . $e->getMessage())); 131 | } 132 | 133 | return; 134 | } 135 | 136 | /** 137 | * Get the DI container. 138 | * 139 | * @return Container 140 | * 141 | * @since 1.0 142 | */ 143 | public function getContainer() 144 | { 145 | return $this->container; 146 | } 147 | 148 | /** 149 | * Set the DI container. 150 | * 151 | * @param Container $container The DI container. 152 | * 153 | * @return $this Method allows chaining 154 | * 155 | * @since 1.0 156 | */ 157 | public function setContainer(Container $container) 158 | { 159 | $this->container = $container; 160 | 161 | return $this; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /App/Controller/NewsController.php: -------------------------------------------------------------------------------- 1 | database->driver = $this->input->get('driver', $oldConfig->get('database.driver')); 42 | $config->database->user = $this->input->get('user', $oldConfig->get('database.user')); 43 | $config->database->password = $this->input->get('password', $oldConfig->get('database.password')); 44 | $config->database->name = $this->input->get('name', $oldConfig->get('database.name')); 45 | $config->database->host = $this->input->get('host', $oldConfig->get('database.host')); 46 | $config->database->prefix = $this->input->get('prefix', $oldConfig->get('database.prefix')); 47 | 48 | file_put_contents($file, json_encode($config)); 49 | 50 | if ($this->input->get('install_sample_data')) 51 | { 52 | $this->installSampleData(); 53 | } 54 | 55 | return true; 56 | } 57 | 58 | /** 59 | * Install sample data for the application 60 | * 61 | * @return boolean 62 | * 63 | * @since 1.0 64 | * @throws \RuntimeException 65 | * @throws \UnexpectedValueException 66 | */ 67 | public function installSampleData() 68 | { 69 | $sampleData = JPATH_SETUP . '/sampleData.sql'; 70 | 71 | if (!is_readable($sampleData)) 72 | { 73 | throw new \RuntimeException('Sample Data file does not exist or is unreadable'); 74 | } 75 | 76 | $sql = file_get_contents($sampleData); 77 | 78 | if (false == $sql) 79 | { 80 | throw new \UnexpectedValueException('SQL file corrupted.'); 81 | } 82 | 83 | foreach ($this->db->splitSql($sql) as $query) 84 | { 85 | $q = trim($this->db->replacePrefix($query)); 86 | 87 | if ('' == trim($q)) 88 | { 89 | continue; 90 | } 91 | 92 | $this->db->setQuery($q)->execute(); 93 | } 94 | 95 | return true; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /App/Model/DefaultModel.php: -------------------------------------------------------------------------------- 1 | input = $input; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /App/Model/NewsModel.php: -------------------------------------------------------------------------------- 1 | input->getUint('id'); 34 | $alias = $this->input->get('id'); 35 | 36 | if (!$id && !$alias) 37 | { 38 | throw new \UnexpectedValueException('No news identifier provided.'); 39 | } 40 | 41 | $query = $this->db->getQuery(true) 42 | ->select('a.*') 43 | ->from($this->db->quoteName('#__news','a')); 44 | 45 | if ($id) 46 | { 47 | $query->where($this->db->quoteName('a.news_id') . ' = ' . (int) $id); 48 | } 49 | elseif ($alias) 50 | { 51 | $query->where($this->db->quoteName('a.alias') . ' = ' . $this->db->quote($alias)); 52 | } 53 | 54 | return $this->db->setQuery($query)->loadObject(); 55 | } 56 | 57 | /** 58 | * Retrieve all news items 59 | * 60 | * @return object Container with news items 61 | * 62 | * @since 1.0 63 | */ 64 | public function getItems() 65 | { 66 | $query = $this->db->getQuery(true) 67 | ->select('a.*') 68 | ->from($this->db->quoteName('#__news','a')); 69 | 70 | return $this->db->setQuery($query)->loadObjectList(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /App/Router/AppRouter.php: -------------------------------------------------------------------------------- 1 | input); 42 | 43 | $this->app = $app; 44 | } 45 | 46 | /** 47 | * Get a ControllerInterface object for a given name. 48 | * 49 | * @param string $name The controller name (excluding prefix) for which to fetch and instance. 50 | * 51 | * @return ControllerInterface 52 | * 53 | * @since 1.0 54 | * @throws \RuntimeException 55 | */ 56 | protected function fetchController($name) 57 | { 58 | // Derive the controller class name. 59 | $class = $this->controllerPrefix . ucfirst($name); 60 | 61 | // If the controller class does not exist panic. 62 | if (!class_exists($class) || !is_subclass_of($class, 'Joomla\\Controller\\ControllerInterface')) 63 | { 64 | throw new \RuntimeException(sprintf('Unable to locate controller `%s`.', $class), 404); 65 | } 66 | 67 | // Instantiate the controller. 68 | $controller = new $class($this->input, $this->app); 69 | 70 | if ($controller instanceof ContainerAwareInterface) 71 | { 72 | $controller->setContainer($this->app->getContainer()); 73 | } 74 | 75 | return $controller; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /App/Service/ConfigServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadObject($configObject); 58 | 59 | $this->config = $config; 60 | } 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public function register(Container $container) 66 | { 67 | $config = $this->config; 68 | 69 | $container->share( 70 | 'config', 71 | function () use ($config) 72 | { 73 | return $config; 74 | } 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /App/Service/DatabaseServiceProvider.php: -------------------------------------------------------------------------------- 1 | set('Joomla\\Database\\DatabaseDriver', 26 | function () use ($container) 27 | { 28 | $config = $container->get('config'); 29 | 30 | $options = array( 31 | 'driver' => $config->get('database.driver'), 32 | 'host' => $config->get('database.host'), 33 | 'user' => $config->get('database.user'), 34 | 'password' => $config->get('database.password'), 35 | 'database' => $config->get('database.name'), 36 | 'prefix' => $config->get('database.prefix') 37 | ); 38 | 39 | $db = DatabaseDriver::getInstance($options); 40 | $db->setDebug($config->get('debug.database', false)); 41 | 42 | return $db; 43 | }, true, true 44 | ); 45 | 46 | // Alias the database 47 | $container->alias('db', 'Joomla\\Database\\DatabaseDriver'); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /App/Setup/InstallScript.php: -------------------------------------------------------------------------------- 1 | tableName = $table; 75 | $this->db = $db; 76 | $this->tableFields = new \stdClass; 77 | 78 | // Set the key to be an array. 79 | if (is_string($keys)) 80 | { 81 | $keys = array($keys); 82 | } 83 | elseif (is_object($keys)) 84 | { 85 | $keys = (array) $keys; 86 | } 87 | 88 | $this->tableKeys = $keys; 89 | 90 | $this->autoIncrement = (count($keys) == 1) ? true : false; 91 | 92 | // Initialise the table properties. 93 | $fields = $this->getFields(); 94 | 95 | if ($fields) 96 | { 97 | foreach ($fields as $name => $v) 98 | { 99 | // Add the field if it is not already present. 100 | $this->tableFields->$name = null; 101 | } 102 | } 103 | } 104 | 105 | /** 106 | * Magic setter to set a table field. 107 | * 108 | * @param string $key The key name. 109 | * @param mixed $value The value to set. 110 | * 111 | * @return void 112 | * 113 | * @since 1.0 114 | * @throws \InvalidArgumentException 115 | */ 116 | public function __set($key, $value) 117 | { 118 | if (isset($this->tableFields->$key) || is_null($this->tableFields->$key)) 119 | { 120 | $this->tableFields->$key = $value; 121 | } 122 | else 123 | { 124 | throw new \InvalidArgumentException(__METHOD__ . ' - Set unknown property: ' . $key); 125 | } 126 | } 127 | 128 | /** 129 | * Magic getter to get a table field. 130 | * 131 | * @param string $key The key name. 132 | * 133 | * @return mixed 134 | * 135 | * @since 1.0 136 | * @throws \InvalidArgumentException 137 | */ 138 | public function __get($key) 139 | { 140 | if (isset($this->tableFields->$key) || is_null($this->tableFields->$key)) 141 | { 142 | return $this->tableFields->$key; 143 | } 144 | 145 | throw new \InvalidArgumentException(__METHOD__ . ' - Get unknown property: ' . $key); 146 | } 147 | 148 | /** 149 | * Method to provide a shortcut to binding, checking and storing a AbstractDatabaseTable 150 | * instance to the database table. The method will check a row in once the 151 | * data has been stored and if an ordering filter is present will attempt to 152 | * reorder the table rows based on the filter. The ordering filter is an instance 153 | * property name. The rows that will be reordered are those whose value matches 154 | * the AbstractDatabaseTable instance for the property specified. 155 | * 156 | * @param mixed $src An associative array or object to bind to the AbstractDatabaseTable instance. 157 | * @param mixed $ignore An optional array or space separated list of properties 158 | * to ignore while binding. 159 | * 160 | * @return $this Method allows chaining 161 | * 162 | * @since 1.0 163 | */ 164 | public function save($src, $ignore = '') 165 | { 166 | $this 167 | // Attempt to bind the source to the instance. 168 | ->bind($src, $ignore) 169 | // Run any sanity checks on the instance and verify that it is ready for storage. 170 | ->check() 171 | // Attempt to store the properties to the database table. 172 | ->store(); 173 | 174 | return $this; 175 | } 176 | 177 | /** 178 | * Method to bind an associative array or object to the AbstractDatabaseTable instance.This 179 | * method only binds properties that are publicly accessible and optionally 180 | * takes an array of properties to ignore when binding. 181 | * 182 | * @param mixed $src An associative array or object to bind to the AbstractDatabaseTable instance. 183 | * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. 184 | * 185 | * @return $this Method allows chaining 186 | * 187 | * @since 1.0 188 | * @throws \InvalidArgumentException 189 | */ 190 | public function bind($src, $ignore = array()) 191 | { 192 | // If the source value is not an array or object return false. 193 | if (!is_object($src) && !is_array($src)) 194 | { 195 | throw new \InvalidArgumentException(sprintf('%s::bind(*%s*)', get_class($this), gettype($src))); 196 | } 197 | 198 | // If the source value is an object, get its accessible properties. 199 | if (is_object($src)) 200 | { 201 | $src = get_object_vars($src); 202 | } 203 | 204 | // If the ignore value is a string, explode it over spaces. 205 | if (!is_array($ignore)) 206 | { 207 | $ignore = explode(' ', $ignore); 208 | } 209 | 210 | // Bind the source value, excluding the ignored fields. 211 | foreach ($this->tableFields as $k => $v) 212 | { 213 | // Only process fields not in the ignore array. 214 | if (!in_array($k, $ignore)) 215 | { 216 | if (isset($src[$k])) 217 | { 218 | $this->tableFields->$k = $src[$k]; 219 | } 220 | } 221 | } 222 | 223 | return $this; 224 | } 225 | 226 | /** 227 | * Method to load a row from the database by primary key and bind the fields 228 | * to the AbstractDatabaseTable instance properties. 229 | * 230 | * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If not 231 | * set the instance property value is used. 232 | * @param boolean $reset True to reset the default values before loading the new row. 233 | * 234 | * @return $this Method allows chaining 235 | * 236 | * @since 1.0 237 | * @throws \RuntimeException 238 | * @throws \UnexpectedValueException 239 | * @throws \InvalidArgumentException 240 | */ 241 | public function load($keys = null, $reset = true) 242 | { 243 | if (empty($keys)) 244 | { 245 | $empty = true; 246 | $keys = array(); 247 | 248 | // If empty, use the value of the current key 249 | foreach ($this->tableKeys as $key) 250 | { 251 | $empty = $empty && empty($this->$key); 252 | $keys[$key] = $this->$key; 253 | } 254 | 255 | // If empty primary key there's is no need to load anything 256 | if ($empty) 257 | { 258 | return $this; 259 | } 260 | } 261 | elseif (!is_array($keys)) 262 | { 263 | // Load by primary key. 264 | $keyCount = count($this->tableKeys); 265 | 266 | if ($keyCount) 267 | { 268 | if ($keyCount > 1) 269 | { 270 | throw new \InvalidArgumentException('Table has multiple primary keys specified, only one primary key value provided.'); 271 | } 272 | 273 | $keys = array($this->getKeyName() => $keys); 274 | } 275 | else 276 | { 277 | throw new \RuntimeException('No table keys defined.'); 278 | } 279 | } 280 | 281 | if ($reset) 282 | { 283 | $this->reset(); 284 | } 285 | 286 | // Initialise the query. 287 | $query = $this->db->getQuery(true) 288 | ->select('*') 289 | ->from($this->db->quoteName($this->tableName)); 290 | 291 | foreach ($keys as $field => $value) 292 | { 293 | // Check that $field is in the table. 294 | if (isset($this->tableFields->$field) || is_null($this->tableFields->$field)) 295 | { 296 | // Add the search tuple to the query. 297 | $query->where($this->db->quoteName($field) . ' = ' . $this->db->quote($value)); 298 | } 299 | else 300 | { 301 | throw new \UnexpectedValueException(sprintf('Missing field in database: %s   %s.', get_class($this), $field)); 302 | } 303 | } 304 | 305 | $this->db->setQuery($query); 306 | 307 | $row = $this->db->loadAssoc(); 308 | 309 | // Check that we have a result. 310 | if (empty($row)) 311 | { 312 | throw new \RuntimeException(__METHOD__ . ' can not bind.'); 313 | } 314 | 315 | // Bind the object with the row and return. 316 | return $this->bind($row); 317 | } 318 | 319 | /** 320 | * Method to delete a row from the database table by primary key value. 321 | * 322 | * @param mixed $pKey An optional primary key value to delete. If not set the instance property value is used. 323 | * 324 | * @return $this Method allows chaining 325 | * 326 | * @since 1.0 327 | * @throws \UnexpectedValueException 328 | */ 329 | public function delete($pKey = null) 330 | { 331 | $key = $this->getKeyName(); 332 | 333 | $pKey = (is_null($pKey)) ? $this->$key : $pKey; 334 | 335 | // If no primary key is given, return false. 336 | if ($pKey === null) 337 | { 338 | throw new \UnexpectedValueException('Null primary key not allowed.'); 339 | } 340 | 341 | // Delete the row by primary key. 342 | $this->db->setQuery( 343 | $this->db->getQuery(true) 344 | ->delete($this->db->quoteName($this->tableName)) 345 | ->where($this->db->quoteName($key) . ' = ' . $this->db->quote($pKey)) 346 | ) 347 | ->execute(); 348 | 349 | return $this; 350 | } 351 | 352 | /** 353 | * Method to reset class properties to the defaults set in the class 354 | * definition. It will ignore the primary key as well as any private class 355 | * properties. 356 | * 357 | * @return void 358 | * 359 | * @since 1.0 360 | */ 361 | public function reset() 362 | { 363 | // Get the default values for the class from the table. 364 | foreach ($this->getFields() as $k => $v) 365 | { 366 | // If the property is not the primary key, reset it. 367 | if (!in_array($k, $this->tableKeys)) 368 | { 369 | $this->$k = $v->Default; 370 | } 371 | } 372 | } 373 | 374 | /** 375 | * Method to perform sanity checks on the AbstractDatabaseTable instance properties to ensure 376 | * they are safe to store in the database. Child classes should override this 377 | * method to make sure the data they are storing in the database is safe and 378 | * as expected before storage. 379 | * 380 | * @return $this Method allows chaining 381 | * 382 | * @since 1.0 383 | */ 384 | public function check() 385 | { 386 | return $this; 387 | } 388 | 389 | /** 390 | * Method to store a row in the database from the DefaultDatabaseTable instance properties. 391 | * If a primary key value is set the row with that primary key value will be 392 | * updated with the instance property values. If no primary key value is set 393 | * a new row will be inserted into the database with the properties from the 394 | * DefaultDatabaseTable instance. 395 | * 396 | * @param boolean $updateNulls True to update fields even if they are null. 397 | * 398 | * @return $this Method allows chaining 399 | * 400 | * @since 1.0 401 | */ 402 | public function store($updateNulls = false) 403 | { 404 | // If a primary key exists update the object, otherwise insert it. 405 | if ($this->hasPrimaryKey()) 406 | { 407 | $this->db->updateObject($this->tableName, $this->tableFields, $this->tableKeys, $updateNulls); 408 | } 409 | else 410 | { 411 | $this->db->insertObject($this->tableName, $this->tableFields, $this->tableKeys[0]); 412 | } 413 | 414 | return $this; 415 | } 416 | 417 | /** 418 | * Validate that the primary key has been set. 419 | * 420 | * @return boolean True if the primary key(s) have been set. 421 | * 422 | * @since 1.0 423 | */ 424 | public function hasPrimaryKey() 425 | { 426 | if ($this->autoIncrement) 427 | { 428 | $empty = true; 429 | 430 | foreach ($this->tableKeys as $key) 431 | { 432 | $empty = $empty && !$this->$key; 433 | } 434 | } 435 | else 436 | { 437 | $query = $this->db->getQuery(true) 438 | ->select('COUNT(*)') 439 | ->from($this->tableName); 440 | $this->appendPrimaryKeys($query); 441 | 442 | $this->db->setQuery($query); 443 | $count = $this->db->loadResult(); 444 | 445 | if ($count == 1) 446 | { 447 | $empty = false; 448 | } 449 | else 450 | { 451 | $empty = true; 452 | } 453 | } 454 | 455 | return !$empty; 456 | } 457 | 458 | /** 459 | * Method to append the primary keys for this table to a query. 460 | * 461 | * @param DatabaseQuery $query A query object to append. 462 | * @param mixed $pk Optional primary key parameter. 463 | * 464 | * @return $this Method allows chaining 465 | * 466 | * @since 1.0 467 | */ 468 | public function appendPrimaryKeys($query, $pk = null) 469 | { 470 | if (is_null($pk)) 471 | { 472 | foreach ($this->tableKeys as $k) 473 | { 474 | $query->where($this->db->quoteName($k) . ' = ' . $this->db->quote($this->$k)); 475 | } 476 | } 477 | else 478 | { 479 | if (is_string($pk)) 480 | { 481 | $pk = array($this->tableKeys[0] => $pk); 482 | } 483 | 484 | $pk = (object) $pk; 485 | 486 | foreach ($this->tableKeys AS $k) 487 | { 488 | $query->where($this->db->quoteName($k) . ' = ' . $this->db->quote($pk->$k)); 489 | } 490 | } 491 | 492 | return $this; 493 | } 494 | 495 | /** 496 | * Method to get the primary key field name for the table. 497 | * 498 | * @param boolean $multiple True to return all primary keys (as an array) or false to return just the first one (as a string). 499 | * 500 | * @return mixed Array of primary key field names or string containing the first primary key field. 501 | * 502 | * @since 1.0 503 | */ 504 | public function getKeyName($multiple = false) 505 | { 506 | // Count the number of keys 507 | if (count($this->tableKeys)) 508 | { 509 | if ($multiple) 510 | { 511 | // If we want multiple keys, return the raw array. 512 | return $this->tableKeys; 513 | } 514 | else 515 | { 516 | // If we want the standard method, just return the first key. 517 | return $this->tableKeys[0]; 518 | } 519 | } 520 | 521 | return ''; 522 | } 523 | 524 | /** 525 | * Get the columns from database table. 526 | * 527 | * @return mixed An array of the field names, or false if an error occurs. 528 | * 529 | * @since 1.0 530 | * @throws \UnexpectedValueException 531 | */ 532 | public function getFields() 533 | { 534 | static $cache = null; 535 | 536 | if ($cache === null) 537 | { 538 | // Lookup the fields for this table only once. 539 | $fields = $this->db->getTableColumns($this->tableName, false); 540 | 541 | if (empty($fields)) 542 | { 543 | throw new \UnexpectedValueException(sprintf('No columns found for %s table', $this->tableName)); 544 | } 545 | 546 | $cache = $fields; 547 | } 548 | 549 | return $cache; 550 | } 551 | 552 | /** 553 | * Get the table name. 554 | * 555 | * @return string 556 | * 557 | * @since 1.0 558 | */ 559 | public function getTableName() 560 | { 561 | return $this->tableName; 562 | } 563 | 564 | /** 565 | * Get an iterator object. 566 | * 567 | * @return \ArrayIterator 568 | * 569 | * @since 1.0 570 | */ 571 | public function getIterator() 572 | { 573 | return new \ArrayIterator($this->tableFields); 574 | } 575 | 576 | /** 577 | * Clone the table. 578 | * 579 | * @return \ArrayIterator 580 | * 581 | * @since 1.0 582 | */ 583 | public function __clone() 584 | { 585 | return $this->getIterator(); 586 | } 587 | } 588 | -------------------------------------------------------------------------------- /App/Table/NewsTable.php: -------------------------------------------------------------------------------- 1 | title) 51 | { 52 | throw new \UnexpectedValueException('A title is required'); 53 | } 54 | 55 | if (!$this->alias) 56 | { 57 | $this->alias = $this->title; 58 | } 59 | 60 | $this->alias = OutputFilter::stringURLSafe($this->alias); 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * Method to store a row in the database from the DefaultDatabaseTable instance properties. 67 | * If a primary key value is set the row with that primary key value will be 68 | * updated with the instance property values. If no primary key value is set 69 | * a new row will be inserted into the database with the properties from the 70 | * DefaultDatabaseTable instance. 71 | * 72 | * @param boolean $updateNulls True to update fields even if they are null. 73 | * 74 | * @return $this Method allows chaining 75 | * 76 | * @since 1.0 77 | */ 78 | public function store($updateNulls = false) 79 | { 80 | $oldId = $this->{$this->getKeyName()}; 81 | 82 | return parent::store($updateNulls); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /App/Templates/dashboard/dashboard.index.twig: -------------------------------------------------------------------------------- 1 | {% extends "index.twig" %} 2 | 3 | {% block content %} 4 |
5 |

Congratulations!
It's Installed

6 | 7 |

You've now got the Joomla! Framework installed and you're ready to get started coding. If you have questions, be sure to check out the docs.

8 | Documentation 9 |
10 | 11 |
12 | 13 |
14 |
15 |

Bootstrap

16 |

This install comes preloaded with the Twitter Bootstrap package pre-installed, symlinked and ready to use.

17 | 18 |

jQuery

19 |

This install comes preloaded with the latest jQuery packages pre-installed, symlinked and ready to use.

20 | 21 |

Twig

22 |

This install comes preloaded with Twig as the templating package pre-installed and ready to use.

23 |
24 | 25 |
26 |

Themes

27 |

This install has a sample theme called "Awesome". Yes, you're looking at it. Create your own theme and set as the default in config.json

28 | 29 |

Database

30 |

This install has a database setup included. (Be sure to checkout the config.json file)

31 | 32 |

Routing

33 |

This install uses a static routing file (routes.json) and also includes minor code to demo automatic routing.

34 |
35 |
36 |
37 | {% if success == false %} 38 |
39 |

You should setup your database now.

40 | Enter DB Settings 41 |
42 | 43 | 79 | {% endif %} 80 | {% endblock %} 81 | -------------------------------------------------------------------------------- /App/Templates/index.twig: -------------------------------------------------------------------------------- 1 | {% set firstPart = uri.route|split("/")|first %} 2 | 3 | 4 | 5 | {% block title %}Awesome{% endblock %} 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 20 |

Joomla! Framework

21 |
22 | 23 |
24 | {% block subtitle %}{% endblock %} 25 | {% for type, flashMessages in flashBag %} 26 | {% for flashMessage in flashMessages %} 27 |
28 | {{ flashMessage }} 29 |
30 | {% endfor %} 31 | {% endfor %} 32 | 33 | {% block content %}{% endblock %} 34 |
35 | 36 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /App/Templates/news/news.edit.twig: -------------------------------------------------------------------------------- 1 | {% extends "index.twig" %} 2 | 3 | {% block title %}Add/Edit Article{% endblock %} 4 | 5 | {% block subtitle %}

Add/Edit Article

{% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 |
11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /App/Templates/news/news.index.twig: -------------------------------------------------------------------------------- 1 | {% extends "index.twig" %} 2 | 3 | {% block title %}News{% endblock %} 4 | 5 | {% block subtitle %}

News

{% endblock %} 6 | 7 | {% block content %} 8 | {% if items %} 9 |
10 | {% for item in items %} 11 |
12 |

{{ item.title }}

13 | 14 |

{{ item.body }}

15 |
16 | {% endfor %} 17 |
18 | {% else %} 19 |
20 |
21 |

News Heading 1

22 | 23 |

The body of this news article goes here. This would pull from your database of articles.

24 | Read Article 25 |
26 | 27 |
28 |

News Heading 2

29 | 30 |

The body of this news article goes here. This would pull from your database of articles.

31 | Read Article 32 |
33 | 34 |
35 |

News Heading 3

36 | 37 |

The body of this news article goes here. This would pull from your database of articles.

38 | Read Article 39 |
40 |
41 | {% endif %} 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /App/Templates/news/news.view.twig: -------------------------------------------------------------------------------- 1 | {% extends "index.twig" %} 2 | 3 | {% block title %}Add/Edit Article{% endblock %} 4 | 5 | {% block subtitle %}

{{ item.title }}

{% endblock %} 6 | 7 | {% block content %} 8 | Article ID# {{ item.news_id }} 9 |

{{ item.body }}

10 | {% endblock %} 11 | 12 | -------------------------------------------------------------------------------- /App/View/Dashboard/DashboardHtmlView.php: -------------------------------------------------------------------------------- 1 | app->input->get('success', false)) 32 | { 33 | $this->app->enqueueMessage("Sweet! You've setup your database successfully. Check out the Sample Page", 'success'); 34 | } 35 | 36 | $this->renderer->set('success', $this->app->input->get('success', false)); 37 | $this->renderer->set('logo', DEFAULT_THEME . '/images/logo.png'); 38 | $this->renderer->set('config', $this->app->getContainer()->get('config')); 39 | 40 | return parent::render(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /App/View/DefaultHtmlView.php: -------------------------------------------------------------------------------- 1 | app = $app; 62 | 63 | $renderer = $app->getContainer()->get('config')->get('renderer.type'); 64 | 65 | $className = 'App\\View\\Renderer\\' . ucfirst($renderer); 66 | 67 | if (false == class_exists($className)) 68 | { 69 | throw new \RuntimeException(sprintf('Invalid renderer: %s', $renderer)); 70 | } 71 | 72 | $config = array(); 73 | $config['templates_base_dir'] = JPATH_TEMPLATES; 74 | 75 | // Load the renderer. 76 | $this->renderer = new $className($config); 77 | 78 | // Register application's Twig extension. 79 | $this->renderer->addExtension(new TwigExtension($app)); 80 | 81 | // Register additional paths. 82 | if (!empty($templatesPaths)) 83 | { 84 | $this->renderer->setTemplatesPaths($templatesPaths, true); 85 | } 86 | 87 | // Register the theme path 88 | $this->renderer->set('themePath', DEFAULT_THEME . '/'); 89 | 90 | // Retrieve and clear the message queue 91 | $this->renderer->set('flashBag', $app->getMessageQueue()); 92 | $app->clearMessageQueue(); 93 | } 94 | 95 | /** 96 | * Magic toString method that is a proxy for the render method. 97 | * 98 | * @return string 99 | * 100 | * @since 1.0 101 | */ 102 | public function __toString() 103 | { 104 | return $this->render(); 105 | } 106 | 107 | /** 108 | * Method to escape output. 109 | * 110 | * @param string $output The output to escape. 111 | * 112 | * @return string The escaped output. 113 | * 114 | * @see ViewInterface::escape() 115 | * @since 1.0 116 | */ 117 | public function escape($output) 118 | { 119 | // Escape the output. 120 | return htmlspecialchars($output, ENT_COMPAT, 'UTF-8'); 121 | } 122 | 123 | /** 124 | * Method to get the view layout. 125 | * 126 | * @return string The layout name. 127 | * 128 | * @since 1.0 129 | */ 130 | public function getLayout() 131 | { 132 | return $this->layout; 133 | } 134 | 135 | /** 136 | * Method to get the renderer object. 137 | * 138 | * @return RendererInterface The renderer object. 139 | * 140 | * @since 1.0 141 | */ 142 | public function getRenderer() 143 | { 144 | return $this->renderer; 145 | } 146 | 147 | /** 148 | * Method to render the view. 149 | * 150 | * @return string The rendered view. 151 | * 152 | * @since 1.0 153 | * @throws \RuntimeException 154 | */ 155 | public function render() 156 | { 157 | return $this->renderer->render($this->layout); 158 | } 159 | 160 | /** 161 | * Method to set the view layout. 162 | * 163 | * @param string $layout The layout name. 164 | * 165 | * @return $this Method supports chaining 166 | * 167 | * @since 1.0 168 | */ 169 | public function setLayout($layout) 170 | { 171 | $this->layout = $layout; 172 | 173 | return $this; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /App/View/News/NewsHtmlView.php: -------------------------------------------------------------------------------- 1 | getLayout()) 41 | { 42 | case 'news.view': 43 | case 'news.edit': 44 | // Get the input 45 | if ($this->app->input->get('task') != 'add') 46 | { 47 | $item = $this->model->getItem(); 48 | $this->renderer->set('item', $item); 49 | } 50 | 51 | break; 52 | 53 | case 'news.add': 54 | $this->setLayout('news.edit'); 55 | 56 | break; 57 | 58 | default: 59 | $items = $this->model->getItems(); 60 | $this->renderer->set('items', $items); 61 | 62 | if (count($items) >= 1) 63 | { 64 | $this->app->enqueueMessage("You've setup your database! Below are dynamic results.", 'success'); 65 | } 66 | else 67 | { 68 | $this->app->enqueueMessage('Here you see a sample page layout. Ideally this would pull articles from the database.', 'alert'); 69 | } 70 | 71 | break; 72 | } 73 | 74 | return parent::render(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /App/View/Renderer/RendererInterface.php: -------------------------------------------------------------------------------- 1 | 'templates/', 26 | 'template_file_ext' => '.twig', 27 | 'twig_cache_dir' => 'cache/twig/', 28 | 'delimiters' => array( 29 | 'tag_comment' => array('{#', '#}'), 30 | 'tag_block' => array('{%', '%}'), 31 | 'tag_variable' => array('{{', '}}') 32 | ), 33 | 'environment' => array() 34 | ); 35 | 36 | /** 37 | * The data for the renderer. 38 | * 39 | * @var array 40 | * @since 1.0 41 | */ 42 | private $data = array(); 43 | 44 | /** 45 | * The templates location paths. 46 | * 47 | * @var array 48 | * @since 1.0 49 | */ 50 | private $templatesPaths = array(); 51 | 52 | /** 53 | * Current template name. 54 | * 55 | * @var string 56 | * @since 1.0 57 | */ 58 | private $template; 59 | 60 | /** 61 | * Loads template from the filesystem. 62 | * 63 | * @var \Twig_Loader_Filesystem 64 | * @since 1.0 65 | */ 66 | private $twigLoader; 67 | 68 | /** 69 | * Instantiate the renderer. 70 | * 71 | * @param array $config The array of configuration parameters. 72 | * 73 | * @since 1.0 74 | * @throws \RuntimeException 75 | */ 76 | public function __construct($config = array()) 77 | { 78 | // Merge the config. 79 | $this->config = array_merge($this->config, $config); 80 | 81 | // Set the templates location path. 82 | $this->setTemplatesPaths($this->config['templates_base_dir'], true); 83 | 84 | try 85 | { 86 | $this->twigLoader = new \Twig_Loader_Filesystem($this->templatesPaths); 87 | } 88 | catch (\Twig_Error_Loader $e) 89 | { 90 | throw new \RuntimeException($e->getRawMessage()); 91 | } 92 | 93 | parent::__construct($this->twigLoader, $this->config['environment']); 94 | } 95 | 96 | /** 97 | * Get the Lexer instance. 98 | * 99 | * @return \Twig_LexerInterface A Twig_LexerInterface instance. 100 | * 101 | * @since 1.0 102 | */ 103 | public function getLexer() 104 | { 105 | if (null === $this->lexer) 106 | { 107 | $this->lexer = new \Twig_Lexer($this, $this->config['delimiters']); 108 | } 109 | 110 | return $this->lexer; 111 | } 112 | 113 | /** 114 | * Set the data for the renderer. 115 | * 116 | * @param mixed $key The variable name or an array of variable names with values. 117 | * @param mixed $value The value. 118 | * @param boolean $global Is this a global variable? 119 | * 120 | * @return Twig Method supports chaining. 121 | * 122 | * @since 1.0 123 | * @throws \InvalidArgumentException 124 | */ 125 | public function set($key, $value = null, $global = false) 126 | { 127 | if (is_array($key)) 128 | { 129 | foreach ($key as $k => $v) 130 | { 131 | $this->set($k, $v, $global); 132 | } 133 | } 134 | else 135 | { 136 | if (!isset($value)) 137 | { 138 | throw new \InvalidArgumentException('No value defined.'); 139 | } 140 | 141 | if ($global) 142 | { 143 | $this->addGlobal($key, $value); 144 | } 145 | else 146 | { 147 | $this->data[$key] = $value; 148 | } 149 | } 150 | 151 | return $this; 152 | } 153 | 154 | /** 155 | * Unset a particular variable. 156 | * 157 | * @param mixed $key The variable name. 158 | * 159 | * @return Twig Method supports chaining. 160 | * 161 | * @since 1.0 162 | */ 163 | public function unsetData($key) 164 | { 165 | return $this->unsetData($key); 166 | } 167 | 168 | /** 169 | * Render and return compiled HTML. 170 | * 171 | * @param string $template The template file name. 172 | * @param array $data An array of data to pass to the template. 173 | * 174 | * @return string Compiled HTML. 175 | * 176 | * @since 1.0 177 | * @throws \RuntimeException 178 | */ 179 | public function render($template = '', array $data = array()) 180 | { 181 | if (!empty($template)) 182 | { 183 | $this->setTemplate($template); 184 | } 185 | 186 | if (!empty($data)) 187 | { 188 | $this->set($data); 189 | } 190 | 191 | try 192 | { 193 | return $this->load()->render($this->data); 194 | } 195 | catch (\Twig_Error_Loader $e) 196 | { 197 | throw new \RuntimeException($e->getRawMessage()); 198 | } 199 | } 200 | 201 | /** 202 | * Display the compiled HTML content. 203 | * 204 | * @param string $template The template file name. 205 | * @param array $data An array of data to pass to the template. 206 | * 207 | * @return void 208 | * 209 | * @since 1.0 210 | */ 211 | public function display($template = '', array $data = array()) 212 | { 213 | if (!empty($template)) 214 | { 215 | $this->setTemplate($template); 216 | } 217 | 218 | if (!empty($data)) 219 | { 220 | $this->set($data); 221 | } 222 | 223 | try 224 | { 225 | $this->load()->display($this->data); 226 | } 227 | catch (\Twig_Error_Loader $e) 228 | { 229 | echo $e->getRawMessage(); 230 | } 231 | } 232 | 233 | /** 234 | * Get the current template name. 235 | * 236 | * @return string The name of the currently loaded template file (without the extension). 237 | * 238 | * @since 1.0 239 | */ 240 | public function getTemplate() 241 | { 242 | return $this->template; 243 | } 244 | 245 | /** 246 | * Add a path to the templates location array. 247 | * 248 | * @param string $path Templates location path. 249 | * 250 | * @return $this 251 | * 252 | * @since 1.0 253 | */ 254 | public function addPath($path) 255 | { 256 | return $this->setTemplatesPaths($path, true); 257 | } 258 | 259 | /** 260 | * Set the template. 261 | * 262 | * @param string $name The name of the template file. 263 | * 264 | * @return Twig Method supports chaining. 265 | * 266 | * @since 1.0 267 | */ 268 | public function setTemplate($name) 269 | { 270 | $this->template = $name; 271 | 272 | return $this; 273 | } 274 | 275 | /** 276 | * Sets the paths where templates are stored. 277 | * 278 | * @param string|array $paths A path or an array of paths where to look for templates. 279 | * @param bool $overrideBaseDir If true a path can be outside themes base directory. 280 | * 281 | * @return Twig 282 | * 283 | * @since 1.0 284 | */ 285 | public function setTemplatesPaths($paths, $overrideBaseDir = false) 286 | { 287 | if (!is_array($paths)) 288 | { 289 | $paths = array($paths); 290 | } 291 | 292 | foreach ($paths as $path) 293 | { 294 | if ($overrideBaseDir) 295 | { 296 | $this->templatesPaths[] = $path; 297 | } 298 | else 299 | { 300 | $this->templatesPaths[] = $this->config['templates_base_dir'] . $path; 301 | } 302 | } 303 | 304 | // Reset the paths if needed. 305 | if (is_object($this->twigLoader)) 306 | { 307 | try 308 | { 309 | $this->twigLoader->setPaths($this->templatesPaths); 310 | } 311 | catch (\Twig_Error_Loader $e) 312 | { 313 | echo $e->getRawMessage(); 314 | } 315 | } 316 | 317 | return $this; 318 | } 319 | 320 | /** 321 | * Load the template and return an output object. 322 | * 323 | * @return object Output object. 324 | * 325 | * @since 1.0 326 | */ 327 | private function load() 328 | { 329 | return $this->loadTemplate($this->getTemplate() . $this->config['template_file_ext']); 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /App/View/Renderer/TwigExtension.php: -------------------------------------------------------------------------------- 1 | app = $app; 34 | } 35 | 36 | /** 37 | * Returns the name of the extension. 38 | * 39 | * @return string The extension name. 40 | * 41 | * @since 1.0 42 | */ 43 | public function getName() 44 | { 45 | return 'jfw-sample'; 46 | } 47 | 48 | /** 49 | * Returns a list of global variables to add to the existing list. 50 | * 51 | * @return array An array of global variables. 52 | * 53 | * @since 1.0 54 | */ 55 | public function getGlobals() 56 | { 57 | return array( 58 | 'uri' => $this->app->get('uri') 59 | ); 60 | } 61 | 62 | /** 63 | * Returns a list of functions to add to the existing list. 64 | * 65 | * @return array An array of functions. 66 | * 67 | * @since 1.0 68 | */ 69 | public function getFunctions() 70 | { 71 | $functions = array( 72 | new \Twig_SimpleFunction('sprintf', 'sprintf'), 73 | new \Twig_SimpleFunction('stripJRoot', array($this, 'stripJRoot')) 74 | ); 75 | 76 | return $functions; 77 | } 78 | 79 | /** 80 | * Returns a list of filters to add to the existing list. 81 | * 82 | * @return array An array of filters 83 | * 84 | * @since 1.0 85 | */ 86 | public function getFilters() 87 | { 88 | return array( 89 | new \Twig_SimpleFilter('basename', 'basename'), 90 | new \Twig_SimpleFilter('get_class', 'get_class'), 91 | new \Twig_SimpleFilter('json_decode', 'json_decode'), 92 | new \Twig_SimpleFilter('stripJRoot', array($this, 'stripJRoot')) 93 | ); 94 | } 95 | 96 | /** 97 | * Replaces the application root path defined by the constant "JPATH_ROOT" with the string "JROOT". 98 | * 99 | * @param string $string The string to process. 100 | * 101 | * @return mixed 102 | * 103 | * @since 1.0 104 | */ 105 | public function stripJRoot($string) 106 | { 107 | return str_replace(JPATH_ROOT, 'JROOT', $string); 108 | } 109 | 110 | /** 111 | * Dummy function to prevent throwing exception on dump function in the non-debug mode. 112 | * 113 | * @return void 114 | * 115 | * @since 1.0 116 | */ 117 | public function dump() 118 | { 119 | return; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | Joomla! Framework Sample Application 294 | Copyright (C) 2013 davidhurley 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | framework-app 2 | ============= 3 | 4 | Joomla! Framework Sample Application 5 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.websparkinc.framework-app", 3 | "description": "Sample Framework App.", 4 | "keywords": ["framework-app","joomla-framework"], 5 | "license": "GPL-2.0+", 6 | "version": "1.0.0-alpha", 7 | "require": { 8 | "php": ">=5.3.10", 9 | "joomla/framework": "1.0.*@stable", 10 | "symfony/http-foundation": "2.4.*@stable", 11 | "twig/twig" : "1.*@stable", 12 | "twbs/bootstrap":"2.3.2" 13 | }, 14 | "require-dev": { 15 | "filp/whoops": "1.0.*@stable" 16 | }, 17 | "scripts": { 18 | "post-install-cmd": [ 19 | "App\\Setup\\InstallScript::postInstall" 20 | ] 21 | }, 22 | "autoload": { 23 | "psr-0": { 24 | "App": "" 25 | } 26 | }, 27 | "minimum-stability": "dev" 28 | } 29 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" 5 | ], 6 | "hash": "99a682e5ada0e905fe577c87f603e579", 7 | "packages": [ 8 | { 9 | "name": "joomla/framework", 10 | "version": "1.0", 11 | "source": { 12 | "type": "git", 13 | "url": "https://github.com/joomla/joomla-framework.git", 14 | "reference": "3db74e6f9bbf151c0e73425887921cb239ad1e0e" 15 | }, 16 | "dist": { 17 | "type": "zip", 18 | "url": "https://api.github.com/repos/joomla/joomla-framework/zipball/3db74e6f9bbf151c0e73425887921cb239ad1e0e", 19 | "reference": "3db74e6f9bbf151c0e73425887921cb239ad1e0e", 20 | "shasum": "" 21 | }, 22 | "require": { 23 | "php": ">=5.3.10", 24 | "psr/log": "~1.0" 25 | }, 26 | "replace": { 27 | "joomla/application": "self.version", 28 | "joomla/archive": "self.version", 29 | "joomla/cache": "self.version", 30 | "joomla/compat": "self.version", 31 | "joomla/controller": "self.version", 32 | "joomla/crypt": "self.version", 33 | "joomla/data": "self.version", 34 | "joomla/database": "self.version", 35 | "joomla/date": "self.version", 36 | "joomla/facebook": "self.version", 37 | "joomla/filesystem": "self.version", 38 | "joomla/filter": "self.version", 39 | "joomla/form": "self.version", 40 | "joomla/github": "self.version", 41 | "joomla/google": "self.version", 42 | "joomla/http": "self.version", 43 | "joomla/image": "self.version", 44 | "joomla/input": "self.version", 45 | "joomla/keychain": "self.version", 46 | "joomla/language": "self.version", 47 | "joomla/linkedin": "self.version", 48 | "joomla/log": "self.version", 49 | "joomla/model": "self.version", 50 | "joomla/oauth1": "self.version", 51 | "joomla/oauth2": "self.version", 52 | "joomla/profiler": "self.version", 53 | "joomla/registry": "self.version", 54 | "joomla/router": "self.version", 55 | "joomla/session": "self.version", 56 | "joomla/string": "self.version", 57 | "joomla/test": "self.version", 58 | "joomla/twitter": "self.version", 59 | "joomla/uri": "self.version", 60 | "joomla/utilities": "self.version", 61 | "joomla/view": "self.version" 62 | }, 63 | "require-dev": { 64 | "symfony/yaml": "~2.0" 65 | }, 66 | "suggest": { 67 | "symfony/yaml": "Install symfony/yaml 2.* if you require YAML support." 68 | }, 69 | "type": "library", 70 | "autoload": { 71 | "psr-0": { 72 | "Joomla": "src/", 73 | "Psr": "vendor/psr/cache" 74 | }, 75 | "classmap": [ 76 | "src/Joomla/Compat/JsonSerializable.php" 77 | ], 78 | "files": [ 79 | "src/Joomla/PHP/methods.php" 80 | ] 81 | }, 82 | "notification-url": "https://packagist.org/downloads/", 83 | "license": [ 84 | "GPL-2.0+" 85 | ], 86 | "description": "The Joomla Framework is a platform for writing Web and command line applications in PHP.", 87 | "homepage": "https://github.com/joomla/joomla-framework", 88 | "keywords": [ 89 | "framework", 90 | "joomla" 91 | ], 92 | "time": "2013-12-01 00:38:39" 93 | }, 94 | { 95 | "name": "psr/log", 96 | "version": "dev-master", 97 | "source": { 98 | "type": "git", 99 | "url": "https://github.com/php-fig/log.git", 100 | "reference": "5144da9525d24346bf009ff28f10cbaa1f0f166e" 101 | }, 102 | "dist": { 103 | "type": "zip", 104 | "url": "https://api.github.com/repos/php-fig/log/zipball/5144da9525d24346bf009ff28f10cbaa1f0f166e", 105 | "reference": "5144da9525d24346bf009ff28f10cbaa1f0f166e", 106 | "shasum": "" 107 | }, 108 | "type": "library", 109 | "extra": { 110 | "branch-alias": { 111 | "dev-master": "1.0.x-dev" 112 | } 113 | }, 114 | "autoload": { 115 | "psr-0": { 116 | "Psr\\Log\\": "" 117 | } 118 | }, 119 | "notification-url": "https://packagist.org/downloads/", 120 | "license": [ 121 | "MIT" 122 | ], 123 | "authors": [ 124 | { 125 | "name": "PHP-FIG", 126 | "homepage": "http://www.php-fig.org/" 127 | } 128 | ], 129 | "description": "Common interface for logging libraries", 130 | "keywords": [ 131 | "log", 132 | "psr", 133 | "psr-3" 134 | ], 135 | "time": "2013-12-05 15:25:07" 136 | }, 137 | { 138 | "name": "symfony/http-foundation", 139 | "version": "v2.4.0", 140 | "target-dir": "Symfony/Component/HttpFoundation", 141 | "source": { 142 | "type": "git", 143 | "url": "https://github.com/symfony/HttpFoundation.git", 144 | "reference": "381245ba3e507a3e9c5b4c2cbf344a312fb6081d" 145 | }, 146 | "dist": { 147 | "type": "zip", 148 | "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/381245ba3e507a3e9c5b4c2cbf344a312fb6081d", 149 | "reference": "381245ba3e507a3e9c5b4c2cbf344a312fb6081d", 150 | "shasum": "" 151 | }, 152 | "require": { 153 | "php": ">=5.3.3" 154 | }, 155 | "type": "library", 156 | "extra": { 157 | "branch-alias": { 158 | "dev-master": "2.4-dev" 159 | } 160 | }, 161 | "autoload": { 162 | "psr-0": { 163 | "Symfony\\Component\\HttpFoundation\\": "" 164 | }, 165 | "classmap": [ 166 | "Symfony/Component/HttpFoundation/Resources/stubs" 167 | ] 168 | }, 169 | "notification-url": "https://packagist.org/downloads/", 170 | "license": [ 171 | "MIT" 172 | ], 173 | "authors": [ 174 | { 175 | "name": "Fabien Potencier", 176 | "email": "fabien@symfony.com" 177 | }, 178 | { 179 | "name": "Symfony Community", 180 | "homepage": "http://symfony.com/contributors" 181 | } 182 | ], 183 | "description": "Symfony HttpFoundation Component", 184 | "homepage": "http://symfony.com", 185 | "time": "2013-11-28 10:27:26" 186 | }, 187 | { 188 | "name": "twbs/bootstrap", 189 | "version": "v2.3.2", 190 | "source": { 191 | "type": "git", 192 | "url": "https://github.com/twbs/bootstrap.git", 193 | "reference": "b4c4072679943773a7582daa63f545ba818fa627" 194 | }, 195 | "dist": { 196 | "type": "zip", 197 | "url": "https://api.github.com/repos/twbs/bootstrap/zipball/b4c4072679943773a7582daa63f545ba818fa627", 198 | "reference": "b4c4072679943773a7582daa63f545ba818fa627", 199 | "shasum": "" 200 | }, 201 | "type": "library", 202 | "notification-url": "https://packagist.org/downloads/", 203 | "license": [ 204 | "Apache-2.0" 205 | ], 206 | "description": "Sleek, intuitive, and powerful front-end framework for faster and easier web development.", 207 | "homepage": "http://twbs.github.com/bootstrap/", 208 | "keywords": [ 209 | "bootstrap", 210 | "css" 211 | ], 212 | "time": "2013-07-27 06:36:38" 213 | }, 214 | { 215 | "name": "twig/twig", 216 | "version": "v1.14.2", 217 | "source": { 218 | "type": "git", 219 | "url": "https://github.com/fabpot/Twig.git", 220 | "reference": "ca445842fcea4f844d68203ffa2d00f5e3cdea64" 221 | }, 222 | "dist": { 223 | "type": "zip", 224 | "url": "https://api.github.com/repos/fabpot/Twig/zipball/ca445842fcea4f844d68203ffa2d00f5e3cdea64", 225 | "reference": "ca445842fcea4f844d68203ffa2d00f5e3cdea64", 226 | "shasum": "" 227 | }, 228 | "require": { 229 | "php": ">=5.2.4" 230 | }, 231 | "type": "library", 232 | "extra": { 233 | "branch-alias": { 234 | "dev-master": "1.14-dev" 235 | } 236 | }, 237 | "autoload": { 238 | "psr-0": { 239 | "Twig_": "lib/" 240 | } 241 | }, 242 | "notification-url": "https://packagist.org/downloads/", 243 | "license": [ 244 | "BSD-3-Clause" 245 | ], 246 | "authors": [ 247 | { 248 | "name": "Fabien Potencier", 249 | "email": "fabien@symfony.com" 250 | }, 251 | { 252 | "name": "Armin Ronacher", 253 | "email": "armin.ronacher@active-4.com" 254 | } 255 | ], 256 | "description": "Twig, the flexible, fast, and secure template language for PHP", 257 | "homepage": "http://twig.sensiolabs.org", 258 | "keywords": [ 259 | "templating" 260 | ], 261 | "time": "2013-10-30 08:20:53" 262 | } 263 | ], 264 | "packages-dev": [ 265 | { 266 | "name": "filp/whoops", 267 | "version": "1.0.9", 268 | "source": { 269 | "type": "git", 270 | "url": "https://github.com/filp/whoops.git", 271 | "reference": "6de5a6fbb675c091ee4ee40fd01b2e3df4f200af" 272 | }, 273 | "dist": { 274 | "type": "zip", 275 | "url": "https://api.github.com/repos/filp/whoops/zipball/6de5a6fbb675c091ee4ee40fd01b2e3df4f200af", 276 | "reference": "6de5a6fbb675c091ee4ee40fd01b2e3df4f200af", 277 | "shasum": "" 278 | }, 279 | "require": { 280 | "php": ">=5.3.0" 281 | }, 282 | "require-dev": { 283 | "mockery/mockery": "dev-master", 284 | "silex/silex": "1.0.*@dev" 285 | }, 286 | "type": "library", 287 | "autoload": { 288 | "psr-0": { 289 | "Whoops": "src/" 290 | } 291 | }, 292 | "notification-url": "https://packagist.org/downloads/", 293 | "license": [ 294 | "MIT" 295 | ], 296 | "authors": [ 297 | { 298 | "name": "Filipe Dobreira", 299 | "homepage": "https://github.com/filp", 300 | "role": "Developer" 301 | } 302 | ], 303 | "description": "php error handling for cool kids", 304 | "homepage": "https://github.com/filp/whoops", 305 | "keywords": [ 306 | "error", 307 | "exception", 308 | "handling", 309 | "library", 310 | "silex-provider", 311 | "whoops", 312 | "zf2" 313 | ], 314 | "time": "2013-10-28 22:06:33" 315 | } 316 | ], 317 | "aliases": [ 318 | 319 | ], 320 | "minimum-stability": "dev", 321 | "stability-flags": { 322 | "joomla/framework": 0, 323 | "symfony/http-foundation": 0, 324 | "twig/twig": 0, 325 | "filp/whoops": 0 326 | }, 327 | "platform": { 328 | "php": ">=5.3.10" 329 | }, 330 | "platform-dev": [ 331 | 332 | ] 333 | } 334 | -------------------------------------------------------------------------------- /www/.htaccess: -------------------------------------------------------------------------------- 1 | ## 2 | # @copyright (C) 2012-2013 Open Source Matters, Inc. All Rights Reserved. 3 | ## 4 | 5 | # == Enable the Rewrite Engine == 6 | RewriteEngine On 7 | 8 | # == Remove multiple slashes == 9 | RewriteCond %{HTTP_HOST} !="" 10 | RewriteCond %{THE_REQUEST} ^[A-Z]+\s//+(.*)\sHTTP/[0-9.]+$ [OR] 11 | RewriteCond %{THE_REQUEST} ^[A-Z]+\s(.*/)/+\sHTTP/[0-9.]+$ 12 | RewriteRule .* http://%{HTTP_HOST}/%1 [R=301,L] 13 | 14 | # == SEF URL Routing == 15 | RewriteCond %{REQUEST_FILENAME} !-f 16 | RewriteCond %{REQUEST_FILENAME} !-d 17 | RewriteRule (.*) index.php 18 | -------------------------------------------------------------------------------- /www/index.php: -------------------------------------------------------------------------------- 1 | registerServiceProvider(new \App\Service\ConfigServiceProvider(JPATH_CONFIGURATION . '/config.json')) 18 | ->registerServiceProvider(new \App\Service\DatabaseServiceProvider); 19 | 20 | // Instantiate the application. 21 | $application = new \App\App($container); 22 | 23 | // Execute the application. 24 | $application->execute(); 25 | -------------------------------------------------------------------------------- /www/themes/awesome/css/theme.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 20px; 3 | padding-bottom: 40px; 4 | } 5 | 6 | /* Custom container */ 7 | .container-narrow { 8 | margin: 0 auto; 9 | max-width: 700px; 10 | } 11 | .container-narrow > hr { 12 | margin: 30px 0; 13 | } 14 | 15 | /* Main marketing message and sign up button */ 16 | .jumbotron { 17 | margin: 60px 0; 18 | text-align: center; 19 | } 20 | .jumbotron h1 { 21 | font-size: 72px; 22 | line-height: 1; 23 | } 24 | .jumbotron .btn { 25 | font-size: 21px; 26 | padding: 14px 24px; 27 | } 28 | 29 | /* Supporting marketing content */ 30 | .marketing { 31 | margin: 60px 0; 32 | } 33 | .marketing p + h4 { 34 | margin-top: 28px; 35 | } 36 | 37 | #databaseModal form { 38 | margin-bottom: 0; 39 | } -------------------------------------------------------------------------------- /www/themes/awesome/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbhurley/framework-app/b35f2859b6773a8bc08f140582c7eaed34ec3bae/www/themes/awesome/images/logo.png --------------------------------------------------------------------------------